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:
linoman
2024-02-16 04:58:05 -06:00
committed by GitHub
parent 846eadff63
commit ac84069071
27 changed files with 300 additions and 105 deletions

View File

@ -812,6 +812,14 @@ use_refresh_token = false
#################################### Basic Auth ##########################
[auth.basic]
enabled = true
# This setting will enable a stronger password policy for user's password under basic auth.
# The password will need to comply with the following password policy
# 1. Have a minimum of 12 characters
# 2. Composed by at least 1 uppercase character
# 3. Composed by at least 1 lowercase character
# 4. Composed by at least 1 digit character
# 5. Composed by at least 1 symbol character
password_policy = false
#################################### Auth Proxy ##########################
[auth.proxy]

View File

@ -745,6 +745,7 @@
#################################### Basic Auth ##########################
[auth.basic]
;enabled = true
;password_policy = false
#################################### Auth Proxy ##########################
[auth.proxy]

View File

@ -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 {

View File

@ -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"`
}

View File

@ -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 {

View File

@ -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,

View File

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

View File

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

View File

@ -18,7 +18,7 @@ import (
const DefaultAdminUserId = 1
func resetPasswordCommand(c utils.CommandLine, runner server.Runner) error {
newPassword := ""
var newPassword user.Password
adminId := int64(c.Int("user-id"))
if c.Bool("password-from-stdin") {
@ -31,9 +31,13 @@ func resetPasswordCommand(c utils.CommandLine, runner server.Runner) error {
}
return fmt.Errorf("can't read password from stdin")
}
newPassword = scanner.Text()
newPassword = user.Password(scanner.Text())
} else {
newPassword = c.Args().First()
newPassword = user.Password(c.Args().First())
}
if err := newPassword.Validate(runner.Cfg); err != nil {
return fmt.Errorf("the new password doesn't meet the password policy criteria")
}
err := resetPassword(adminId, newPassword, runner.UserService)
@ -44,12 +48,7 @@ func resetPasswordCommand(c utils.CommandLine, runner server.Runner) error {
return err
}
func resetPassword(adminId int64, newPassword string, userSvc user.Service) error {
password := user.Password(newPassword)
if password.IsWeak() {
return fmt.Errorf("new password is too short")
}
func resetPassword(adminId int64, newPassword user.Password, userSvc user.Service) error {
userQuery := user.GetUserByIDQuery{ID: adminId}
usr, err := userSvc.GetByID(context.Background(), &userQuery)
if err != nil {
@ -59,14 +58,14 @@ func resetPassword(adminId int64, newPassword string, userSvc user.Service) erro
return ErrMustBeAdmin
}
passwordHashed, err := util.EncodePassword(newPassword, usr.Salt)
passwordHashed, err := util.EncodePassword(string(newPassword), usr.Salt)
if err != nil {
return err
}
cmd := user.ChangeUserPasswordCommand{
UserID: adminId,
NewPassword: passwordHashed,
NewPassword: user.Password(passwordHashed),
}
if err := userSvc.ChangePassword(context.Background(), &cmd); err != nil {

View File

@ -100,7 +100,7 @@ func (c *Grafana) AuthenticatePassword(ctx context.Context, r *authn.Request, us
// user was found so set auth module in req metadata
r.SetMeta(authn.MetaKeyAuthModule, "grafana")
if ok := comparePassword(password, usr.Salt, usr.Password); !ok {
if ok := comparePassword(password, usr.Salt, string(usr.Password)); !ok {
return nil, errInvalidPassword.Errorf("invalid password")
}

View File

@ -171,7 +171,7 @@ func TestGrafana_AuthenticatePassword(t *testing.T) {
hashed, _ := util.EncodePassword("password", "salt")
userService := &usertest.FakeUserService{
ExpectedSignedInUser: tt.expectedSignedInUser,
ExpectedUser: &user.User{Password: hashed, Salt: "salt"},
ExpectedUser: &user.User{Password: user.Password(hashed), Salt: "salt"},
}
if !tt.findUser {

View File

@ -70,7 +70,7 @@ func validateUserEmailCode(cfg *setting.Cfg, user *user.User, code string) (bool
}
// right active code
payload := strconv.FormatInt(user.ID, 10) + user.Email + user.Login + user.Password + user.Rands
payload := strconv.FormatInt(user.ID, 10) + user.Email + user.Login + string(user.Password) + user.Rands
expectedCode, err := createTimeLimitCode(cfg.SecretKey, payload, minutes, startStr)
if err != nil {
return false, err
@ -103,7 +103,7 @@ func getLoginForEmailCode(code string) string {
func createUserEmailCode(cfg *setting.Cfg, user *user.User, startStr string) (string, error) {
minutes := cfg.EmailCodeValidMinutes
payload := strconv.FormatInt(user.ID, 10) + user.Email + user.Login + user.Password + user.Rands
payload := strconv.FormatInt(user.ID, 10) + user.Email + user.Login + string(user.Password) + user.Rands
code, err := createTimeLimitCode(cfg.SecretKey, payload, minutes, startStr)
if err != nil {
return "", err

View File

@ -18,7 +18,7 @@ func TestTimeLimitCodes(t *testing.T) {
user := &user.User{ID: 10, Email: "t@a.com", Login: "asd", Password: "1", Rands: "2"}
format := "200601021504"
mailPayload := strconv.FormatInt(user.ID, 10) + user.Email + user.Login + user.Password + user.Rands
mailPayload := strconv.FormatInt(user.ID, 10) + user.Email + user.Login + string(user.Password) + user.Rands
tenMinutesAgo := time.Now().Add(-time.Minute * 10)
tests := []struct {

View File

@ -226,7 +226,7 @@ func (ss *SQLStore) ensureMainOrgAndAdminUser(test bool) error {
if _, err := ss.createUser(ctx, sess, user.CreateUserCommand{
Login: ss.Cfg.AdminUser,
Email: ss.Cfg.AdminEmail,
Password: ss.Cfg.AdminPassword,
Password: user.Password(ss.Cfg.AdminPassword),
IsAdmin: true,
}); err != nil {
return fmt.Errorf("failed to create admin user: %s", err)

View File

@ -88,11 +88,11 @@ func (ss *SQLStore) createUser(ctx context.Context, sess *DBSession, args user.C
usr.Rands = rands
if len(args.Password) > 0 {
encodedPassword, err := util.EncodePassword(args.Password, usr.Salt)
encodedPassword, err := util.EncodePassword(string(args.Password), usr.Salt)
if err != nil {
return usr, err
}
usr.Password = encodedPassword
usr.Password = user.Password(encodedPassword)
}
sess.UseBool("is_admin")

View File

@ -26,7 +26,7 @@ type User struct {
Email string
Name string
Login string
Password string
Password Password
Salt string
Rands string
Company string
@ -52,7 +52,7 @@ type CreateUserCommand struct {
Company string
OrgID int64
OrgName string
Password string
Password Password
EmailVerified bool
IsAdmin bool
IsDisabled bool
@ -79,8 +79,8 @@ type UpdateUserCommand struct {
}
type ChangeUserPasswordCommand struct {
OldPassword string `json:"oldPassword"`
NewPassword string `json:"newPassword"`
OldPassword Password `json:"oldPassword"`
NewPassword Password `json:"newPassword"`
UserID int64 `json:"-"`
}
@ -280,9 +280,3 @@ type AdminCreateUserResponse struct {
ID int64 `json:"id"`
Message string `json:"message"`
}
type Password string
func (p Password) IsWeak() bool {
return len(p) <= 4
}

View File

@ -0,0 +1,71 @@
package user
import (
"unicode"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil"
)
var (
ErrPasswordTooShort = errutil.NewBase(errutil.StatusBadRequest, "password-policy-too-short", errutil.WithPublicMessage("New password is too short"))
ErrPasswordPolicyInfringe = errutil.NewBase(errutil.StatusBadRequest, "password-policy-infringe", errutil.WithPublicMessage("New password doesn't comply with the password policy"))
MinPasswordLength = 12
)
type Password string
func NewPassword(newPassword string, config *setting.Cfg) (Password, error) {
if err := ValidatePassword(newPassword, config); err != nil {
return "", err
}
return Password(newPassword), nil
}
func (p Password) Validate(config *setting.Cfg) error {
return ValidatePassword(string(p), config)
}
// ValidatePassword checks if a new password meets the required criteria based on the given configuration.
// If BasicAuthStrongPasswordPolicy is disabled, it only checks for password length.
// Otherwise, it ensures the password meets the minimum length requirement and contains at least one uppercase letter,
// one lowercase letter, one number, and one symbol.
func ValidatePassword(newPassword string, config *setting.Cfg) error {
if !config.BasicAuthStrongPasswordPolicy {
if len(newPassword) <= 4 {
return ErrPasswordTooShort
}
return nil
}
if len(newPassword) < MinPasswordLength {
return ErrPasswordTooShort
}
hasUpperCase := false
hasLowerCase := false
hasNumber := false
hasSymbol := false
for _, r := range newPassword {
if !hasLowerCase && unicode.IsLower(r) {
hasLowerCase = true
}
if !hasUpperCase && unicode.IsUpper(r) {
hasUpperCase = true
}
if !hasNumber && unicode.IsNumber(r) {
hasNumber = true
}
if !hasSymbol && !unicode.IsLetter(r) && !unicode.IsNumber(r) {
hasSymbol = true
}
if hasUpperCase && hasLowerCase && hasNumber && hasSymbol {
return nil
}
}
return ErrPasswordPolicyInfringe
}

View File

@ -0,0 +1,93 @@
package user
import (
"testing"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/assert"
)
func TestPasswowrdService_ValidatePasswordHardcodePolicy(t *testing.T) {
LOWERCASE := "lowercase"
UPPERCASE := "UPPERCASE"
NUMBER := "123"
SYMBOLS := "!@#$%"
testCases := []struct {
expectedError error
name string
passwordTest string
strongPasswordPolicyEnabled bool
}{
{
name: "should return error when the password has less than 4 characters and strong password policy is disabled",
passwordTest: NUMBER,
expectedError: ErrPasswordTooShort,
strongPasswordPolicyEnabled: false,
},
{name: "should not return error when the password has 4 characters and strong password policy is disabled",
passwordTest: LOWERCASE,
expectedError: nil,
strongPasswordPolicyEnabled: false,
},
{
name: "should return error when the password has less than 12 characters and strong password policy is enabled",
passwordTest: NUMBER,
expectedError: ErrPasswordTooShort,
strongPasswordPolicyEnabled: true,
},
{
name: "should return error when the password is missing an uppercase character and strong password policy is enabled",
passwordTest: LOWERCASE + NUMBER + SYMBOLS,
expectedError: ErrPasswordPolicyInfringe,
strongPasswordPolicyEnabled: true,
},
{
name: "should return error when the password is missing a lowercase character and strong password policy is enabled",
passwordTest: UPPERCASE + NUMBER + SYMBOLS,
expectedError: ErrPasswordPolicyInfringe,
strongPasswordPolicyEnabled: true,
},
{
name: "should return error when the password is missing a number character and strong password policy is enabled",
passwordTest: LOWERCASE + UPPERCASE + SYMBOLS,
expectedError: ErrPasswordPolicyInfringe,
strongPasswordPolicyEnabled: true,
},
{
name: "should return error when the password is missing a symbol characters and strong password policy is enabled",
passwordTest: LOWERCASE + UPPERCASE + NUMBER,
expectedError: ErrPasswordPolicyInfringe,
strongPasswordPolicyEnabled: true,
},
{
name: "should not return error when the password has lowercase, uppercase, number and symbol and strong password policy is enabled",
passwordTest: LOWERCASE + UPPERCASE + NUMBER + SYMBOLS,
expectedError: nil,
strongPasswordPolicyEnabled: true,
},
{
name: "should not return error when the password has uppercase, number, symbol and lowercase and strong password policy is enabled",
passwordTest: UPPERCASE + NUMBER + SYMBOLS + LOWERCASE,
expectedError: nil,
strongPasswordPolicyEnabled: true,
},
{
name: "should not return error when the password has number, symbol, lowercase and uppercase and strong password policy is enabled",
passwordTest: NUMBER + SYMBOLS + LOWERCASE + UPPERCASE,
expectedError: nil,
strongPasswordPolicyEnabled: true,
},
{
name: "should not return error when the password has symbol, lowercase, uppercase and number and strong password policy is enabled",
passwordTest: SYMBOLS + LOWERCASE + UPPERCASE + NUMBER,
expectedError: nil,
strongPasswordPolicyEnabled: true,
},
}
for _, tc := range testCases {
cfg := setting.NewCfg()
cfg.BasicAuthStrongPasswordPolicy = tc.strongPasswordPolicyEnabled
err := ValidatePassword(tc.passwordTest, cfg)
assert.Equal(t, tc.expectedError, err)
}
}

View File

@ -209,7 +209,7 @@ func TestIntegrationUserDataAccess(t *testing.T) {
require.Nil(t, err)
require.Equal(t, result.Email, "usertest@test.com")
require.Equal(t, result.Password, "")
require.Equal(t, string(result.Password), "")
require.Len(t, result.Rands, 10)
require.Len(t, result.Salt, 10)
require.False(t, result.IsDisabled)
@ -218,7 +218,7 @@ func TestIntegrationUserDataAccess(t *testing.T) {
require.Nil(t, err)
require.Equal(t, result.Email, "usertest@test.com")
require.Equal(t, result.Password, "")
require.Equal(t, string(result.Password), "")
require.Len(t, result.Rands, 10)
require.Len(t, result.Salt, 10)
require.False(t, result.IsDisabled)
@ -230,7 +230,7 @@ func TestIntegrationUserDataAccess(t *testing.T) {
require.Nil(t, err)
require.Equal(t, result.Email, "usertest@test.com")
require.Equal(t, result.Password, "")
require.Equal(t, string(result.Password), "")
require.Len(t, result.Rands, 10)
require.Len(t, result.Salt, 10)
require.False(t, result.IsDisabled)
@ -243,7 +243,7 @@ func TestIntegrationUserDataAccess(t *testing.T) {
require.Nil(t, err)
require.Equal(t, result.Email, "usertest@test.com")
require.Equal(t, result.Password, "")
require.Equal(t, string(result.Password), "")
require.Len(t, result.Rands, 10)
require.Len(t, result.Salt, 10)
require.False(t, result.IsDisabled)
@ -252,7 +252,7 @@ func TestIntegrationUserDataAccess(t *testing.T) {
require.Nil(t, err)
require.Equal(t, result.Email, "usertest@test.com")
require.Equal(t, result.Password, "")
require.Equal(t, string(result.Password), "")
require.Len(t, result.Rands, 10)
require.Len(t, result.Salt, 10)
require.False(t, result.IsDisabled)

View File

@ -72,11 +72,16 @@ func ProvideService(
func (s *Service) GetUsageStats(ctx context.Context) map[string]any {
stats := map[string]any{}
caseInsensitiveLoginVal := 0
basicAuthStrongPasswordPolicyVal := 0
if s.cfg.CaseInsensitiveLogin {
caseInsensitiveLoginVal = 1
}
if s.cfg.BasicAuthStrongPasswordPolicy {
basicAuthStrongPasswordPolicyVal = 1
}
stats["stats.case_insensitive_login.count"] = caseInsensitiveLoginVal
stats["stats.password_policy.count"] = basicAuthStrongPasswordPolicyVal
count, err := s.store.CountUserAccountsWithEmptyRole(ctx)
if err != nil {
@ -161,11 +166,11 @@ func (s *Service) Create(ctx context.Context, cmd *user.CreateUserCommand) (*use
usr.Rands = rands
if len(cmd.Password) > 0 {
encodedPassword, err := util.EncodePassword(cmd.Password, usr.Salt)
encodedPassword, err := util.EncodePassword(string(cmd.Password), usr.Salt)
if err != nil {
return nil, err
}
usr.Password = encodedPassword
usr.Password = user.Password(encodedPassword)
}
_, err = s.store.Insert(ctx, usr)

View File

@ -219,13 +219,15 @@ func TestMetrics(t *testing.T) {
userService.cfg = setting.NewCfg()
userService.cfg.CaseInsensitiveLogin = true
userService.cfg.BasicAuthStrongPasswordPolicy = true
stats := userService.GetUsageStats(context.Background())
assert.NotEmpty(t, stats)
assert.Len(t, stats, 2, stats)
assert.Len(t, stats, 3, stats)
assert.Equal(t, 1, stats["stats.case_insensitive_login.count"])
assert.Equal(t, int64(1), stats["stats.user.role_none.count"])
assert.Equal(t, 1, stats["stats.password_policy.count"])
})
}

View File

@ -218,24 +218,25 @@ type Cfg struct {
DefaultHomeDashboardPath string
// Auth
LoginCookieName string
LoginMaxInactiveLifetime time.Duration
LoginMaxLifetime time.Duration
TokenRotationIntervalMinutes int
SigV4AuthEnabled bool
SigV4VerboseLogging bool
AzureAuthEnabled bool
AzureSkipOrgRoleSync bool
BasicAuthEnabled bool
AdminUser string
AdminPassword string
DisableLogin bool
AdminEmail string
DisableLoginForm bool
SignoutRedirectUrl string
IDResponseHeaderEnabled bool
IDResponseHeaderPrefix string
IDResponseHeaderNamespaces map[string]struct{}
LoginCookieName string
LoginMaxInactiveLifetime time.Duration
LoginMaxLifetime time.Duration
TokenRotationIntervalMinutes int
SigV4AuthEnabled bool
SigV4VerboseLogging bool
AzureAuthEnabled bool
AzureSkipOrgRoleSync bool
BasicAuthEnabled bool
BasicAuthStrongPasswordPolicy bool
AdminUser string
AdminPassword string
DisableLogin bool
AdminEmail string
DisableLoginForm bool
SignoutRedirectUrl string
IDResponseHeaderEnabled bool
IDResponseHeaderPrefix string
IDResponseHeaderNamespaces map[string]struct{}
// Not documented & not supported
// stand in until a more complete solution is implemented
AuthConfigUIAdminAccess bool
@ -1591,6 +1592,7 @@ func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) {
// basic auth
authBasic := iniFile.Section("auth.basic")
cfg.BasicAuthEnabled = authBasic.Key("enabled").MustBool(true)
cfg.BasicAuthStrongPasswordPolicy = authBasic.Key("password_policy").MustBool(false)
// Extended JWT auth
authExtendedJWT := cfg.SectionWithEnvOverrides("auth.extended_jwt")

View File

@ -50,7 +50,7 @@ func NewTestEnv(t *testing.T) TestContext {
type User struct {
User user.User
password string
password user.Password
}
type GetParams struct {

View File

@ -390,7 +390,7 @@ func (c K8sTestHelper) createTestUsers(orgName string) OrgUsers {
createUser := func(key string, role org.RoleType) User {
u, err := userSvc.Create(context.Background(), &user.CreateUserCommand{
DefaultOrgRole: string(role),
Password: key,
Password: user.Password(key),
Login: fmt.Sprintf("%s-%d", key, orgId),
OrgID: orgId,
})

View File

@ -2147,7 +2147,7 @@
"format": "int64"
},
"password": {
"type": "string"
"$ref": "#/definitions/Password"
}
}
},
@ -2268,7 +2268,7 @@
"type": "object",
"properties": {
"password": {
"type": "string"
"$ref": "#/definitions/Password"
}
}
},
@ -3020,10 +3020,10 @@
"type": "object",
"properties": {
"newPassword": {
"type": "string"
"$ref": "#/definitions/Password"
},
"oldPassword": {
"type": "string"
"$ref": "#/definitions/Password"
}
}
},
@ -5440,6 +5440,9 @@
}
}
},
"Password": {
"type": "string"
},
"PatchAnnotationsCmd": {
"type": "object",
"properties": {

View File

@ -11773,7 +11773,7 @@
"format": "int64"
},
"password": {
"type": "string"
"$ref": "#/definitions/Password"
}
}
},
@ -11894,7 +11894,7 @@
"type": "object",
"properties": {
"password": {
"type": "string"
"$ref": "#/definitions/Password"
}
}
},
@ -13196,10 +13196,10 @@
"type": "object",
"properties": {
"newPassword": {
"type": "string"
"$ref": "#/definitions/Password"
},
"oldPassword": {
"type": "string"
"$ref": "#/definitions/Password"
}
}
},
@ -17176,6 +17176,9 @@
}
}
},
"Password": {
"type": "string"
},
"PatchAnnotationsCmd": {
"type": "object",
"properties": {

View File

@ -2341,7 +2341,7 @@
"type": "integer"
},
"password": {
"type": "string"
"$ref": "#/components/schemas/Password"
}
},
"type": "object"
@ -2462,7 +2462,7 @@
"AdminUpdateUserPasswordForm": {
"properties": {
"password": {
"type": "string"
"$ref": "#/components/schemas/Password"
}
},
"type": "object"
@ -3764,10 +3764,10 @@
"ChangeUserPasswordCommand": {
"properties": {
"newPassword": {
"type": "string"
"$ref": "#/components/schemas/Password"
},
"oldPassword": {
"type": "string"
"$ref": "#/components/schemas/Password"
}
},
"type": "object"
@ -7745,6 +7745,9 @@
},
"type": "object"
},
"Password": {
"type": "string"
},
"PatchAnnotationsCmd": {
"properties": {
"data": {