mirror of
https://github.com/hay-kot/homebox.git
synced 2024-11-22 00:25:43 +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 (
|
type ChangePassword struct {
|
||||||
ChangePassword struct {
|
Current string `json:"current,omitempty"`
|
||||||
Current string `json:"current,omitempty"`
|
New string `json:"new,omitempty"`
|
||||||
New string `json:"new,omitempty"`
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// HandleUserSelfChangePassword godoc
|
// HandleUserSelfChangePassword godoc
|
||||||
//
|
//
|
||||||
|
@ -146,7 +144,42 @@ func (ctrl *V1Controller) HandleUserSelfChangePassword() errchain.HandlerFunc {
|
||||||
|
|
||||||
ctx := services.NewContext(r.Context())
|
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 {
|
if !ok {
|
||||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
@ -165,6 +198,10 @@ func (ctrl *V1Controller) HandleUserSelfChangePassword() errchain.HandlerFunc {
|
||||||
// @Router /v1/users/request-password-reset [Post]
|
// @Router /v1/users/request-password-reset [Post]
|
||||||
func (ctrl *V1Controller) HandleUserRequestPasswordReset() errchain.HandlerFunc {
|
func (ctrl *V1Controller) HandleUserRequestPasswordReset() errchain.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) error {
|
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)
|
v, err := adapters.DecodeBody[services.PasswordResetRequest](r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -173,7 +210,7 @@ func (ctrl *V1Controller) HandleUserRequestPasswordReset() errchain.HandlerFunc
|
||||||
go func() {
|
go func() {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
err = ctrl.svc.User.RequestPasswordReset(ctx, v)
|
err = ctrl.svc.User.PasswordResetRequest(ctx, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn().
|
log.Warn().
|
||||||
Err(err).
|
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.Post(v1Base("/users/logout"), chain.ToHandlerFunc(v1Ctrl.HandleAuthLogout(), userMW...))
|
||||||
r.Get(v1Base("/users/refresh"), chain.ToHandlerFunc(v1Ctrl.HandleAuthRefresh(), 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"), 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.Post(v1Base("/groups/invitations"), chain.ToHandlerFunc(v1Ctrl.HandleGroupInvitationsCreate(), userMW...))
|
||||||
r.Get(v1Base("/groups/statistics"), chain.ToHandlerFunc(v1Ctrl.HandleGroupStatistics(), 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)
|
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)
|
usr, err := svc.repos.Users.GetOneID(ctx, ctx.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
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")
|
log.Err(errors.New("current password is incorrect")).Msg("Failed to change password")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
hashed, err := hasher.HashPassword(new)
|
hashed, err := hasher.HashPassword(newPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Err(err).Msg("Failed to hash password")
|
log.Err(err).Msg("Failed to hash password")
|
||||||
return false
|
return false
|
||||||
|
@ -260,7 +260,37 @@ func (svc *UserService) ChangePassword(ctx Context, current string, new string)
|
||||||
return true
|
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)
|
usr, err := svc.repos.Users.GetOneEmail(ctx, req.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn().Err(err).Msg("failed to get user for email reset")
|
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().
|
WithUser().
|
||||||
Only(ctx)
|
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");
|
toast.success("Password reset link sent to your email");
|
||||||
return await Promise.resolve();
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue