refac: reamaining auth surface rewrite, T0
This commit is contained in:
1 parent
6d510d2805
commit
aa865af2e9
50 files changed
+3723
-149
No files matched your search
@@ -2,6 +2,7 @@ package article
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/api/pkg/claims"
|
||||
validator "RenewCMS/api/pkg/validator"
|
||||
domain "RenewCMS/internal/domain/article"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
@@ -13,15 +14,15 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/jwtauth/v5"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
CreateUseCase *useCases.CreateArticleUseCase
|
||||
GetUseCase *useCases.GetArticleUseCase
|
||||
ListUseCase *useCases.ListArticlesUseCase
|
||||
DeleteUseCase *useCases.DeleteArticleUseCase
|
||||
UpdateUseCase *useCases.UpdateArticleUseCase
|
||||
CreateUseCase *useCases.CreateArticleUseCase
|
||||
GetUseCase *useCases.GetArticleUseCase
|
||||
ListUseCase *useCases.ListArticlesUseCase
|
||||
DeleteUseCase *useCases.DeleteArticleUseCase
|
||||
UpdateUseCase *useCases.UpdateArticleUseCase
|
||||
GetUserUseCase *useCases.GetUserUseCase
|
||||
}
|
||||
|
||||
type PostArticle struct {
|
||||
@@ -29,13 +30,14 @@ type PostArticle struct {
|
||||
Body string `json:"body" validate:"required,max=10000"`
|
||||
}
|
||||
|
||||
func NewHandler(create *useCases.CreateArticleUseCase, get *useCases.GetArticleUseCase, list *useCases.ListArticlesUseCase, delete *useCases.DeleteArticleUseCase, update *useCases.UpdateArticleUseCase) *Handler {
|
||||
func NewHandler(create *useCases.CreateArticleUseCase, get *useCases.GetArticleUseCase, list *useCases.ListArticlesUseCase, delete *useCases.DeleteArticleUseCase, update *useCases.UpdateArticleUseCase, getUser *useCases.GetUserUseCase) *Handler {
|
||||
return &Handler{
|
||||
CreateUseCase: create,
|
||||
GetUseCase: get,
|
||||
ListUseCase: list,
|
||||
DeleteUseCase: delete,
|
||||
UpdateUseCase: update,
|
||||
CreateUseCase: create,
|
||||
GetUseCase: get,
|
||||
ListUseCase: list,
|
||||
DeleteUseCase: delete,
|
||||
UpdateUseCase: update,
|
||||
GetUserUseCase: getUser,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,15 +186,24 @@ func (h *Handler) UnpublishArticle(w http.ResponseWriter, r *http.Request) {
|
||||
h.setIsOnline(w, r, false)
|
||||
}
|
||||
|
||||
func requestHasValidJwt(r *http.Request) bool {
|
||||
token, _, err := jwtauth.FromContext(r.Context())
|
||||
return token != nil && err == nil
|
||||
func (h *Handler) callerIsVerified(r *http.Request) bool {
|
||||
userId, ok := claims.UserId(r)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
currentUser, err := h.GetUserUseCase.GetUser(userId)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return currentUser.IsVerified
|
||||
}
|
||||
|
||||
func (h *Handler) ListArticles(w http.ResponseWriter, r *http.Request) {
|
||||
var articles []domain.Article
|
||||
var err error
|
||||
if requestHasValidJwt(r) {
|
||||
if h.callerIsVerified(r) {
|
||||
articles, err = h.ListUseCase.ListAllArticles()
|
||||
} else {
|
||||
articles, err = h.ListUseCase.ListOnlineArticles()
|
||||
|
||||
@@ -2,6 +2,7 @@ package auth
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/api/pkg/claims"
|
||||
validator "RenewCMS/api/pkg/validator"
|
||||
"RenewCMS/internal/domain/user"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
@@ -14,12 +15,16 @@ import (
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
Token *jwtauth.JWTAuth
|
||||
GetUseCase *useCases.GetUserUseCase
|
||||
ListUseCase *useCases.ListUsersUseCase
|
||||
CreateUseCase *useCases.CreateUserUseCase
|
||||
AuthenticateUseCase *useCases.AuthenticateUserUseCase
|
||||
IsProduction bool
|
||||
Token *jwtauth.JWTAuth
|
||||
GetUseCase *useCases.GetUserUseCase
|
||||
ListUseCase *useCases.ListUsersUseCase
|
||||
CreateUseCase *useCases.CreateUserUseCase
|
||||
AuthenticateUseCase *useCases.AuthenticateUserUseCase
|
||||
VerifyUseCase *useCases.VerifyUserUseCase
|
||||
CancelUseCase *useCases.CancelRegistrationUseCase
|
||||
RequestPasswordResetUseCase *useCases.RequestPasswordResetUseCase
|
||||
ResetPasswordUseCase *useCases.ResetPasswordUseCase
|
||||
IsProduction bool
|
||||
}
|
||||
|
||||
type RegisterCredentials struct {
|
||||
@@ -33,6 +38,20 @@ type LoginCredentials struct {
|
||||
Password string `json:"password" validate:"required,min=8"`
|
||||
}
|
||||
|
||||
type VerifyCredentials struct {
|
||||
Code string `json:"code" validate:"required"`
|
||||
}
|
||||
|
||||
type RequestPasswordResetCredentials struct {
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
}
|
||||
|
||||
type ResetPasswordCredentials struct {
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
Code string `json:"code" validate:"required"`
|
||||
Password string `json:"password" validate:"required,min=8"`
|
||||
}
|
||||
|
||||
func (h *Handler) removeJwtCookie(w http.ResponseWriter) {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "jwt",
|
||||
@@ -45,14 +64,18 @@ func (h *Handler) removeJwtCookie(w http.ResponseWriter) {
|
||||
})
|
||||
}
|
||||
|
||||
func NewHandler(token *jwtauth.JWTAuth, get *useCases.GetUserUseCase, list *useCases.ListUsersUseCase, create *useCases.CreateUserUseCase, authenticate *useCases.AuthenticateUserUseCase, isProduction bool) *Handler {
|
||||
func NewHandler(token *jwtauth.JWTAuth, get *useCases.GetUserUseCase, list *useCases.ListUsersUseCase, create *useCases.CreateUserUseCase, authenticate *useCases.AuthenticateUserUseCase, verify *useCases.VerifyUserUseCase, cancel *useCases.CancelRegistrationUseCase, requestPasswordReset *useCases.RequestPasswordResetUseCase, resetPassword *useCases.ResetPasswordUseCase, isProduction bool) *Handler {
|
||||
return &Handler{
|
||||
Token: token,
|
||||
GetUseCase: get,
|
||||
ListUseCase: list,
|
||||
CreateUseCase: create,
|
||||
AuthenticateUseCase: authenticate,
|
||||
IsProduction: isProduction,
|
||||
Token: token,
|
||||
GetUseCase: get,
|
||||
ListUseCase: list,
|
||||
CreateUseCase: create,
|
||||
AuthenticateUseCase: authenticate,
|
||||
VerifyUseCase: verify,
|
||||
CancelUseCase: cancel,
|
||||
RequestPasswordResetUseCase: requestPasswordReset,
|
||||
ResetPasswordUseCase: resetPassword,
|
||||
IsProduction: isProduction,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,21 +110,6 @@ func (h *Handler) IsLoggedIn(r *http.Request) bool {
|
||||
return token != nil && err == nil
|
||||
}
|
||||
|
||||
func (h *Handler) IsVerified(r *http.Request) bool {
|
||||
token, err := jwtauth.VerifyRequest(
|
||||
h.Token,
|
||||
r,
|
||||
jwtauth.TokenFromCookie,
|
||||
jwtauth.TokenFromHeader,
|
||||
jwtauth.TokenFromQuery)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
userId := token.PrivateClaims()["user_id"].(float64)
|
||||
currentUser, _ := h.GetUseCase.GetUser(uint32(userId))
|
||||
return currentUser.IsVerified
|
||||
}
|
||||
|
||||
func (h *Handler) Login(w http.ResponseWriter, r *http.Request) {
|
||||
var credentials LoginCredentials
|
||||
|
||||
@@ -163,6 +171,118 @@ func (h *Handler) Register(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write(message)
|
||||
}
|
||||
|
||||
func (h *Handler) Verify(w http.ResponseWriter, r *http.Request) {
|
||||
var credentials VerifyCredentials
|
||||
err := json.NewDecoder(r.Body).Decode(&credentials)
|
||||
if err != nil {
|
||||
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err = validator.Validate.Struct(credentials)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
userId, ok := claims.UserId(r)
|
||||
if !ok {
|
||||
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
verifiedUser, err := h.VerifyUseCase.Verify(userId, credentials.Code)
|
||||
if err != nil {
|
||||
if errors.Is(err, user.ErrUserNotFound) {
|
||||
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
if errors.Is(err, user.ErrInvalidVerificationCode) || errors.Is(err, user.ErrVerificationCodeExpired) {
|
||||
http.Error(w, apperr.VerificationFailedErrorMessage, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(verifiedUser)
|
||||
_, _ = w.Write(body)
|
||||
}
|
||||
|
||||
func (h *Handler) CancelRegistration(w http.ResponseWriter, r *http.Request) {
|
||||
userId, ok := claims.UserId(r)
|
||||
if !ok {
|
||||
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
err := h.CancelUseCase.CancelRegistration(userId)
|
||||
if err != nil {
|
||||
if errors.Is(err, user.ErrUserNotFound) {
|
||||
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
if errors.Is(err, user.ErrAlreadyVerified) {
|
||||
http.Error(w, apperr.AlreadyVerifiedErrorMessage, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
h.removeJwtCookie(w)
|
||||
message, _ := json.Marshal(map[string]any{"message": "Registration cancelled! Account deleted and HTTPonly jwt cookie cleared"})
|
||||
_, _ = w.Write(message)
|
||||
}
|
||||
|
||||
func (h *Handler) RequestPasswordReset(w http.ResponseWriter, r *http.Request) {
|
||||
var credentials RequestPasswordResetCredentials
|
||||
err := json.NewDecoder(r.Body).Decode(&credentials)
|
||||
if err != nil {
|
||||
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err = validator.Validate.Struct(credentials)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
_ = h.RequestPasswordResetUseCase.RequestPasswordReset(credentials.Email)
|
||||
|
||||
message, _ := json.Marshal(map[string]any{"message": apperr.PasswordResetRequestedMessage})
|
||||
_, _ = w.Write(message)
|
||||
}
|
||||
|
||||
func (h *Handler) ResetPassword(w http.ResponseWriter, r *http.Request) {
|
||||
var credentials ResetPasswordCredentials
|
||||
err := json.NewDecoder(r.Body).Decode(&credentials)
|
||||
if err != nil {
|
||||
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err = validator.Validate.Struct(credentials)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.ResetPasswordUseCase.ResetPassword(credentials.Email, credentials.Code, credentials.Password)
|
||||
if err != nil {
|
||||
if errors.Is(err, user.ErrInvalidPasswordResetCode) || errors.Is(err, user.ErrPasswordResetCodeExpired) {
|
||||
http.Error(w, apperr.PasswordResetFailedErrorMessage, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
message, _ := json.Marshal(map[string]any{"message": "Your password has been reset. You can now log in."})
|
||||
_, _ = w.Write(message)
|
||||
}
|
||||
|
||||
func (h *Handler) Logout(w http.ResponseWriter, _ *http.Request) {
|
||||
h.removeJwtCookie(w)
|
||||
message, _ := json.Marshal(map[string]any{"message": "User logged out! HTTPonly jwt cookie deleted"})
|
||||
|
||||
Reference in new issue
Block a user