171 lines
4.9 KiB
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)
|
|
}
|
|
}
|