From 5a5ac4ed72310fe6630034818c6a7b028e9cbab8 Mon Sep 17 00:00:00 2001 From: liuyu <> Date: Fri, 13 Sep 2024 16:14:46 +0800 Subject: [PATCH] fix: update password with access token --- internal/authentication/file_user_provider.go | 4 +- .../authentication/file_user_provider_test.go | 6 +- .../kubesphere_user_provider.go | 88 ++++--------------- internal/authentication/ldap_user_provider.go | 4 +- .../authentication/ldap_user_provider_test.go | 34 +++---- internal/authentication/user_provider.go | 2 +- .../handlers/handler_reset_password_step2.go | 2 +- internal/mocks/user_provider.go | 5 +- 8 files changed, 47 insertions(+), 98 deletions(-) diff --git a/internal/authentication/file_user_provider.go b/internal/authentication/file_user_provider.go index e02ae289a8780..8d93e320ab5ee 100644 --- a/internal/authentication/file_user_provider.go +++ b/internal/authentication/file_user_provider.go @@ -19,6 +19,8 @@ import ( "github.com/authelia/authelia/v4/internal/logging" ) +var _ UserProvider = &FileUserProvider{} + // FileUserProvider is a provider reading details from a file. type FileUserProvider struct { config *schema.FileAuthenticationBackend @@ -96,7 +98,7 @@ func (p *FileUserProvider) GetDetails(username string) (details *UserDetails, er } // UpdatePassword update the password of the given user. -func (p *FileUserProvider) UpdatePassword(username string, newPassword string) (err error) { +func (p *FileUserProvider) UpdatePassword(username, _ string, newPassword string) (err error) { var details DatabaseUserDetails if details, err = p.database.GetUserDetails(username); err != nil { diff --git a/internal/authentication/file_user_provider_test.go b/internal/authentication/file_user_provider_test.go index 503a4d8cb244a..043b00f6f7330 100644 --- a/internal/authentication/file_user_provider_test.go +++ b/internal/authentication/file_user_provider_test.go @@ -156,7 +156,7 @@ func TestShouldUpdatePassword(t *testing.T) { assert.NoError(t, provider.StartupCheck()) - err := provider.UpdatePassword("harry", "newpassword") + err := provider.UpdatePassword("harry", "", "newpassword") assert.NoError(t, err) // Reset the provider to force a read from disk. @@ -181,7 +181,7 @@ func TestShouldUpdatePasswordHashingAlgorithmToArgon2id(t *testing.T) { assert.NoError(t, provider.StartupCheck()) assert.True(t, strings.HasPrefix(provider.database.Users["harry"].Digest.Encode(), "$6$")) - err := provider.UpdatePassword("harry", "newpassword") + err := provider.UpdatePassword("harry", "", "newpassword") assert.NoError(t, err) // Reset the provider to force a read from disk. @@ -208,7 +208,7 @@ func TestShouldUpdatePasswordHashingAlgorithmToSHA512(t *testing.T) { assert.NoError(t, provider.StartupCheck()) assert.True(t, strings.HasPrefix(provider.database.Users["john"].Digest.Encode(), "$argon2id$")) - err := provider.UpdatePassword("john", "newpassword") + err := provider.UpdatePassword("john", "", "newpassword") assert.NoError(t, err) // Reset the provider to force a read from disk. diff --git a/internal/authentication/kubesphere_user_provider.go b/internal/authentication/kubesphere_user_provider.go index 58878c956c622..ade7e1318211d 100644 --- a/internal/authentication/kubesphere_user_provider.go +++ b/internal/authentication/kubesphere_user_provider.go @@ -22,7 +22,6 @@ import ( "github.com/emicklei/go-restful/v3" "github.com/go-resty/resty/v2" - "github.com/jellydator/ttlcache/v3" "github.com/authelia/authelia/v4/internal/utils" ) @@ -32,23 +31,15 @@ const ( TokenCacheCapacity = 1000 ) -type UserCache struct { - token string - pwd string -} +var _ UserProvider = &KubesphereUserProvider{} type KubesphereUserProvider struct { client *resty.Client - cache *ttlcache.Cache[string, UserCache] } func NewKubesphereUserProvider() *KubesphereUserProvider { return &KubesphereUserProvider{ client: resty.New().SetTimeout(2 * time.Second), - cache: ttlcache.New( - ttlcache.WithTTL[string, UserCache](TokenCacheTTL), - ttlcache.WithCapacity[string, UserCache](TokenCacheCapacity), // max online client 1000. - ), } } @@ -92,80 +83,37 @@ func (p *KubesphereUserProvider) CheckUserPassword(username string, password str RefreshToken: tokens.RefreshToken, } - p.cache.Set(username, UserCache{tokens.AccessToken, password}, TokenCacheTTL) - return true, res, nil } func (p *KubesphereUserProvider) GetDetails(username string) (details *UserDetails, err error) { - token := p.cache.Get(username) - if token == nil { - info, err := utils.GetUserInfoFromBFL(p.client, username) - if err != nil { - return nil, err - } - - details := &UserDetails{ - Username: username, - DisplayName: username, - Groups: []string{info.OwnerRole}, - Emails: []string{username + "@myterminus.com"}, // FIXME: - } - - return details, nil - } else { - userUrl := fmt.Sprintf("http://%s.user-space-%s/bfl/iam/v1alpha1/users/%s", utils.BFL_NAME, username, username) - - resp, err := p.client.R(). - SetHeader(restful.HEADER_Accept, restful.MIME_JSON). - SetHeader(string(utils.TerminusAuthTokenHeader), token.Value().token). - SetResult(&utils.Response{Data: &utils.UserDetail{}}). - Get(userUrl) - - if err != nil { - return nil, err - } - - if resp.StatusCode() != http.StatusOK { - return nil, errors.New(string(resp.Body())) - } - - responseData := resp.Result().(*utils.Response) - - if responseData.Code != 0 { - return nil, errors.New(responseData.Message) - } - - d := responseData.Data.(*utils.UserDetail) - - details := &UserDetails{ - Username: username, - DisplayName: username, - // Emails: []string{d.Email}, - Emails: []string{username + "@myterminus.com"}, // FIXME: - Groups: d.Roles, - } + info, err := utils.GetUserInfoFromBFL(p.client, username) + if err != nil { + return nil, err + } - return details, nil + details = &UserDetails{ + Username: username, + DisplayName: username, + Groups: []string{info.OwnerRole}, + Emails: []string{username + "@myterminus.com"}, // FIXME: } + + return details, nil } // UpdatePassword update the password of the given user. -func (p *KubesphereUserProvider) UpdatePassword(username string, newPassword string) (err error) { - cache := p.cache.Get(username) - if cache == nil { - return ErrUserNotFound - } +func (p *KubesphereUserProvider) UpdatePassword(username, token string, newPassword string) (err error) { userUrl := fmt.Sprintf("http://%s.user-space-%s/bfl/iam/v1alpha1/users/%s/password", utils.BFL_NAME, username, username) reset := utils.PasswordReset{ - CurrentPassword: cache.Value().pwd, + CurrentPassword: "", Password: newPassword, } resp, err := p.client.R(). SetHeader(restful.HEADER_Accept, restful.MIME_JSON). - SetHeader(string(utils.TerminusAuthTokenHeader), cache.Value().token). + SetHeader(string(utils.TerminusAuthTokenHeader), token). SetResult(&utils.Response{}). SetBody(reset). Put(userUrl) @@ -226,12 +174,6 @@ func (p *KubesphereUserProvider) Refresh(username, token string) (res *ValidResu RefreshToken: tokens.RefreshToken, } - cache := p.cache.Get(username) - if cache != nil { - password := cache.Value().pwd - p.cache.Set(username, UserCache{tokens.AccessToken, password}, TokenCacheTTL) - } - return res, nil } diff --git a/internal/authentication/ldap_user_provider.go b/internal/authentication/ldap_user_provider.go index 3002cbb65e268..b31dafbff72c6 100644 --- a/internal/authentication/ldap_user_provider.go +++ b/internal/authentication/ldap_user_provider.go @@ -16,6 +16,8 @@ import ( "github.com/authelia/authelia/v4/internal/utils" ) +var _ UserProvider = &LDAPUserProvider{} + // LDAPUserProvider is a UserProvider that connects to LDAP servers like ActiveDirectory, OpenLDAP, OpenDJ, FreeIPA, etc. type LDAPUserProvider struct { config schema.LDAPAuthenticationBackend @@ -177,7 +179,7 @@ func (p *LDAPUserProvider) GetDetails(username string) (details *UserDetails, er } // UpdatePassword update the password of the given user. -func (p *LDAPUserProvider) UpdatePassword(username, password string) (err error) { +func (p *LDAPUserProvider) UpdatePassword(username, _, password string) (err error) { var ( client LDAPClient profile *ldapUserProfile diff --git a/internal/authentication/ldap_user_provider_test.go b/internal/authentication/ldap_user_provider_test.go index 3e4c9b87812aa..3bd59420840a4 100644 --- a/internal/authentication/ldap_user_provider_test.go +++ b/internal/authentication/ldap_user_provider_test.go @@ -1387,7 +1387,7 @@ func TestShouldNotUpdateUserPasswordConnect(t *testing.T) { err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") assert.EqualError(t, err, "unable to update password. Cause: dial failed with error: tcp timeout") } @@ -1464,7 +1464,7 @@ func TestShouldNotUpdateUserPasswordGetDetails(t *testing.T) { err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") assert.EqualError(t, err, "unable to update password. Cause: cannot find user DN of user 'john'. Cause: LDAP Result Code 2 \"Protocol Error\": permission error") } @@ -1571,7 +1571,7 @@ func TestShouldUpdateUserPassword(t *testing.T) { err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") require.NoError(t, err) } @@ -1680,7 +1680,7 @@ func TestShouldUpdateUserPasswordMSAD(t *testing.T) { err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") require.NoError(t, err) } @@ -1809,7 +1809,7 @@ func TestShouldUpdateUserPasswordMSADWithReferrals(t *testing.T) { err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") require.NoError(t, err) } @@ -1927,7 +1927,7 @@ func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralConnectErr(t *test err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") assert.EqualError(t, err, "unable to update password. Cause: error occurred connecting to referred LDAP server 'ldap://192.168.0.1': dial failed with error: tcp timeout. Original Error: LDAP Result Code 10 \"Referral\": error occurred") } @@ -2060,7 +2060,7 @@ func TestShouldUpdateUserPasswordMSADWithReferralsWithReferralModifyErr(t *testi err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") assert.EqualError(t, err, "unable to update password. Cause: error occurred performing modify on referred LDAP server 'ldap://192.168.0.1': LDAP Result Code 51 \"Busy\": error occurred. Original Error: LDAP Result Code 10 \"Referral\": error occurred") } @@ -2174,7 +2174,7 @@ func TestShouldUpdateUserPasswordMSADWithoutReferrals(t *testing.T) { err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") assert.EqualError(t, err, "unable to update password. Cause: LDAP Result Code 10 \"Referral\": error occurred") } @@ -2280,7 +2280,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtension(t *testing.T) { err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") require.NoError(t, err) } @@ -2408,7 +2408,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferrals(t *testing.T err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") require.NoError(t, err) } @@ -2521,7 +2521,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithoutReferrals(t *testin err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") assert.EqualError(t, err, "unable to update password. Cause: LDAP Result Code 10 \"Referral\": error occurred") } @@ -2638,7 +2638,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralConne err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") assert.EqualError(t, err, "unable to update password. Cause: error occurred connecting to referred LDAP server 'ldap://192.168.0.1': dial failed with error: tcp timeout. Original Error: LDAP Result Code 10 \"Referral\": error occurred") } @@ -2770,7 +2770,7 @@ func TestShouldUpdateUserPasswordPasswdModifyExtensionWithReferralsReferralPassw err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") assert.EqualError(t, err, "unable to update password. Cause: error occurred performing password modify on referred LDAP server 'ldap://192.168.0.1': LDAP Result Code 51 \"Busy\": too busy. Original Error: LDAP Result Code 10 \"Referral\": error occurred") } @@ -2881,7 +2881,7 @@ func TestShouldUpdateUserPasswordActiveDirectoryWithServerPolicyHints(t *testing err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") assert.NoError(t, err) } @@ -2992,7 +2992,7 @@ func TestShouldUpdateUserPasswordActiveDirectoryWithServerPolicyHintsDeprecated( err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") require.NoError(t, err) } @@ -3103,7 +3103,7 @@ func TestShouldUpdateUserPasswordActiveDirectory(t *testing.T) { err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") require.NoError(t, err) } @@ -3211,7 +3211,7 @@ func TestShouldUpdateUserPasswordBasic(t *testing.T) { err := provider.StartupCheck() require.NoError(t, err) - err = provider.UpdatePassword("john", "password") + err = provider.UpdatePassword("john", "", "password") require.NoError(t, err) } diff --git a/internal/authentication/user_provider.go b/internal/authentication/user_provider.go index d48e4ae4803e8..19a6e2bccea99 100644 --- a/internal/authentication/user_provider.go +++ b/internal/authentication/user_provider.go @@ -24,6 +24,6 @@ type UserProvider interface { CheckUserPassword(username string, password string) (valid bool, result *ValidResult, err error) GetDetails(username string) (details *UserDetails, err error) - UpdatePassword(username string, newPassword string) (err error) + UpdatePassword(username, accessToken string, newPassword string) (err error) Refresh(username, token string) (*ValidResult, error) } diff --git a/internal/handlers/handler_reset_password_step2.go b/internal/handlers/handler_reset_password_step2.go index 0db30c17ca403..fabeb91210d5d 100644 --- a/internal/handlers/handler_reset_password_step2.go +++ b/internal/handlers/handler_reset_password_step2.go @@ -43,7 +43,7 @@ func ResetPasswordPOST(ctx *middlewares.AutheliaCtx) { return } - if err = ctx.Providers.UserProvider.UpdatePassword(username, requestBody.Password); err != nil { + if err = ctx.Providers.UserProvider.UpdatePassword(username, userSession.AccessToken, requestBody.Password); err != nil { switch { case utils.IsStringInSliceContains(err.Error(), ldapPasswordComplexityCodes), utils.IsStringInSliceContains(err.Error(), ldapPasswordComplexityErrors): diff --git a/internal/mocks/user_provider.go b/internal/mocks/user_provider.go index 49d176a4dd9f3..5be75495df890 100644 --- a/internal/mocks/user_provider.go +++ b/internal/mocks/user_provider.go @@ -12,6 +12,9 @@ import ( authentication "github.com/authelia/authelia/v4/internal/authentication" ) +var _ authentication.UserProvider = &MockUserProvider{} + + // MockUserProvider is a mock of UserProvider interface. type MockUserProvider struct { ctrl *gomock.Controller @@ -80,7 +83,7 @@ func (mr *MockUserProviderMockRecorder) StartupCheck() *gomock.Call { } // UpdatePassword mocks base method. -func (m *MockUserProvider) UpdatePassword(arg0, arg1 string) error { +func (m *MockUserProvider) UpdatePassword(arg0, _, arg1 string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdatePassword", arg0, arg1) ret0, _ := ret[0].(error)