fix(authentication): err when user/display name same ldap attribute (#3364)
This fixes an issue when both the username and display name attributes are the same. If the username attribute is the same as the display name attribute previously we only set the display name profile value which is incorrect. We should set the username profile value instead and allow the display name to be blank.pull/3365/head
parent
0c28788c5f
commit
c427b8f920
|
@ -339,14 +339,9 @@ func (p *LDAPUserProvider) getUserProfile(client LDAPClient, username string) (p
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, attr := range searchResult.Entries[0].Attributes {
|
for _, attr := range searchResult.Entries[0].Attributes {
|
||||||
switch attr.Name {
|
|
||||||
case p.config.DisplayNameAttribute:
|
|
||||||
userProfile.DisplayName = attr.Values[0]
|
|
||||||
case p.config.MailAttribute:
|
|
||||||
userProfile.Emails = attr.Values
|
|
||||||
case p.config.UsernameAttribute:
|
|
||||||
attrs := len(attr.Values)
|
attrs := len(attr.Values)
|
||||||
|
|
||||||
|
if attr.Name == p.config.UsernameAttribute {
|
||||||
switch attrs {
|
switch attrs {
|
||||||
case 1:
|
case 1:
|
||||||
userProfile.Username = attr.Values[0]
|
userProfile.Username = attr.Values[0]
|
||||||
|
@ -358,6 +353,18 @@ func (p *LDAPUserProvider) getUserProfile(client LDAPClient, username string) (p
|
||||||
username, attrs, p.config.UsernameAttribute)
|
username, attrs, p.config.UsernameAttribute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if attrs == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr.Name == p.config.MailAttribute {
|
||||||
|
userProfile.Emails = attr.Values
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr.Name == p.config.DisplayNameAttribute {
|
||||||
|
userProfile.DisplayName = attr.Values[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if userProfile.Username == "" {
|
if userProfile.Username == "" {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/go-ldap/ldap/v3"
|
"github.com/go-ldap/ldap/v3"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
|
"github.com/authelia/authelia/v4/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StartupCheck implements the startup check provider interface.
|
// StartupCheck implements the startup check provider interface.
|
||||||
|
@ -88,10 +89,16 @@ func (p *LDAPUserProvider) parseDynamicUsersConfiguration() {
|
||||||
|
|
||||||
p.log.Tracef("Dynamically generated users filter is %s", p.config.UsersFilter)
|
p.log.Tracef("Dynamically generated users filter is %s", p.config.UsersFilter)
|
||||||
|
|
||||||
p.usersAttributes = []string{
|
if !utils.IsStringInSlice(p.config.UsernameAttribute, p.usersAttributes) {
|
||||||
p.config.DisplayNameAttribute,
|
p.usersAttributes = append(p.usersAttributes, p.config.UsernameAttribute)
|
||||||
p.config.MailAttribute,
|
}
|
||||||
p.config.UsernameAttribute,
|
|
||||||
|
if !utils.IsStringInSlice(p.config.MailAttribute, p.usersAttributes) {
|
||||||
|
p.usersAttributes = append(p.usersAttributes, p.config.MailAttribute)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.IsStringInSlice(p.config.DisplayNameAttribute, p.usersAttributes) {
|
||||||
|
p.usersAttributes = append(p.usersAttributes, p.config.DisplayNameAttribute)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.config.AdditionalUsersDN != "" {
|
if p.config.AdditionalUsersDN != "" {
|
||||||
|
|
|
@ -545,6 +545,222 @@ func TestShouldEscapeUserInput(t *testing.T) {
|
||||||
assert.EqualError(t, err, "user not found")
|
assert.EqualError(t, err, "user not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShouldReturnEmailWhenAttributeSameAsUsername(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
mockFactory := NewMockLDAPClientFactory(ctrl)
|
||||||
|
mockClient := NewMockLDAPClient(ctrl)
|
||||||
|
|
||||||
|
ldapClient := newLDAPUserProvider(
|
||||||
|
schema.LDAPAuthenticationBackendConfiguration{
|
||||||
|
URL: "ldap://127.0.0.1:389",
|
||||||
|
User: "cn=admin,dc=example,dc=com",
|
||||||
|
Password: "password",
|
||||||
|
UsernameAttribute: "mail",
|
||||||
|
MailAttribute: "mail",
|
||||||
|
DisplayNameAttribute: "displayName",
|
||||||
|
UsersFilter: "(&({username_attribute}={input})(objectClass=inetOrgPerson))",
|
||||||
|
AdditionalUsersDN: "ou=users",
|
||||||
|
BaseDN: "dc=example,dc=com",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
mockFactory)
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"mail", "displayName"}, ldapClient.usersAttributes)
|
||||||
|
|
||||||
|
dialURL := mockFactory.EXPECT().
|
||||||
|
DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()).
|
||||||
|
Return(mockClient, nil)
|
||||||
|
|
||||||
|
bind := mockClient.EXPECT().
|
||||||
|
Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")).
|
||||||
|
Return(nil)
|
||||||
|
|
||||||
|
search := mockClient.EXPECT().
|
||||||
|
Search(NewSearchRequestMatcher("(&(mail=john@example.com)(objectClass=inetOrgPerson))")).
|
||||||
|
Return(&ldap.SearchResult{
|
||||||
|
Entries: []*ldap.Entry{
|
||||||
|
{
|
||||||
|
DN: "uid=john,dc=example,dc=com",
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
{
|
||||||
|
Name: "mail",
|
||||||
|
Values: []string{"john@example.com"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "displayName",
|
||||||
|
Values: []string{"John Doe"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
|
||||||
|
gomock.InOrder(dialURL, bind, search)
|
||||||
|
|
||||||
|
client, err := ldapClient.connect()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
profile, err := ldapClient.getUserProfile(client, "john@example.com")
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
require.NotNil(t, profile)
|
||||||
|
|
||||||
|
assert.Equal(t, "uid=john,dc=example,dc=com", profile.DN)
|
||||||
|
assert.Equal(t, "john@example.com", profile.Username)
|
||||||
|
assert.Equal(t, "John Doe", profile.DisplayName)
|
||||||
|
|
||||||
|
require.Len(t, profile.Emails, 1)
|
||||||
|
assert.Equal(t, "john@example.com", profile.Emails[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShouldReturnUsernameAndBlankDisplayNameWhenAttributesTheSame(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
mockFactory := NewMockLDAPClientFactory(ctrl)
|
||||||
|
mockClient := NewMockLDAPClient(ctrl)
|
||||||
|
|
||||||
|
ldapClient := newLDAPUserProvider(
|
||||||
|
schema.LDAPAuthenticationBackendConfiguration{
|
||||||
|
URL: "ldap://127.0.0.1:389",
|
||||||
|
User: "cn=admin,dc=example,dc=com",
|
||||||
|
Password: "password",
|
||||||
|
UsernameAttribute: "uid",
|
||||||
|
MailAttribute: "mail",
|
||||||
|
DisplayNameAttribute: "uid",
|
||||||
|
UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=inetOrgPerson))",
|
||||||
|
AdditionalUsersDN: "ou=users",
|
||||||
|
BaseDN: "dc=example,dc=com",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
mockFactory)
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"uid", "mail"}, ldapClient.usersAttributes)
|
||||||
|
|
||||||
|
dialURL := mockFactory.EXPECT().
|
||||||
|
DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()).
|
||||||
|
Return(mockClient, nil)
|
||||||
|
|
||||||
|
bind := mockClient.EXPECT().
|
||||||
|
Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")).
|
||||||
|
Return(nil)
|
||||||
|
|
||||||
|
search := mockClient.EXPECT().
|
||||||
|
Search(NewSearchRequestMatcher("(&(|(uid=john@example.com)(mail=john@example.com))(objectClass=inetOrgPerson))")).
|
||||||
|
Return(&ldap.SearchResult{
|
||||||
|
Entries: []*ldap.Entry{
|
||||||
|
{
|
||||||
|
DN: "uid=john,dc=example,dc=com",
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
{
|
||||||
|
Name: "uid",
|
||||||
|
Values: []string{"john"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "mail",
|
||||||
|
Values: []string{"john@example.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
|
||||||
|
gomock.InOrder(dialURL, bind, search)
|
||||||
|
|
||||||
|
client, err := ldapClient.connect()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
profile, err := ldapClient.getUserProfile(client, "john@example.com")
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
require.NotNil(t, profile)
|
||||||
|
|
||||||
|
assert.Equal(t, "uid=john,dc=example,dc=com", profile.DN)
|
||||||
|
assert.Equal(t, "john", profile.Username)
|
||||||
|
assert.Equal(t, "john", profile.DisplayName)
|
||||||
|
|
||||||
|
require.Len(t, profile.Emails, 1)
|
||||||
|
assert.Equal(t, "john@example.com", profile.Emails[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShouldReturnBlankEmailAndDisplayNameWhenAttrsLenZero(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
mockFactory := NewMockLDAPClientFactory(ctrl)
|
||||||
|
mockClient := NewMockLDAPClient(ctrl)
|
||||||
|
|
||||||
|
ldapClient := newLDAPUserProvider(
|
||||||
|
schema.LDAPAuthenticationBackendConfiguration{
|
||||||
|
URL: "ldap://127.0.0.1:389",
|
||||||
|
User: "cn=admin,dc=example,dc=com",
|
||||||
|
Password: "password",
|
||||||
|
UsernameAttribute: "uid",
|
||||||
|
MailAttribute: "mail",
|
||||||
|
DisplayNameAttribute: "displayName",
|
||||||
|
UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=inetOrgPerson))",
|
||||||
|
AdditionalUsersDN: "ou=users",
|
||||||
|
BaseDN: "dc=example,dc=com",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
mockFactory)
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"uid", "mail", "displayName"}, ldapClient.usersAttributes)
|
||||||
|
|
||||||
|
dialURL := mockFactory.EXPECT().
|
||||||
|
DialURL(gomock.Eq("ldap://127.0.0.1:389"), gomock.Any()).
|
||||||
|
Return(mockClient, nil)
|
||||||
|
|
||||||
|
bind := mockClient.EXPECT().
|
||||||
|
Bind(gomock.Eq("cn=admin,dc=example,dc=com"), gomock.Eq("password")).
|
||||||
|
Return(nil)
|
||||||
|
|
||||||
|
search := mockClient.EXPECT().
|
||||||
|
Search(NewSearchRequestMatcher("(&(|(uid=john@example.com)(mail=john@example.com))(objectClass=inetOrgPerson))")).
|
||||||
|
Return(&ldap.SearchResult{
|
||||||
|
Entries: []*ldap.Entry{
|
||||||
|
{
|
||||||
|
DN: "uid=john,dc=example,dc=com",
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
{
|
||||||
|
Name: "uid",
|
||||||
|
Values: []string{"john"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "mail",
|
||||||
|
Values: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "displayName",
|
||||||
|
Values: []string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
|
||||||
|
gomock.InOrder(dialURL, bind, search)
|
||||||
|
|
||||||
|
client, err := ldapClient.connect()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
profile, err := ldapClient.getUserProfile(client, "john@example.com")
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
require.NotNil(t, profile)
|
||||||
|
|
||||||
|
assert.Equal(t, "uid=john,dc=example,dc=com", profile.DN)
|
||||||
|
assert.Equal(t, "john", profile.Username)
|
||||||
|
assert.Equal(t, "", profile.DisplayName)
|
||||||
|
|
||||||
|
assert.Len(t, profile.Emails, 0)
|
||||||
|
}
|
||||||
|
|
||||||
func TestShouldCombineUsernameFilterAndUsersFilter(t *testing.T) {
|
func TestShouldCombineUsernameFilterAndUsersFilter(t *testing.T) {
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
|
@ -569,6 +785,8 @@ func TestShouldCombineUsernameFilterAndUsersFilter(t *testing.T) {
|
||||||
nil,
|
nil,
|
||||||
mockFactory)
|
mockFactory)
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"uid", "mail", "displayName"}, ldapClient.usersAttributes)
|
||||||
|
|
||||||
assert.True(t, ldapClient.usersFilterReplacementInput)
|
assert.True(t, ldapClient.usersFilterReplacementInput)
|
||||||
|
|
||||||
mockClient.EXPECT().
|
mockClient.EXPECT().
|
||||||
|
|
Loading…
Reference in New Issue