Password policy (#82268)
* add password service interface * add password service implementation * add tests for password service * add password service wiring * add feature toggle * Rework from service interface to static function * Replace previous password validations * Add codeowners to password service * add error logs * update config files --------- Co-authored-by: Karl Persson <kalle.persson@grafana.com>
This commit is contained in:
@@ -115,8 +115,8 @@ func (hs *HTTPServer) AdminUpdateUserPassword(c *contextmodel.ReqContext) respon
|
||||
return response.Error(http.StatusBadRequest, "id is invalid", err)
|
||||
}
|
||||
|
||||
if len(form.Password) < 4 {
|
||||
return response.Error(http.StatusBadRequest, "New password too short", nil)
|
||||
if err := form.Password.Validate(hs.Cfg); err != nil {
|
||||
return response.Err(err)
|
||||
}
|
||||
|
||||
userQuery := user.GetUserByIDQuery{ID: userID}
|
||||
@@ -134,14 +134,14 @@ func (hs *HTTPServer) AdminUpdateUserPassword(c *contextmodel.ReqContext) respon
|
||||
}
|
||||
}
|
||||
|
||||
passwordHashed, err := util.EncodePassword(form.Password, usr.Salt)
|
||||
passwordHashed, err := util.EncodePassword(string(form.Password), usr.Salt)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Could not encode password", err)
|
||||
}
|
||||
|
||||
cmd := user.ChangeUserPasswordCommand{
|
||||
UserID: userID,
|
||||
NewPassword: passwordHashed,
|
||||
NewPassword: user.Password(passwordHashed),
|
||||
}
|
||||
|
||||
if err := hs.userService.ChangePassword(c.Req.Context(), &cmd); err != nil {
|
||||
|
||||
+10
-7
@@ -1,6 +1,9 @@
|
||||
package dtos
|
||||
|
||||
import "github.com/grafana/grafana/pkg/services/org"
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
type AddInviteForm struct {
|
||||
LoginOrEmail string `json:"loginOrEmail" binding:"Required"`
|
||||
@@ -17,10 +20,10 @@ type InviteInfo struct {
|
||||
}
|
||||
|
||||
type CompleteInviteForm struct {
|
||||
InviteCode string `json:"inviteCode"`
|
||||
Email string `json:"email" binding:"Required"`
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
ConfirmPassword string `json:"confirmPassword"`
|
||||
InviteCode string `json:"inviteCode"`
|
||||
Email string `json:"email" binding:"Required"`
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
Password user.Password `json:"password"`
|
||||
ConfirmPassword user.Password `json:"confirmPassword"`
|
||||
}
|
||||
|
||||
+17
-15
@@ -1,28 +1,30 @@
|
||||
package dtos
|
||||
|
||||
import "github.com/grafana/grafana/pkg/services/user"
|
||||
|
||||
type SignUpForm struct {
|
||||
Email string `json:"email" binding:"Required"`
|
||||
}
|
||||
|
||||
type SignUpStep2Form struct {
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Code string `json:"code"`
|
||||
OrgName string `json:"orgName"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
Password user.Password `json:"password"`
|
||||
Code string `json:"code"`
|
||||
OrgName string `json:"orgName"`
|
||||
}
|
||||
|
||||
type AdminCreateUserForm struct {
|
||||
Email string `json:"email"`
|
||||
Login string `json:"login"`
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password" binding:"Required"`
|
||||
OrgId int64 `json:"orgId"`
|
||||
Email string `json:"email"`
|
||||
Login string `json:"login"`
|
||||
Name string `json:"name"`
|
||||
Password user.Password `json:"password" binding:"Required"`
|
||||
OrgId int64 `json:"orgId"`
|
||||
}
|
||||
|
||||
type AdminUpdateUserPasswordForm struct {
|
||||
Password string `json:"password" binding:"Required"`
|
||||
Password user.Password `json:"password" binding:"Required"`
|
||||
}
|
||||
|
||||
type AdminUpdateUserPermissionsForm struct {
|
||||
@@ -34,9 +36,9 @@ type SendResetPasswordEmailForm struct {
|
||||
}
|
||||
|
||||
type ResetUserPasswordForm struct {
|
||||
Code string `json:"code"`
|
||||
NewPassword string `json:"newPassword"`
|
||||
ConfirmPassword string `json:"confirmPassword"`
|
||||
Code string `json:"code"`
|
||||
NewPassword user.Password `json:"newPassword"`
|
||||
ConfirmPassword user.Password `json:"confirmPassword"`
|
||||
}
|
||||
|
||||
type UserLookupDTO struct {
|
||||
|
||||
@@ -270,6 +270,10 @@ func (hs *HTTPServer) CompleteInvite(c *contextmodel.ReqContext) response.Respon
|
||||
}
|
||||
}
|
||||
|
||||
if err := completeInvite.Password.Validate(hs.Cfg); err != nil {
|
||||
return response.Err(err)
|
||||
}
|
||||
|
||||
cmd := user.CreateUserCommand{
|
||||
Email: completeInvite.Email,
|
||||
Name: completeInvite.Name,
|
||||
|
||||
+5
-4
@@ -97,17 +97,18 @@ func (hs *HTTPServer) ResetPassword(c *contextmodel.ReqContext) response.Respons
|
||||
return response.Error(http.StatusBadRequest, "Passwords do not match", nil)
|
||||
}
|
||||
|
||||
password := user.Password(form.NewPassword)
|
||||
if password.IsWeak() {
|
||||
return response.Error(http.StatusBadRequest, "New password is too short", nil)
|
||||
if err := form.NewPassword.Validate(hs.Cfg); err != nil {
|
||||
c.Logger.Warn("the new password doesn't meet the password policy criteria", "err", err)
|
||||
return response.Err(err)
|
||||
}
|
||||
|
||||
cmd := user.ChangeUserPasswordCommand{}
|
||||
cmd.UserID = userResult.ID
|
||||
cmd.NewPassword, err = util.EncodePassword(form.NewPassword, userResult.Salt)
|
||||
encodedPassword, err := util.EncodePassword(string(form.NewPassword), userResult.Salt)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to encode password", err)
|
||||
}
|
||||
cmd.NewPassword = user.Password(encodedPassword)
|
||||
|
||||
if err := hs.userService.ChangePassword(c.Req.Context(), &cmd); err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to change user password", err)
|
||||
|
||||
+7
-6
@@ -490,24 +490,25 @@ func (hs *HTTPServer) ChangeUserPassword(c *contextmodel.ReqContext) response.Re
|
||||
}
|
||||
}
|
||||
|
||||
passwordHashed, err := util.EncodePassword(cmd.OldPassword, usr.Salt)
|
||||
passwordHashed, err := util.EncodePassword(string(cmd.OldPassword), usr.Salt)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to encode password", err)
|
||||
}
|
||||
if passwordHashed != usr.Password {
|
||||
if user.Password(passwordHashed) != usr.Password {
|
||||
return response.Error(http.StatusUnauthorized, "Invalid old password", nil)
|
||||
}
|
||||
|
||||
password := user.Password(cmd.NewPassword)
|
||||
if password.IsWeak() {
|
||||
return response.Error(http.StatusBadRequest, "New password is too short", nil)
|
||||
if err := cmd.NewPassword.Validate(hs.Cfg); err != nil {
|
||||
c.Logger.Warn("the new password doesn't meet the password policy criteria", "err", err)
|
||||
return response.Err(err)
|
||||
}
|
||||
|
||||
cmd.UserID = userID
|
||||
cmd.NewPassword, err = util.EncodePassword(cmd.NewPassword, usr.Salt)
|
||||
encodedPassword, err := util.EncodePassword(string(cmd.NewPassword), usr.Salt)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to encode password", err)
|
||||
}
|
||||
cmd.NewPassword = user.Password(encodedPassword)
|
||||
|
||||
if err := hs.userService.ChangePassword(c.Req.Context(), &cmd); err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to change user password", err)
|
||||
|
||||
Reference in New Issue
Block a user