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
parent
565515646a
commit
8ee0597486
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue