mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08: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