mirror of
https://github.com/hay-kot/homebox.git
synced 2024-11-21 16:15:45 +00:00
implement password reset
This commit is contained in:
parent
2231c54f21
commit
64d2957853
5 changed files with 87 additions and 13 deletions
|
@ -117,12 +117,10 @@ func (ctrl *V1Controller) HandleUserSelfDelete() errchain.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
type (
|
||||
ChangePassword struct {
|
||||
Current string `json:"current,omitempty"`
|
||||
New string `json:"new,omitempty"`
|
||||
}
|
||||
)
|
||||
type ChangePassword struct {
|
||||
Current string `json:"current,omitempty"`
|
||||
New string `json:"new,omitempty"`
|
||||
}
|
||||
|
||||
// HandleUserSelfChangePassword godoc
|
||||
//
|
||||
|
@ -146,7 +144,42 @@ func (ctrl *V1Controller) HandleUserSelfChangePassword() errchain.HandlerFunc {
|
|||
|
||||
ctx := services.NewContext(r.Context())
|
||||
|
||||
ok := ctrl.svc.User.ChangePassword(ctx, cp.Current, cp.New)
|
||||
ok := ctrl.svc.User.PasswordChange(ctx, cp.Current, cp.New)
|
||||
if !ok {
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return server.JSON(w, http.StatusNoContent, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleUserSelfChangePasswordWithToken godoc
|
||||
//
|
||||
// @Summary Change Password
|
||||
// @Tags User
|
||||
// @Success 204
|
||||
// @Param payload body ChangePassword true "Password Payload"
|
||||
// @Router /v1/users/change-password-token [PUT]
|
||||
func (ctrl *V1Controller) HandleUserSelfChangePasswordWithToken() errchain.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
tokenQueryParam := r.URL.Query().Get("token")
|
||||
if tokenQueryParam == "" {
|
||||
return validate.NewRequestError(fmt.Errorf("missing token query param"), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if ctrl.isDemo {
|
||||
return validate.NewRequestError(nil, http.StatusForbidden)
|
||||
}
|
||||
|
||||
var cp ChangePassword
|
||||
err := server.Decode(r, &cp)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("user failed to change password")
|
||||
}
|
||||
|
||||
ctx := services.NewContext(r.Context())
|
||||
|
||||
ok := ctrl.svc.User.PasswordChange(ctx, cp.Current, cp.New)
|
||||
if !ok {
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
@ -165,6 +198,10 @@ func (ctrl *V1Controller) HandleUserSelfChangePassword() errchain.HandlerFunc {
|
|||
// @Router /v1/users/request-password-reset [Post]
|
||||
func (ctrl *V1Controller) HandleUserRequestPasswordReset() errchain.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
if ctrl.isDemo {
|
||||
return validate.NewRequestError(nil, http.StatusForbidden)
|
||||
}
|
||||
|
||||
v, err := adapters.DecodeBody[services.PasswordResetRequest](r)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -173,7 +210,7 @@ func (ctrl *V1Controller) HandleUserRequestPasswordReset() errchain.HandlerFunc
|
|||
go func() {
|
||||
ctx := context.Background()
|
||||
|
||||
err = ctrl.svc.User.RequestPasswordReset(ctx, v)
|
||||
err = ctrl.svc.User.PasswordResetRequest(ctx, v)
|
||||
if err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
|
|
|
@ -85,6 +85,7 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain) {
|
|||
r.Post(v1Base("/users/logout"), chain.ToHandlerFunc(v1Ctrl.HandleAuthLogout(), userMW...))
|
||||
r.Get(v1Base("/users/refresh"), chain.ToHandlerFunc(v1Ctrl.HandleAuthRefresh(), userMW...))
|
||||
r.Put(v1Base("/users/self/change-password"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelfChangePassword(), userMW...))
|
||||
r.Put(v1Base("/users/self/change-password-token"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelfChangePasswordWithToken()))
|
||||
|
||||
r.Post(v1Base("/groups/invitations"), chain.ToHandlerFunc(v1Ctrl.HandleGroupInvitationsCreate(), userMW...))
|
||||
r.Get(v1Base("/groups/statistics"), chain.ToHandlerFunc(v1Ctrl.HandleGroupStatistics(), userMW...))
|
||||
|
|
|
@ -234,18 +234,18 @@ func (svc *UserService) DeleteSelf(ctx context.Context, userID uuid.UUID) error
|
|||
return svc.repos.Users.Delete(ctx, userID)
|
||||
}
|
||||
|
||||
func (svc *UserService) ChangePassword(ctx Context, current string, new string) (ok bool) {
|
||||
func (svc *UserService) PasswordChange(ctx Context, currentPassword, newPassword string) (ok bool) {
|
||||
usr, err := svc.repos.Users.GetOneID(ctx, ctx.UserID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !hasher.CheckPasswordHash(current, usr.PasswordHash) {
|
||||
if !hasher.CheckPasswordHash(currentPassword, usr.PasswordHash) {
|
||||
log.Err(errors.New("current password is incorrect")).Msg("Failed to change password")
|
||||
return false
|
||||
}
|
||||
|
||||
hashed, err := hasher.HashPassword(new)
|
||||
hashed, err := hasher.HashPassword(newPassword)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to hash password")
|
||||
return false
|
||||
|
@ -260,7 +260,37 @@ func (svc *UserService) ChangePassword(ctx Context, current string, new string)
|
|||
return true
|
||||
}
|
||||
|
||||
func (svc *UserService) RequestPasswordReset(ctx context.Context, req PasswordResetRequest) error {
|
||||
func (svc *UserService) PasswordChangeWithToken(ctx Context, token, newPassword string) error {
|
||||
hashed, err := hasher.HashPassword(newPassword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tokenHash := hasher.HashToken(token)
|
||||
|
||||
resetToken, err := svc.repos.Users.PasswordResetGet(ctx.Context, tokenHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resetToken.UserID != ctx.UserID {
|
||||
return ErrorTokenIDMismatch
|
||||
}
|
||||
|
||||
err = svc.repos.Users.ChangePassword(ctx.Context, ctx.UserID, hashed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = svc.repos.Users.PasswordResetDelete(ctx.Context, tokenHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *UserService) PasswordResetRequest(ctx context.Context, req PasswordResetRequest) error {
|
||||
usr, err := svc.repos.Users.GetOneEmail(ctx, req.Email)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("failed to get user for email reset")
|
||||
|
|
|
@ -149,3 +149,10 @@ func (r *UserRepository) PasswordResetGet(ctx context.Context, token []byte) (*e
|
|||
WithUser().
|
||||
Only(ctx)
|
||||
}
|
||||
|
||||
func (r *UserRepository) PasswordResetDelete(ctx context.Context, token []byte) error {
|
||||
_, err := r.db.ActionToken.Delete().
|
||||
Where(actiontoken.Token(token)).
|
||||
Exec(ctx)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -128,7 +128,6 @@
|
|||
}
|
||||
|
||||
toast.success("Password reset link sent to your email");
|
||||
return await Promise.resolve();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
Loading…
Reference in a new issue