go-kit/auth/service.go

171 lines
4.9 KiB
Go

package auth
import (
"encoding/json"
"fmt"
"intranet/internal/dependencies"
"intranet/internal/models"
"intranet/pkg/token"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
jwt "github.com/golang-jwt/jwt/v5"
"golang.org/x/crypto/bcrypt"
)
// Service for auth (Interface AND implementation)
// All the business logic should be here
type Service interface {
// FindByEmail(id string) ([]*models.Auth, error)
GetLoginHandler() func(http.ResponseWriter, *http.Request)
GetSignupHandler() func(http.ResponseWriter, *http.Request)
GetAdminRoleRequiredMiddleware() gin.HandlerFunc
}
// AuthService godoc
type AuthServiceImplementation struct {
DB dependencies.DB
Log dependencies.Logger
}
// NewAuthService returns a service to manipulate auth
func NewAuthService(db dependencies.DB, log dependencies.Logger) *AuthServiceImplementation {
return &AuthServiceImplementation{db, log}
}
// GetLoginHandler returns an handled for the login route
func (s *AuthServiceImplementation) GetLoginHandler() http.HandlerFunc {
log := s.Log
db := s.DB
log.Info("[LoginHandler] Installed")
//@return a http.handlerFunc that will check for loginDataand return a JWT token if login is successful
return func(w http.ResponseWriter, r *http.Request) {
log.Info("[LoginHandler] Start")
loginData := LoginData{}
// Decode the incoming note json
err := json.NewDecoder(r.Body).Decode(&loginData)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
email := loginData.Email
password := loginData.Password
var u models.Account
db.Where("email = ?", email).First(&u)
if (u == models.Account{}) {
http.Error(w, "Invalid login/pass", http.StatusUnauthorized)
return
}
// Check password
log.Debug("Before check")
err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
log.Debug("After check (%s)", err)
if err != nil {
log.Info(err)
http.Error(w, "Invalid login/pass", http.StatusUnauthorized)
return
}
// Return JWT if password valid
// TODO: add role from database (not hardcoded)
tokenString, err := token.CreateToken(strconv.FormatInt(int64(u.ID), 9), token.TokenSecret, "admin")
if err != nil {
log.Info(err)
http.Error(w, "Unable to create token", http.StatusUnauthorized)
return
}
log.Debug("Token generated " + tokenString)
expire := time.Now().AddDate(-1, 0, 1)
cookie := http.Cookie{Name: "token", Value: tokenString, Expires: expire}
http.SetCookie(w, &cookie)
j, err := json.Marshal(LoginResponse{UserID: u.ID, Email: u.Email, IsAdmin: true, Token: tokenString, User: u.Email})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(j)
}
}
// GetSignupHandler returns a handler for the signup route
func (s *AuthServiceImplementation) GetSignupHandler() http.HandlerFunc {
log := s.Log
log.Info("[SignupHandler] Installed")
return func(w http.ResponseWriter, r *http.Request) {
email := r.FormValue("email")
password := r.FormValue("password")
log.Infof("In signup handler %s", email)
if email == "" {
http.Error(w, "Email missing", http.StatusInternalServerError)
return
}
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 13)
if err != nil {
log.Error("Can't generate hash for password: " + err.Error())
http.Error(w, "Can't generate hash for password", http.StatusInternalServerError)
return
}
u := models.Account{Email: email, Password: string(bytes), Status: "pending"}
u, err = s.createUser(&u)
if err != nil {
log.Infof("Could'nt create Account = (%+v)", u)
log.Error(err)
http.Error(w, "Can't create account in database", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/", http.StatusMovedPermanently)
}
}
// createUser is a method on AuthServiceImplementation that create an user
// copying the part in GetSignupHandler that create a user in database
func (s *AuthServiceImplementation) createUser(u *models.Account) (models.Account, error) {
db := s.DB
db.Create(u)
if u.ID == 0 {
return *u, fmt.Errorf("Can't create account in database")
}
return *u, nil
}
// GetAdminRoleRequiredMiddleware returns a middleware that checks if the user is an admin
func (s *AuthServiceImplementation) GetAdminRoleRequiredMiddleware() http.HandlerFunc {
log := s.Log
log.Info("Installing RoleMiddleware")
return func(next http.HandlerFunc) http.HandlerFunc {
log.Info("[RoleMiddleware]")
claims, OK := c.Get("claims")
if !OK {
http.Error(w, "No claims found", http.StatusForbidden)
return
}
claimsMap := claims.(jwt.MapClaims)
log.Infof("[RoleMiddleware] claimsMap[role] = (%s)", claimsMap["Role"])
log.Infof("[RoleMiddleware] claims = (%+v)", claims)
if claimsMap["Role"] != "admin" {
http.Error(w, "Not an admin", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
}
}