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) } }