mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	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:
		| @@ -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 New Issue
	
	Block a user