Allow LDAP Sources to provide Avatars (#16851)
* Allow LDAP Sources to provide Avatars Add setting to LDAP source to allow it to provide an Avatar. Currently this is required to point to the image bytes. Fix #4144 Signed-off-by: Andrew Thornton <art27@cantab.net> * Rename as Avatar Attribute (drop JPEG) Signed-off-by: Andrew Thornton <art27@cantab.net> * Always synchronize avatar if there is change Signed-off-by: Andrew Thornton <art27@cantab.net> * Actually get the avatar from the ldap Signed-off-by: Andrew Thornton <art27@cantab.net> * clean-up Signed-off-by: Andrew Thornton <art27@cantab.net> * use len()>0 rather than != "" Signed-off-by: Andrew Thornton <art27@cantab.net> * slight shortcut in IsUploadAvatarChanged Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
This commit is contained in:
parent
7e98cd58dd
commit
123f0aea00
13 changed files with 80 additions and 6 deletions
|
@ -93,6 +93,10 @@ var (
|
||||||
Name: "skip-local-2fa",
|
Name: "skip-local-2fa",
|
||||||
Usage: "Set to true to skip local 2fa for users authenticated by this source",
|
Usage: "Set to true to skip local 2fa for users authenticated by this source",
|
||||||
},
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "avatar-attribute",
|
||||||
|
Usage: "The attribute of the user’s LDAP record containing the user’s avatar.",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
ldapBindDnCLIFlags = append(commonLdapCLIFlags,
|
ldapBindDnCLIFlags = append(commonLdapCLIFlags,
|
||||||
|
@ -234,6 +238,9 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
|
||||||
if c.IsSet("public-ssh-key-attribute") {
|
if c.IsSet("public-ssh-key-attribute") {
|
||||||
config.AttributeSSHPublicKey = c.String("public-ssh-key-attribute")
|
config.AttributeSSHPublicKey = c.String("public-ssh-key-attribute")
|
||||||
}
|
}
|
||||||
|
if c.IsSet("avatar-attribute") {
|
||||||
|
config.AttributeAvatar = c.String("avatar-attribute")
|
||||||
|
}
|
||||||
if c.IsSet("page-size") {
|
if c.IsSet("page-size") {
|
||||||
config.SearchPageSize = uint32(c.Uint("page-size"))
|
config.SearchPageSize = uint32(c.Uint("page-size"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ func TestAddLdapBindDn(t *testing.T) {
|
||||||
"--surname-attribute", "sn-bind full",
|
"--surname-attribute", "sn-bind full",
|
||||||
"--email-attribute", "mail-bind full",
|
"--email-attribute", "mail-bind full",
|
||||||
"--public-ssh-key-attribute", "publickey-bind full",
|
"--public-ssh-key-attribute", "publickey-bind full",
|
||||||
|
"--avatar-attribute", "avatar-bind full",
|
||||||
"--bind-dn", "cn=readonly,dc=full-domain-bind,dc=org",
|
"--bind-dn", "cn=readonly,dc=full-domain-bind,dc=org",
|
||||||
"--bind-password", "secret-bind-full",
|
"--bind-password", "secret-bind-full",
|
||||||
"--attributes-in-bind",
|
"--attributes-in-bind",
|
||||||
|
@ -71,6 +72,7 @@ func TestAddLdapBindDn(t *testing.T) {
|
||||||
AttributeMail: "mail-bind full",
|
AttributeMail: "mail-bind full",
|
||||||
AttributesInBind: true,
|
AttributesInBind: true,
|
||||||
AttributeSSHPublicKey: "publickey-bind full",
|
AttributeSSHPublicKey: "publickey-bind full",
|
||||||
|
AttributeAvatar: "avatar-bind full",
|
||||||
SearchPageSize: 99,
|
SearchPageSize: 99,
|
||||||
Filter: "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)",
|
Filter: "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)",
|
||||||
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
|
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
|
||||||
|
@ -269,6 +271,7 @@ func TestAddLdapSimpleAuth(t *testing.T) {
|
||||||
"--surname-attribute", "sn-simple full",
|
"--surname-attribute", "sn-simple full",
|
||||||
"--email-attribute", "mail-simple full",
|
"--email-attribute", "mail-simple full",
|
||||||
"--public-ssh-key-attribute", "publickey-simple full",
|
"--public-ssh-key-attribute", "publickey-simple full",
|
||||||
|
"--avatar-attribute", "avatar-simple full",
|
||||||
"--user-dn", "cn=%s,ou=Users,dc=full-domain-simple,dc=org",
|
"--user-dn", "cn=%s,ou=Users,dc=full-domain-simple,dc=org",
|
||||||
},
|
},
|
||||||
loginSource: &login.Source{
|
loginSource: &login.Source{
|
||||||
|
@ -288,6 +291,7 @@ func TestAddLdapSimpleAuth(t *testing.T) {
|
||||||
AttributeSurname: "sn-simple full",
|
AttributeSurname: "sn-simple full",
|
||||||
AttributeMail: "mail-simple full",
|
AttributeMail: "mail-simple full",
|
||||||
AttributeSSHPublicKey: "publickey-simple full",
|
AttributeSSHPublicKey: "publickey-simple full",
|
||||||
|
AttributeAvatar: "avatar-simple full",
|
||||||
Filter: "(&(objectClass=posixAccount)(full-simple-cn=%s))",
|
Filter: "(&(objectClass=posixAccount)(full-simple-cn=%s))",
|
||||||
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)",
|
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)",
|
||||||
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-simple,dc=org)",
|
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-simple,dc=org)",
|
||||||
|
@ -501,6 +505,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
|
||||||
"--surname-attribute", "sn-bind full",
|
"--surname-attribute", "sn-bind full",
|
||||||
"--email-attribute", "mail-bind full",
|
"--email-attribute", "mail-bind full",
|
||||||
"--public-ssh-key-attribute", "publickey-bind full",
|
"--public-ssh-key-attribute", "publickey-bind full",
|
||||||
|
"--avatar-attribute", "avatar-bind full",
|
||||||
"--bind-dn", "cn=readonly,dc=full-domain-bind,dc=org",
|
"--bind-dn", "cn=readonly,dc=full-domain-bind,dc=org",
|
||||||
"--bind-password", "secret-bind-full",
|
"--bind-password", "secret-bind-full",
|
||||||
"--synchronize-users",
|
"--synchronize-users",
|
||||||
|
@ -534,6 +539,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
|
||||||
AttributeMail: "mail-bind full",
|
AttributeMail: "mail-bind full",
|
||||||
AttributesInBind: false,
|
AttributesInBind: false,
|
||||||
AttributeSSHPublicKey: "publickey-bind full",
|
AttributeSSHPublicKey: "publickey-bind full",
|
||||||
|
AttributeAvatar: "avatar-bind full",
|
||||||
SearchPageSize: 99,
|
SearchPageSize: 99,
|
||||||
Filter: "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)",
|
Filter: "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)",
|
||||||
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
|
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
|
||||||
|
@ -932,6 +938,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
|
||||||
"--surname-attribute", "sn-simple full",
|
"--surname-attribute", "sn-simple full",
|
||||||
"--email-attribute", "mail-simple full",
|
"--email-attribute", "mail-simple full",
|
||||||
"--public-ssh-key-attribute", "publickey-simple full",
|
"--public-ssh-key-attribute", "publickey-simple full",
|
||||||
|
"--avatar-attribute", "avatar-simple full",
|
||||||
"--user-dn", "cn=%s,ou=Users,dc=full-domain-simple,dc=org",
|
"--user-dn", "cn=%s,ou=Users,dc=full-domain-simple,dc=org",
|
||||||
},
|
},
|
||||||
id: 7,
|
id: 7,
|
||||||
|
@ -952,6 +959,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
|
||||||
AttributeSurname: "sn-simple full",
|
AttributeSurname: "sn-simple full",
|
||||||
AttributeMail: "mail-simple full",
|
AttributeMail: "mail-simple full",
|
||||||
AttributeSSHPublicKey: "publickey-simple full",
|
AttributeSSHPublicKey: "publickey-simple full",
|
||||||
|
AttributeAvatar: "avatar-simple full",
|
||||||
Filter: "(&(objectClass=posixAccount)(full-simple-cn=%s))",
|
Filter: "(&(objectClass=posixAccount)(full-simple-cn=%s))",
|
||||||
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)",
|
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)",
|
||||||
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-simple,dc=org)",
|
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-simple,dc=org)",
|
||||||
|
|
|
@ -152,6 +152,7 @@ Admin operations:
|
||||||
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
|
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
|
||||||
- `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address. Required.
|
- `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address. Required.
|
||||||
- `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
|
- `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
|
||||||
|
- `--avatar-attribute value`: The attribute of the user’s LDAP record containing the user’s avatar.
|
||||||
- `--bind-dn value`: The DN to bind to the LDAP server with when searching for the user.
|
- `--bind-dn value`: The DN to bind to the LDAP server with when searching for the user.
|
||||||
- `--bind-password value`: The password for the Bind DN, if any.
|
- `--bind-password value`: The password for the Bind DN, if any.
|
||||||
- `--attributes-in-bind`: Fetch attributes in bind DN context.
|
- `--attributes-in-bind`: Fetch attributes in bind DN context.
|
||||||
|
@ -177,6 +178,7 @@ Admin operations:
|
||||||
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
|
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
|
||||||
- `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address.
|
- `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address.
|
||||||
- `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
|
- `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
|
||||||
|
- `--avatar-attribute value`: The attribute of the user’s LDAP record containing the user’s avatar.
|
||||||
- `--bind-dn value`: The DN to bind to the LDAP server with when searching for the user.
|
- `--bind-dn value`: The DN to bind to the LDAP server with when searching for the user.
|
||||||
- `--bind-password value`: The password for the Bind DN, if any.
|
- `--bind-password value`: The password for the Bind DN, if any.
|
||||||
- `--attributes-in-bind`: Fetch attributes in bind DN context.
|
- `--attributes-in-bind`: Fetch attributes in bind DN context.
|
||||||
|
@ -202,6 +204,7 @@ Admin operations:
|
||||||
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
|
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
|
||||||
- `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address. Required.
|
- `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address. Required.
|
||||||
- `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
|
- `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
|
||||||
|
- `--avatar-attribute value`: The attribute of the user’s LDAP record containing the user’s avatar.
|
||||||
- `--user-dn value`: The user’s DN. Required.
|
- `--user-dn value`: The user’s DN. Required.
|
||||||
- Examples:
|
- Examples:
|
||||||
- `gitea admin auth add-ldap-simple --name ldap --security-protocol unencrypted --host mydomain.org --port 389 --user-dn "cn=%s,ou=Users,dc=mydomain,dc=org" --user-filter "(&(objectClass=posixAccount)(cn=%s))" --email-attribute mail`
|
- `gitea admin auth add-ldap-simple --name ldap --security-protocol unencrypted --host mydomain.org --port 389 --user-dn "cn=%s,ou=Users,dc=mydomain,dc=org" --user-filter "(&(objectClass=posixAccount)(cn=%s))" --email-attribute mail`
|
||||||
|
@ -223,6 +226,7 @@ Admin operations:
|
||||||
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
|
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
|
||||||
- `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address.
|
- `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address.
|
||||||
- `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
|
- `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
|
||||||
|
- `--avatar-attribute value`: The attribute of the user’s LDAP record containing the user’s avatar.
|
||||||
- `--user-dn value`: The user’s DN.
|
- `--user-dn value`: The user’s DN.
|
||||||
- Examples:
|
- Examples:
|
||||||
- `gitea admin auth update-ldap-simple --id 1 --name "my ldap auth source"`
|
- `gitea admin auth update-ldap-simple --id 1 --name "my ldap auth source"`
|
||||||
|
|
|
@ -153,6 +153,15 @@ func (u *User) UploadAvatar(data []byte) error {
|
||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsUploadAvatarChanged returns true if the current user's avatar would be changed with the provided data
|
||||||
|
func (u *User) IsUploadAvatarChanged(data []byte) bool {
|
||||||
|
if !u.UseCustomAvatar || len(u.Avatar) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
avatarID := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", u.ID, md5.Sum(data)))))
|
||||||
|
return u.Avatar != avatarID
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteAvatar deletes the user's custom avatar.
|
// DeleteAvatar deletes the user's custom avatar.
|
||||||
func (u *User) DeleteAvatar() error {
|
func (u *User) DeleteAvatar() error {
|
||||||
aPath := u.CustomAvatarRelativePath()
|
aPath := u.CustomAvatarRelativePath()
|
||||||
|
|
|
@ -2421,6 +2421,7 @@ auths.attribute_name = First Name Attribute
|
||||||
auths.attribute_surname = Surname Attribute
|
auths.attribute_surname = Surname Attribute
|
||||||
auths.attribute_mail = Email Attribute
|
auths.attribute_mail = Email Attribute
|
||||||
auths.attribute_ssh_public_key = Public SSH Key Attribute
|
auths.attribute_ssh_public_key = Public SSH Key Attribute
|
||||||
|
auths.attribute_avatar = Avatar Attribute
|
||||||
auths.attributes_in_bind = Fetch Attributes in Bind DN Context
|
auths.attributes_in_bind = Fetch Attributes in Bind DN Context
|
||||||
auths.allow_deactivate_all = Allow an empty search result to deactivate all users
|
auths.allow_deactivate_all = Allow an empty search result to deactivate all users
|
||||||
auths.use_paged_search = Use Paged Search
|
auths.use_paged_search = Use Paged Search
|
||||||
|
|
|
@ -136,6 +136,7 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source {
|
||||||
AttributeMail: form.AttributeMail,
|
AttributeMail: form.AttributeMail,
|
||||||
AttributesInBind: form.AttributesInBind,
|
AttributesInBind: form.AttributesInBind,
|
||||||
AttributeSSHPublicKey: form.AttributeSSHPublicKey,
|
AttributeSSHPublicKey: form.AttributeSSHPublicKey,
|
||||||
|
AttributeAvatar: form.AttributeAvatar,
|
||||||
SearchPageSize: pageSize,
|
SearchPageSize: pageSize,
|
||||||
Filter: form.Filter,
|
Filter: form.Filter,
|
||||||
GroupsEnabled: form.GroupsEnabled,
|
GroupsEnabled: form.GroupsEnabled,
|
||||||
|
|
|
@ -42,6 +42,7 @@ type Source struct {
|
||||||
AttributeMail string // E-mail attribute
|
AttributeMail string // E-mail attribute
|
||||||
AttributesInBind bool // fetch attributes in bind context (not user)
|
AttributesInBind bool // fetch attributes in bind context (not user)
|
||||||
AttributeSSHPublicKey string // LDAP SSH Public Key attribute
|
AttributeSSHPublicKey string // LDAP SSH Public Key attribute
|
||||||
|
AttributeAvatar string
|
||||||
SearchPageSize uint32 // Search with paging page size
|
SearchPageSize uint32 // Search with paging page size
|
||||||
Filter string // Query filter to validate entry
|
Filter string // Query filter to validate entry
|
||||||
AdminFilter string // Query filter to check if user is admin
|
AdminFilter string // Query filter to check if user is admin
|
||||||
|
|
|
@ -96,6 +96,10 @@ func (source *Source) Authenticate(user *models.User, userName, password string)
|
||||||
err = models.RewriteAllPublicKeys()
|
err = models.RewriteAllPublicKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err == nil && len(source.AttributeAvatar) > 0 {
|
||||||
|
_ = user.UploadAvatar(sr.Avatar)
|
||||||
|
}
|
||||||
|
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ type SearchResult struct {
|
||||||
IsAdmin bool // if user is administrator
|
IsAdmin bool // if user is administrator
|
||||||
IsRestricted bool // if user is restricted
|
IsRestricted bool // if user is restricted
|
||||||
LowerName string // Lowername
|
LowerName string // Lowername
|
||||||
|
Avatar []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *Source) sanitizedUserQuery(username string) (string, bool) {
|
func (ls *Source) sanitizedUserQuery(username string) (string, bool) {
|
||||||
|
@ -266,7 +267,8 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var isAttributeSSHPublicKeySet = len(strings.TrimSpace(ls.AttributeSSHPublicKey)) > 0
|
isAttributeSSHPublicKeySet := len(strings.TrimSpace(ls.AttributeSSHPublicKey)) > 0
|
||||||
|
isAtributeAvatarSet := len(strings.TrimSpace(ls.AttributeAvatar)) > 0
|
||||||
|
|
||||||
attribs := []string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail}
|
attribs := []string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail}
|
||||||
if len(strings.TrimSpace(ls.UserUID)) > 0 {
|
if len(strings.TrimSpace(ls.UserUID)) > 0 {
|
||||||
|
@ -275,8 +277,11 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul
|
||||||
if isAttributeSSHPublicKeySet {
|
if isAttributeSSHPublicKeySet {
|
||||||
attribs = append(attribs, ls.AttributeSSHPublicKey)
|
attribs = append(attribs, ls.AttributeSSHPublicKey)
|
||||||
}
|
}
|
||||||
|
if isAtributeAvatarSet {
|
||||||
|
attribs = append(attribs, ls.AttributeAvatar)
|
||||||
|
}
|
||||||
|
|
||||||
log.Trace("Fetching attributes '%v', '%v', '%v', '%v', '%v', '%v' with filter '%s' and base '%s'", ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, ls.AttributeSSHPublicKey, ls.UserUID, userFilter, userDN)
|
log.Trace("Fetching attributes '%v', '%v', '%v', '%v', '%v', '%v', '%v' with filter '%s' and base '%s'", ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, ls.AttributeSSHPublicKey, ls.AttributeAvatar, ls.UserUID, userFilter, userDN)
|
||||||
search := ldap.NewSearchRequest(
|
search := ldap.NewSearchRequest(
|
||||||
userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter,
|
userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter,
|
||||||
attribs, nil)
|
attribs, nil)
|
||||||
|
@ -296,6 +301,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul
|
||||||
}
|
}
|
||||||
|
|
||||||
var sshPublicKey []string
|
var sshPublicKey []string
|
||||||
|
var Avatar []byte
|
||||||
|
|
||||||
username := sr.Entries[0].GetAttributeValue(ls.AttributeUsername)
|
username := sr.Entries[0].GetAttributeValue(ls.AttributeUsername)
|
||||||
firstname := sr.Entries[0].GetAttributeValue(ls.AttributeName)
|
firstname := sr.Entries[0].GetAttributeValue(ls.AttributeName)
|
||||||
|
@ -363,6 +369,10 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isAtributeAvatarSet {
|
||||||
|
Avatar = sr.Entries[0].GetRawAttributeValue(ls.AttributeAvatar)
|
||||||
|
}
|
||||||
|
|
||||||
return &SearchResult{
|
return &SearchResult{
|
||||||
LowerName: strings.ToLower(username),
|
LowerName: strings.ToLower(username),
|
||||||
Username: username,
|
Username: username,
|
||||||
|
@ -372,6 +382,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul
|
||||||
SSHPublicKey: sshPublicKey,
|
SSHPublicKey: sshPublicKey,
|
||||||
IsAdmin: isAdmin,
|
IsAdmin: isAdmin,
|
||||||
IsRestricted: isRestricted,
|
IsRestricted: isRestricted,
|
||||||
|
Avatar: Avatar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,14 +414,18 @@ func (ls *Source) SearchEntries() ([]*SearchResult, error) {
|
||||||
|
|
||||||
userFilter := fmt.Sprintf(ls.Filter, "*")
|
userFilter := fmt.Sprintf(ls.Filter, "*")
|
||||||
|
|
||||||
var isAttributeSSHPublicKeySet = len(strings.TrimSpace(ls.AttributeSSHPublicKey)) > 0
|
isAttributeSSHPublicKeySet := len(strings.TrimSpace(ls.AttributeSSHPublicKey)) > 0
|
||||||
|
isAtributeAvatarSet := len(strings.TrimSpace(ls.AttributeAvatar)) > 0
|
||||||
|
|
||||||
attribs := []string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail}
|
attribs := []string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail}
|
||||||
if isAttributeSSHPublicKeySet {
|
if isAttributeSSHPublicKeySet {
|
||||||
attribs = append(attribs, ls.AttributeSSHPublicKey)
|
attribs = append(attribs, ls.AttributeSSHPublicKey)
|
||||||
}
|
}
|
||||||
|
if isAtributeAvatarSet {
|
||||||
|
attribs = append(attribs, ls.AttributeAvatar)
|
||||||
|
}
|
||||||
|
|
||||||
log.Trace("Fetching attributes '%v', '%v', '%v', '%v', '%v' with filter %s and base %s", ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, ls.AttributeSSHPublicKey, userFilter, ls.UserBase)
|
log.Trace("Fetching attributes '%v', '%v', '%v', '%v', '%v', '%v' with filter %s and base %s", ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, ls.AttributeSSHPublicKey, ls.AttributeAvatar, userFilter, ls.UserBase)
|
||||||
search := ldap.NewSearchRequest(
|
search := ldap.NewSearchRequest(
|
||||||
ls.UserBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter,
|
ls.UserBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter,
|
||||||
attribs, nil)
|
attribs, nil)
|
||||||
|
@ -442,8 +457,10 @@ func (ls *Source) SearchEntries() ([]*SearchResult, error) {
|
||||||
if isAttributeSSHPublicKeySet {
|
if isAttributeSSHPublicKeySet {
|
||||||
result[i].SSHPublicKey = v.GetAttributeValues(ls.AttributeSSHPublicKey)
|
result[i].SSHPublicKey = v.GetAttributeValues(ls.AttributeSSHPublicKey)
|
||||||
}
|
}
|
||||||
|
if isAtributeAvatarSet {
|
||||||
|
result[i].Avatar = v.GetRawAttributeValue(ls.AttributeAvatar)
|
||||||
|
}
|
||||||
result[i].LowerName = strings.ToLower(result[i].Username)
|
result[i].LowerName = strings.ToLower(result[i].Username)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|
|
@ -112,12 +112,18 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("SyncExternalUsers[%s]: Error creating user %s: %v", source.loginSource.Name, su.Username, err)
|
log.Error("SyncExternalUsers[%s]: Error creating user %s: %v", source.loginSource.Name, su.Username, err)
|
||||||
} else if isAttributeSSHPublicKeySet {
|
}
|
||||||
|
|
||||||
|
if err == nil && isAttributeSSHPublicKeySet {
|
||||||
log.Trace("SyncExternalUsers[%s]: Adding LDAP Public SSH Keys for user %s", source.loginSource.Name, usr.Name)
|
log.Trace("SyncExternalUsers[%s]: Adding LDAP Public SSH Keys for user %s", source.loginSource.Name, usr.Name)
|
||||||
if models.AddPublicKeysBySource(usr, source.loginSource, su.SSHPublicKey) {
|
if models.AddPublicKeysBySource(usr, source.loginSource, su.SSHPublicKey) {
|
||||||
sshKeysNeedUpdate = true
|
sshKeysNeedUpdate = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err == nil && len(source.AttributeAvatar) > 0 {
|
||||||
|
_ = usr.UploadAvatar(su.Avatar)
|
||||||
|
}
|
||||||
} else if updateExisting {
|
} else if updateExisting {
|
||||||
// Synchronize SSH Public Key if that attribute is set
|
// Synchronize SSH Public Key if that attribute is set
|
||||||
if isAttributeSSHPublicKeySet && models.SynchronizePublicKeys(usr, source.loginSource, su.SSHPublicKey) {
|
if isAttributeSSHPublicKeySet && models.SynchronizePublicKeys(usr, source.loginSource, su.SSHPublicKey) {
|
||||||
|
@ -150,6 +156,13 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
|
||||||
log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", source.loginSource.Name, usr.Name, err)
|
log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", source.loginSource.Name, usr.Name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if usr.IsUploadAvatarChanged(su.Avatar) {
|
||||||
|
if err == nil && len(source.AttributeAvatar) > 0 {
|
||||||
|
_ = usr.UploadAvatar(su.Avatar)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ type AuthenticationForm struct {
|
||||||
AttributeSurname string
|
AttributeSurname string
|
||||||
AttributeMail string
|
AttributeMail string
|
||||||
AttributeSSHPublicKey string
|
AttributeSSHPublicKey string
|
||||||
|
AttributeAvatar string
|
||||||
AttributesInBind bool
|
AttributesInBind bool
|
||||||
UsePagedSearch bool
|
UsePagedSearch bool
|
||||||
SearchPageSize int
|
SearchPageSize int
|
||||||
|
|
|
@ -104,6 +104,10 @@
|
||||||
<label for="attribute_ssh_public_key">{{.i18n.Tr "admin.auths.attribute_ssh_public_key"}}</label>
|
<label for="attribute_ssh_public_key">{{.i18n.Tr "admin.auths.attribute_ssh_public_key"}}</label>
|
||||||
<input id="attribute_ssh_public_key" name="attribute_ssh_public_key" value="{{$cfg.AttributeSSHPublicKey}}" placeholder="e.g. SshPublicKey">
|
<input id="attribute_ssh_public_key" name="attribute_ssh_public_key" value="{{$cfg.AttributeSSHPublicKey}}" placeholder="e.g. SshPublicKey">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="attribute_avatar">{{.i18n.Tr "admin.auths.attribute_avatar"}}</label>
|
||||||
|
<input id="attribute_avatar" name="attribute_avatar" value="{{$cfg.AttributeAvatar}}" placeholder="e.g. jpegPhoto">
|
||||||
|
</div>
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<label for="groups_enabled"><strong>{{.i18n.Tr "admin.auths.verify_group_membership"}}</strong></label>
|
<label for="groups_enabled"><strong>{{.i18n.Tr "admin.auths.verify_group_membership"}}</strong></label>
|
||||||
|
|
|
@ -76,6 +76,10 @@
|
||||||
<label for="attribute_ssh_public_key">{{.i18n.Tr "admin.auths.attribute_ssh_public_key"}}</label>
|
<label for="attribute_ssh_public_key">{{.i18n.Tr "admin.auths.attribute_ssh_public_key"}}</label>
|
||||||
<input id="attribute_ssh_public_key" name="attribute_ssh_public_key" value="{{.attribute_ssh_public_key}}" placeholder="e.g. SshPublicKey">
|
<input id="attribute_ssh_public_key" name="attribute_ssh_public_key" value="{{.attribute_ssh_public_key}}" placeholder="e.g. SshPublicKey">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="attribute_avatar">{{.i18n.Tr "admin.auths.attribute_avatar"}}</label>
|
||||||
|
<input id="attribute_avatar" name="attribute_avatar" value="{{.attribute_avatar}}" placeholder="e.g. jpegPhoto">
|
||||||
|
</div>
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<label for="groups_enabled"><strong>{{.i18n.Tr "admin.auths.verify_group_membership"}}</strong></label>
|
<label for="groups_enabled"><strong>{{.i18n.Tr "admin.auths.verify_group_membership"}}</strong></label>
|
||||||
|
|
Reference in a new issue