feat(authentication): use the passwordmodify exop for pwd resets with ldap (#2124)

Implement the LDAP password modify extended operation for LDAP providers that advertise they support it.
pull/2157/head
Arsenović Arsen 2021-07-06 09:13:17 +00:00 committed by GitHub
parent 565515646a
commit 8ee0597486
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 11 deletions

View File

@ -15,6 +15,7 @@ type LDAPConnection interface {
Search(searchRequest *ldap.SearchRequest) (*ldap.SearchResult, error) Search(searchRequest *ldap.SearchRequest) (*ldap.SearchResult, error)
Modify(modifyRequest *ldap.ModifyRequest) error Modify(modifyRequest *ldap.ModifyRequest) error
PasswordModify(pwdModifyRequest *ldap.PasswordModifyRequest) error
StartTLS(config *tls.Config) error StartTLS(config *tls.Config) error
} }
@ -48,6 +49,12 @@ func (lc *LDAPConnectionImpl) Modify(modifyRequest *ldap.ModifyRequest) error {
return lc.conn.Modify(modifyRequest) return lc.conn.Modify(modifyRequest)
} }
// PasswordModify modifies an ldap objects password.
func (lc *LDAPConnectionImpl) PasswordModify(pwdModifyRequest *ldap.PasswordModifyRequest) error {
_, err := lc.conn.PasswordModify(pwdModifyRequest)
return err
}
// StartTLS requests the LDAP server upgrades to TLS encryption. // StartTLS requests the LDAP server upgrades to TLS encryption.
func (lc *LDAPConnectionImpl) StartTLS(config *tls.Config) error { func (lc *LDAPConnectionImpl) StartTLS(config *tls.Config) error {
return lc.conn.StartTLS(config) return lc.conn.StartTLS(config)

View File

@ -88,6 +88,20 @@ func (mr *MockLDAPConnectionMockRecorder) Modify(modifyRequest interface{}) *gom
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Modify", reflect.TypeOf((*MockLDAPConnection)(nil).Modify), modifyRequest) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Modify", reflect.TypeOf((*MockLDAPConnection)(nil).Modify), modifyRequest)
} }
// PasswordModify mocks base method
func (m *MockLDAPConnection) PasswordModify(pwdModifyRequest *ldap.PasswordModifyRequest) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PasswordModify", pwdModifyRequest)
ret0, _ := ret[0].(error)
return ret0
}
// PasswordModify indicates an expected call of PasswordModify
func (mr *MockLDAPConnectionMockRecorder) PasswordModify(pwdModifyRequest interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordModify", reflect.TypeOf((*MockLDAPConnection)(nil).Modify), pwdModifyRequest)
}
// StartTLS mocks base method // StartTLS mocks base method
func (m *MockLDAPConnection) StartTLS(config *tls.Config) error { func (m *MockLDAPConnection) StartTLS(config *tls.Config) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View File

@ -342,20 +342,30 @@ func (p *LDAPUserProvider) UpdatePassword(inputUsername string, newPassword stri
return fmt.Errorf("Unable to update password. Cause: %s", err) return fmt.Errorf("Unable to update password. Cause: %s", err)
} }
modifyRequest := ldap.NewModifyRequest(profile.DN, nil) switch {
case p.supportExtensionPasswdModify:
modifyRequest := ldap.NewPasswordModifyRequest(
profile.DN,
"",
newPassword,
)
switch p.configuration.Implementation { err = conn.PasswordModify(modifyRequest)
case schema.LDAPImplementationActiveDirectory: case p.configuration.Implementation == schema.LDAPImplementationActiveDirectory:
modifyRequest := ldap.NewModifyRequest(profile.DN, nil)
utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)
// The password needs to be enclosed in quotes // The password needs to be enclosed in quotes
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/6e803168-f140-4d23-b2d3-c3a8ab5917d2 // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/6e803168-f140-4d23-b2d3-c3a8ab5917d2
pwdEncoded, _ := utf16.NewEncoder().String(fmt.Sprintf("\"%s\"", newPassword)) pwdEncoded, _ := utf16.NewEncoder().String(fmt.Sprintf("\"%s\"", newPassword))
modifyRequest.Replace("unicodePwd", []string{pwdEncoded}) modifyRequest.Replace("unicodePwd", []string{pwdEncoded})
default:
modifyRequest.Replace("userPassword", []string{newPassword})
}
err = conn.Modify(modifyRequest) err = conn.Modify(modifyRequest)
default:
modifyRequest := ldap.NewModifyRequest(profile.DN, nil)
modifyRequest.Replace("userPassword", []string{newPassword})
err = conn.Modify(modifyRequest)
}
if err != nil { if err != nil {
return fmt.Errorf("Unable to update password. Cause: %s", err) return fmt.Errorf("Unable to update password. Cause: %s", err)

View File

@ -649,10 +649,36 @@ func TestShouldUpdateUserPassword(t *testing.T) {
nil, nil,
mockFactory) mockFactory)
modifyRequest := ldap.NewModifyRequest("uid=test,dc=example,dc=com", nil) pwdModifyRequest := ldap.NewPasswordModifyRequest(
modifyRequest.Replace("userPassword", []string{"password"}) "uid=test,dc=example,dc=com",
"",
"password",
)
gomock.InOrder( gomock.InOrder(
mockFactory.EXPECT().
DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()).
Return(mockConn, nil),
mockConn.EXPECT().
Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")).
Return(nil),
mockConn.EXPECT().
Search(NewExtendedSearchRequestMatcher("(objectClass=*)", "", ldap.ScopeBaseObject, ldap.NeverDerefAliases, false, []string{ldapSupportedExtensionAttribute})).
Return(&ldap.SearchResult{
Entries: []*ldap.Entry{
{
DN: "",
Attributes: []*ldap.EntryAttribute{
{
Name: ldapSupportedExtensionAttribute,
Values: []string{ldapOIDPasswdModifyExtension},
},
},
},
},
}, nil),
mockFactory.EXPECT(). mockFactory.EXPECT().
DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()). DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()).
Return(mockConn, nil), Return(mockConn, nil),
@ -683,14 +709,16 @@ func TestShouldUpdateUserPassword(t *testing.T) {
}, },
}, nil), }, nil),
mockConn.EXPECT(). mockConn.EXPECT().
Modify(modifyRequest). PasswordModify(pwdModifyRequest).
Return(nil), Return(nil),
mockConn.EXPECT(). mockConn.EXPECT().
Close(), Close(),
) )
err := ldapClient.UpdatePassword("john", "password") err := ldapClient.checkServer()
require.NoError(t, err)
err = ldapClient.UpdatePassword("john", "password")
require.NoError(t, err) require.NoError(t, err)
} }