Changes
14 changed files (+423/-65)
-
-
@@ -260,7 +260,6 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error {if c.IsSet("skip-local-2fa") { config.SkipLocalTwoFA = c.Bool("skip-local-2fa") } return nil }
-
-
-
@@ -11,6 +11,9 @@ import ("strings" "testing" "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/services/auth" "github.com/stretchr/testify/assert"
-
@@ -97,7 +100,13 @@ func getLDAPServerHost() string {return host } func addAuthSourceLDAP(t *testing.T, sshKeyAttribute string) { func addAuthSourceLDAP(t *testing.T, sshKeyAttribute string, groupMapParams ...string) { groupTeamMapRemoval := "off" groupTeamMap := "" if len(groupMapParams) == 2 { groupTeamMapRemoval = groupMapParams[0] groupTeamMap = groupMapParams[1] } session := loginUser(t, "user1") csrf := GetCSRF(t, session, "/admin/auths/new") req := NewRequestWithValues(t, "POST", "/admin/auths/new", map[string]string{
-
@@ -119,6 +128,12 @@ func addAuthSourceLDAP(t *testing.T, sshKeyAttribute string) {"attribute_ssh_public_key": sshKeyAttribute, "is_sync_enabled": "on", "is_active": "on", "groups_enabled": "on", "group_dn": "ou=people,dc=planetexpress,dc=com", "group_member_uid": "member", "group_team_map": groupTeamMap, "group_team_map_removal": groupTeamMapRemoval, "user_uid": "DN", }) session.MakeRequest(t, req, http.StatusFound) }
-
@@ -294,3 +309,105 @@ func TestLDAPUserSSHKeySync(t *testing.T) {assert.ElementsMatch(t, u.SSHKeys, syncedKeys, "Unequal number of keys synchronized for user: %s", u.UserName) } } func TestLDAPGroupTeamSyncAddMember(t *testing.T) { if skipLDAPTests() { t.Skip() return } defer prepareTestEnv(t)() addAuthSourceLDAP(t, "", "on", `{"cn=ship_crew,ou=people,dc=planetexpress,dc=com":{"org26": ["team11"]},"cn=admin_staff,ou=people,dc=planetexpress,dc=com": {"non-existent": ["non-existent"]}}`) org, err := models.GetOrgByName("org26") assert.NoError(t, err) team, err := models.GetTeam(org.ID, "team11") assert.NoError(t, err) auth.SyncExternalUsers(context.Background(), true) for _, gitLDAPUser := range gitLDAPUsers { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ Name: gitLDAPUser.UserName, }).(*user_model.User) usersOrgs, err := models.FindOrgs(models.FindOrgOptions{ UserID: user.ID, IncludePrivate: true, }) assert.NoError(t, err) allOrgTeams, err := models.GetUserOrgTeams(org.ID, user.ID) assert.NoError(t, err) if user.Name == "fry" || user.Name == "leela" || user.Name == "bender" { // assert members of LDAP group "cn=ship_crew" are added to mapped teams assert.Equal(t, len(usersOrgs), 1, "User [%s] should be member of one organization", user.Name) assert.Equal(t, usersOrgs[0].Name, "org26", "Membership should be added to the right organization") isMember, err := models.IsTeamMember(usersOrgs[0].ID, team.ID, user.ID) assert.NoError(t, err) assert.True(t, isMember, "Membership should be added to the right team") err = team.RemoveMember(user.ID) assert.NoError(t, err) err = usersOrgs[0].RemoveMember(user.ID) assert.NoError(t, err) } else { // assert members of LDAP group "cn=admin_staff" keep initial team membership since mapped team does not exist assert.Empty(t, usersOrgs, "User should be member of no organization") isMember, err := models.IsTeamMember(org.ID, team.ID, user.ID) assert.NoError(t, err) assert.False(t, isMember, "User should no be added to this team") assert.Empty(t, allOrgTeams, "User should not be added to any team") } } } func TestLDAPGroupTeamSyncRemoveMember(t *testing.T) { if skipLDAPTests() { t.Skip() return } defer prepareTestEnv(t)() addAuthSourceLDAP(t, "", "on", `{"cn=dispatch,ou=people,dc=planetexpress,dc=com": {"org26": ["team11"]}}`) org, err := models.GetOrgByName("org26") assert.NoError(t, err) team, err := models.GetTeam(org.ID, "team11") assert.NoError(t, err) loginUserWithPassword(t, gitLDAPUsers[0].UserName, gitLDAPUsers[0].Password) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ Name: gitLDAPUsers[0].UserName, }).(*user_model.User) err = org.AddMember(user.ID) assert.NoError(t, err) err = team.AddMember(user.ID) assert.NoError(t, err) isMember, err := models.IsOrganizationMember(org.ID, user.ID) assert.NoError(t, err) assert.True(t, isMember, "User should be member of this organization") isMember, err = models.IsTeamMember(org.ID, team.ID, user.ID) assert.NoError(t, err) assert.True(t, isMember, "User should be member of this team") // assert team member "professor" gets removed from org26 team11 loginUserWithPassword(t, gitLDAPUsers[0].UserName, gitLDAPUsers[0].Password) isMember, err = models.IsOrganizationMember(org.ID, user.ID) assert.NoError(t, err) assert.False(t, isMember, "User membership should have been removed from organization") isMember, err = models.IsTeamMember(org.ID, team.ID, user.ID) assert.NoError(t, err) assert.False(t, isMember, "User membership should have been removed from team") } // Login should work even if Team Group Map contains a broken JSON func TestBrokenLDAPMapUserSignin(t *testing.T) { if skipLDAPTests() { t.Skip() return } defer prepareTestEnv(t)() addAuthSourceLDAP(t, "", "on", `{"NOT_A_VALID_JSON"["MISSING_DOUBLE_POINT"]}`) u := gitLDAPUsers[0] session := loginUserWithPassword(t, u.UserName, u.Password) req := NewRequest(t, "GET", "/user/settings") resp := session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) assert.Equal(t, u.UserName, htmlDoc.GetInputValueByName("name")) assert.Equal(t, u.FullName, htmlDoc.GetInputValueByName("full_name")) assert.Equal(t, u.Email, htmlDoc.Find(`label[for="email"]`).Siblings().First().Text()) }
-
-
-
@@ -2581,11 +2581,13 @@ auths.filter = User Filterauths.admin_filter = Admin Filter auths.restricted_filter = Restricted Filter auths.restricted_filter_helper = Leave empty to not set any users as restricted. Use an asterisk ('*') to set all users that do not match Admin Filter as restricted. auths.verify_group_membership = Verify group membership in LDAP auths.verify_group_membership = Verify group membership in LDAP (leave the filter empty to skip) auths.group_search_base = Group Search Base DN auths.valid_groups_filter = Valid Groups Filter auths.group_attribute_list_users = Group Attribute Containing List Of Users auths.user_attribute_in_group = User Attribute Listed In Group auths.map_group_to_team = Map LDAP groups to Organization teams (leave the field empty to skip) auths.map_group_to_team_removal = Remove users from synchronized teams if user does not belong to corresponding LDAP group auths.enable_ldap_groups = Enable LDAP groups auths.ms_ad_sa = MS AD Search Attributes auths.smtp_auth = SMTP Authentication Type auths.smtphost = SMTP Host
-
-
-
@@ -145,6 +145,8 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source {GroupDN: form.GroupDN, GroupFilter: form.GroupFilter, GroupMemberUID: form.GroupMemberUID, GroupTeamMap: form.GroupTeamMap, GroupTeamMapRemoval: form.GroupTeamMapRemoval, UserUID: form.UserUID, AdminFilter: form.AdminFilter, RestrictedFilter: form.RestrictedFilter,
-
-
-
@@ -120,3 +120,11 @@ share the following fields:* Group Attribute for User (optional) * Which group LDAP attribute contains an array above user attribute names. * Example: memberUid * Team group map (optional) * Automatically add users to Organization teams, depending on LDAP group memberships. * Note: this function only adds users to teams, it never removes users. * Example: {"cn=MyGroup,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2", ...], ...}, ...} * Team group map removal (optional) * If set to true, users will be removed from teams if they are not members of the corresponding group.
-
-
-
@@ -52,6 +52,8 @@ type Source struct {GroupDN string // Group Search Base GroupFilter string // Group Name Filter GroupMemberUID string // Group Attribute containing array of UserUID GroupTeamMap string // Map LDAP groups to teams GroupTeamMapRemoval bool // Remove user from teams which are synchronized and user is not a member of the corresponding LDAP group UserUID string // User Attribute listed in Group SkipLocalTwoFA bool `json:",omitempty"` // Skip Local 2fa for users authenticated with this source
-
-
-
@@ -8,6 +8,7 @@ import ("fmt" "strings" "code.gitea.io/gitea/models" asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db"
-
@@ -59,10 +60,14 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str} if user != nil { if source.GroupsEnabled && (source.GroupTeamMap != "" || source.GroupTeamMapRemoval) { orgCache := make(map[string]*models.Organization) teamCache := make(map[string]*models.Team) source.SyncLdapGroupsToTeams(user, sr.LdapTeamAdd, sr.LdapTeamRemove, orgCache, teamCache) } if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(user, source.authSource, sr.SSHPublicKey) { return user, asymkey_model.RewriteAllPublicKeys() } return user, nil }
-
@@ -98,10 +103,14 @@ func (source *Source) Authenticate(user *user_model.User, userName, password strif isAttributeSSHPublicKeySet && asymkey_model.AddPublicKeysBySource(user, source.authSource, sr.SSHPublicKey) { err = asymkey_model.RewriteAllPublicKeys() } if err == nil && len(source.AttributeAvatar) > 0 { _ = user_service.UploadAvatar(user, sr.Avatar) } if source.GroupsEnabled && (source.GroupTeamMap != "" || source.GroupTeamMapRemoval) { orgCache := make(map[string]*models.Organization) teamCache := make(map[string]*models.Team) source.SyncLdapGroupsToTeams(user, sr.LdapTeamAdd, sr.LdapTeamRemove, orgCache, teamCache) } return user, err }
-
-
-
@@ -0,0 +1,100 @@// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package ldap import ( "code.gitea.io/gitea/models" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" ) // SyncLdapGroupsToTeams maps LDAP groups to organization and team memberships func (source *Source) SyncLdapGroupsToTeams(user *user_model.User, ldapTeamAdd, ldapTeamRemove map[string][]string, orgCache map[string]*models.Organization, teamCache map[string]*models.Team) { var err error if source.GroupsEnabled && source.GroupTeamMapRemoval { // when the user is not a member of configs LDAP group, remove mapped organizations/teams memberships removeMappedMemberships(user, ldapTeamRemove, orgCache, teamCache) } for orgName, teamNames := range ldapTeamAdd { org, ok := orgCache[orgName] if !ok { org, err = models.GetOrgByName(orgName) if err != nil { // organization must be created before LDAP group sync log.Warn("LDAP group sync: Could not find organisation %s: %v", orgName, err) continue } orgCache[orgName] = org } if isMember, err := models.IsOrganizationMember(org.ID, user.ID); !isMember && err == nil { log.Trace("LDAP group sync: adding user [%s] to organization [%s]", user.Name, org.Name) err = org.AddMember(user.ID) if err != nil { log.Error("LDAP group sync: Could not add user to organization: %v", err) continue } } for _, teamName := range teamNames { team, ok := teamCache[orgName+teamName] if !ok { team, err = org.GetTeam(teamName) if err != nil { // team must be created before LDAP group sync log.Warn("LDAP group sync: Could not find team %s: %v", teamName, err) continue } teamCache[orgName+teamName] = team } if isMember, err := models.IsTeamMember(org.ID, team.ID, user.ID); !isMember && err == nil { log.Trace("LDAP group sync: adding user [%s] to team [%s]", user.Name, org.Name) } else { continue } err := team.AddMember(user.ID) if err != nil { log.Error("LDAP group sync: Could not add user to team: %v", err) } } } } // remove membership to organizations/teams if user is not member of corresponding LDAP group // e.g. lets assume user is member of LDAP group "x", but LDAP group team map contains LDAP groups "x" and "y" // then users membership gets removed for all organizations/teams mapped by LDAP group "y" func removeMappedMemberships(user *user_model.User, ldapTeamRemove map[string][]string, orgCache map[string]*models.Organization, teamCache map[string]*models.Team) { var err error for orgName, teamNames := range ldapTeamRemove { org, ok := orgCache[orgName] if !ok { org, err = models.GetOrgByName(orgName) if err != nil { // organization must be created before LDAP group sync log.Warn("LDAP group sync: Could not find organisation %s: %v", orgName, err) continue } orgCache[orgName] = org } for _, teamName := range teamNames { team, ok := teamCache[orgName+teamName] if !ok { team, err = org.GetTeam(teamName) if err != nil { // team must must be created before LDAP group sync log.Warn("LDAP group sync: Could not find team %s: %v", teamName, err) continue } } if isMember, err := models.IsTeamMember(org.ID, team.ID, user.ID); isMember && err == nil { log.Trace("LDAP group sync: removing user [%s] from team [%s]", user.Name, org.Name) } else { continue } err = team.RemoveMember(user.ID) if err != nil { log.Error("LDAP group sync: Could not remove user from team: %v", err) } } } }
-
-
-
@@ -12,22 +12,26 @@ import ("strconv" "strings" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" "github.com/go-ldap/ldap/v3" ) // SearchResult : user data type SearchResult struct { Username string // Username Name string // Name Surname string // Surname Mail string // E-mail address SSHPublicKey []string // SSH Public Key IsAdmin bool // if user is administrator IsRestricted bool // if user is restricted LowerName string // Lowername Avatar []byte Username string // Username Name string // Name Surname string // Surname Mail string // E-mail address SSHPublicKey []string // SSH Public Key IsAdmin bool // if user is administrator IsRestricted bool // if user is restricted LowerName string // LowerName Avatar []byte LdapTeamAdd map[string][]string // organizations teams to add LdapTeamRemove map[string][]string // organizations teams to remove } func (ls *Source) sanitizedUserQuery(username string) (string, bool) {
-
@@ -192,6 +196,71 @@ func checkRestricted(l *ldap.Conn, ls *Source, userDN string) bool {return false } // List all group memberships of a user func (ls *Source) listLdapGroupMemberships(l *ldap.Conn, uid string) []string { var ldapGroups []string groupFilter := fmt.Sprintf("(%s=%s)", ls.GroupMemberUID, uid) result, err := l.Search(ldap.NewSearchRequest( ls.GroupDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, groupFilter, []string{}, nil, )) if err != nil { log.Error("Failed group search using filter[%s]: %v", groupFilter, err) return ldapGroups } for _, entry := range result.Entries { if entry.DN == "" { log.Error("LDAP search was successful, but found no DN!") continue } ldapGroups = append(ldapGroups, entry.DN) } return ldapGroups } // parse LDAP groups and return map of ldap groups to organizations teams func (ls *Source) mapLdapGroupsToTeams() map[string]map[string][]string { ldapGroupsToTeams := make(map[string]map[string][]string) err := json.Unmarshal([]byte(ls.GroupTeamMap), &ldapGroupsToTeams) if err != nil { log.Error("Failed to unmarshall LDAP teams map: %v", err) return ldapGroupsToTeams } return ldapGroupsToTeams } // getMappedMemberships : returns the organizations and teams to modify the users membership func (ls *Source) getMappedMemberships(l *ldap.Conn, uid string) (map[string][]string, map[string][]string) { // get all LDAP group memberships for user usersLdapGroups := ls.listLdapGroupMemberships(l, uid) // unmarshall LDAP group team map from configs ldapGroupsToTeams := ls.mapLdapGroupsToTeams() membershipsToAdd := map[string][]string{} membershipsToRemove := map[string][]string{} for group, memberships := range ldapGroupsToTeams { isUserInGroup := util.IsStringInSlice(group, usersLdapGroups) if isUserInGroup { for org, teams := range memberships { membershipsToAdd[org] = teams } } else if !isUserInGroup { for org, teams := range memberships { membershipsToRemove[org] = teams } } } return membershipsToAdd, membershipsToRemove } // SearchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResult { // See https://tools.ietf.org/search/rfc4513#section-5.1.2
-
@@ -308,9 +377,12 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResulsurname := sr.Entries[0].GetAttributeValue(ls.AttributeSurname) mail := sr.Entries[0].GetAttributeValue(ls.AttributeMail) uid := sr.Entries[0].GetAttributeValue(ls.UserUID) if ls.UserUID == "dn" || ls.UserUID == "DN" { uid = sr.Entries[0].DN } // Check group membership if ls.GroupsEnabled { if ls.GroupsEnabled && ls.GroupFilter != "" { groupFilter, ok := ls.sanitizedGroupFilter(ls.GroupFilter) if !ok { return nil
-
@@ -373,16 +445,24 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResulAvatar = sr.Entries[0].GetRawAttributeValue(ls.AttributeAvatar) } teamsToAdd := make(map[string][]string) teamsToRemove := make(map[string][]string) if ls.GroupsEnabled && (ls.GroupTeamMap != "" || ls.GroupTeamMapRemoval) { teamsToAdd, teamsToRemove = ls.getMappedMemberships(l, uid) } return &SearchResult{ LowerName: strings.ToLower(username), Username: username, Name: firstname, Surname: surname, Mail: mail, SSHPublicKey: sshPublicKey, IsAdmin: isAdmin, IsRestricted: isRestricted, Avatar: Avatar, LowerName: strings.ToLower(username), Username: username, Name: firstname, Surname: surname, Mail: mail, SSHPublicKey: sshPublicKey, IsAdmin: isAdmin, IsRestricted: isRestricted, Avatar: Avatar, LdapTeamAdd: teamsToAdd, LdapTeamRemove: teamsToRemove, } }
-
@@ -417,7 +497,7 @@ func (ls *Source) SearchEntries() ([]*SearchResult, error) {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, ls.UserUID} if isAttributeSSHPublicKeySet { attribs = append(attribs, ls.AttributeSSHPublicKey) }
-
@@ -444,12 +524,23 @@ func (ls *Source) SearchEntries() ([]*SearchResult, error) {result := make([]*SearchResult, len(sr.Entries)) for i, v := range sr.Entries { teamsToAdd := make(map[string][]string) teamsToRemove := make(map[string][]string) if ls.GroupsEnabled && (ls.GroupTeamMap != "" || ls.GroupTeamMapRemoval) { userAttributeListedInGroup := v.GetAttributeValue(ls.UserUID) if ls.UserUID == "dn" || ls.UserUID == "DN" { userAttributeListedInGroup = v.DN } teamsToAdd, teamsToRemove = ls.getMappedMemberships(l, userAttributeListedInGroup) } result[i] = &SearchResult{ Username: v.GetAttributeValue(ls.AttributeUsername), Name: v.GetAttributeValue(ls.AttributeName), Surname: v.GetAttributeValue(ls.AttributeSurname), Mail: v.GetAttributeValue(ls.AttributeMail), IsAdmin: checkAdmin(l, ls, v.DN), Username: v.GetAttributeValue(ls.AttributeUsername), Name: v.GetAttributeValue(ls.AttributeName), Surname: v.GetAttributeValue(ls.AttributeSurname), Mail: v.GetAttributeValue(ls.AttributeMail), IsAdmin: checkAdmin(l, ls, v.DN), LdapTeamAdd: teamsToAdd, LdapTeamRemove: teamsToRemove, } if !result[i].IsAdmin { result[i].IsRestricted = checkRestricted(l, ls, v.DN)
-
-
-
@@ -10,6 +10,7 @@ import ("sort" "strings" "code.gitea.io/gitea/models" asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user"
-
@@ -61,6 +62,8 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {}) userPos := 0 orgCache := make(map[string]*models.Organization) teamCache := make(map[string]*models.Team) for _, su := range sr { select {
-
@@ -166,6 +169,10 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {} } } // Synchronize LDAP groups with organization and team memberships if source.GroupsEnabled && (source.GroupTeamMap != "" || source.GroupTeamMapRemoval) { source.SyncLdapGroupsToTeams(usr, su.LdapTeamAdd, su.LdapTeamRemove, orgCache, teamCache) } } // Rewrite authorized_keys file if LDAP Public SSH Key attribute is set and any key was added or removed
-
-
-
@@ -79,6 +79,8 @@ type AuthenticationForm struct {SSPIStripDomainNames bool SSPISeparatorReplacement string `binding:"AlphaDashDot;MaxSize(5)"` SSPIDefaultLanguage string GroupTeamMap string GroupTeamMapRemoval bool } // Validate validates fields
-
-
-
@@ -108,31 +108,43 @@<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> <!-- ldap group begin --> <div class="inline field"> <div class="ui checkbox"> <label for="groups_enabled"><strong>{{.i18n.Tr "admin.auths.verify_group_membership"}}</strong></label> <input id="groups_enabled" name="groups_enabled" type="checkbox" {{if $cfg.GroupsEnabled}}checked{{end}}> <label><strong>{{.i18n.Tr "admin.auths.enable_ldap_groups"}}</strong></label> <input type="checkbox" name="groups_enabled" class="js-ldap-group-toggle" {{if $cfg.GroupsEnabled}}checked{{end}}> </div> </div> <div id="groups_enabled_change"> <div id="ldap-group-options" class="ui segment secondary" {{if not $cfg.GroupsEnabled}}hidden{{end}}> <div class="field"> <label>{{.i18n.Tr "admin.auths.group_search_base"}}</label> <input name="group_dn" value="{{$cfg.GroupDN}}" placeholder="e.g. ou=group,dc=mydomain,dc=com"> </div> <div class="field"> <label for="group_dn">{{.i18n.Tr "admin.auths.group_search_base"}}</label> <input id="group_dn" name="group_dn" value="{{$cfg.GroupDN}}" placeholder="e.g. ou=group,dc=mydomain,dc=com"> <label>{{.i18n.Tr "admin.auths.group_attribute_list_users"}}</label> <input name="group_member_uid" value="{{$cfg.GroupMemberUID}}" placeholder="e.g. memberUid"> </div> <div class="field"> <label for="group_filter">{{.i18n.Tr "admin.auths.valid_groups_filter"}}</label> <input id="group_filter" name="group_filter" value="{{$cfg.GroupFilter}}" placeholder="e.g. (|(cn=gitea_users)(cn=admins))"> <label>{{.i18n.Tr "admin.auths.user_attribute_in_group"}}</label> <input name="user_uid" value="{{$cfg.UserUID}}" placeholder="e.g. uid"> </div> <div class="field"> <label for="group_member_uid">{{.i18n.Tr "admin.auths.group_attribute_list_users"}}</label> <input id="group_member_uid" name="group_member_uid" value="{{$cfg.GroupMemberUID}}" placeholder="e.g. memberUid"> <label>{{.i18n.Tr "admin.auths.verify_group_membership"}}</label> <input name="group_filter" value="{{$cfg.GroupFilter}}" placeholder="e.g. (|(cn=gitea_users)(cn=admins))"> </div> <div class="field"> <label for="user_uid">{{.i18n.Tr "admin.auths.user_attribute_in_group"}}</label> <input id="user_uid" name="user_uid" value="{{$cfg.UserUID}}" placeholder="e.g. uid"> <label>{{.i18n.Tr "admin.auths.map_group_to_team"}}</label> <input name="group_team_map" value="{{$cfg.GroupTeamMap}}" placeholder='e.g. {"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}'> </div> <div class="ui checkbox"> <label>{{.i18n.Tr "admin.auths.map_group_to_team_removal"}}</label> <input name="group_team_map_removal" type="checkbox" {{if $cfg.GroupTeamMapRemoval}}checked{{end}}> </div> <br/> </div> <!-- ldap group end --> {{if .Source.IsLDAP}} <div class="inline field"> <div class="ui checkbox">
-
-
-
@@ -79,31 +79,42 @@<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> <!-- ldap group begin --> <div class="inline field"> <div class="ui checkbox"> <label for="groups_enabled"><strong>{{.i18n.Tr "admin.auths.verify_group_membership"}}</strong></label> <input id="groups_enabled" name="groups_enabled" type="checkbox" {{if .groups_enabled}}checked{{end}}> <label><strong>{{.i18n.Tr "admin.auths.enable_ldap_groups"}}</strong></label> <input type="checkbox" name="groups_enabled" class="js-ldap-group-toggle" {{if .groups_enabled}}checked{{end}}> </div> </div> <div id="groups_enabled_change"> <div id="ldap-group-options" class="ui segment secondary"> <div class="field"> <label for="group_dn">{{.i18n.Tr "admin.auths.group_search_base"}}</label> <input id="group_dn" name="group_dn" value="{{.group_dn}}" placeholder="e.g. ou=group,dc=mydomain,dc=com"> <label>{{.i18n.Tr "admin.auths.group_search_base"}}</label> <input name="group_dn" value="{{.group_dn}}" placeholder="e.g. ou=group,dc=mydomain,dc=com"> </div> <div class="field"> <label for="group_filter">{{.i18n.Tr "admin.auths.valid_groups_filter"}}</label> <input id="group_filter" name="group_filter" value="{{.group_filter}}" placeholder="e.g. (|(cn=gitea_users)(cn=admins))"> <label>{{.i18n.Tr "admin.auths.group_attribute_list_users"}}</label> <input name="group_member_uid" value="{{.group_member_uid}}" placeholder="e.g. memberUid"> </div> <div class="field"> <label for="group_member_uid">{{.i18n.Tr "admin.auths.group_attribute_list_users"}}</label> <input id="group_member_uid" name="group_member_uid" value="{{.group_member_uid}}" placeholder="e.g. memberUid"> <label>{{.i18n.Tr "admin.auths.user_attribute_in_group"}}</label> <input name="user_uid" value="{{.user_uid}}" placeholder="e.g. uid"> </div> <div class="field"> <label for="user_uid">{{.i18n.Tr "admin.auths.user_attribute_in_group"}}</label> <input id="user_uid" name="user_uid" value="{{.user_uid}}" placeholder="e.g. uid"> <label>{{.i18n.Tr "admin.auths.verify_group_membership"}}</label> <input name="group_filter" value="{{.group_filter}}" placeholder="e.g. (|(cn=gitea_users)(cn=admins))"> </div> <div class="field"> <label>{{.i18n.Tr "admin.auths.map_group_to_team"}}</label> <input name="group_team_map" value="{{.group_team_map}}" placeholder='e.g. {"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}'> </div> <div class="ui checkbox"> <label>{{.i18n.Tr "admin.auths.map_group_to_team_removal"}}</label> <input name="group_team_map_removal" type="checkbox" {{if .group_team_map_removal}}checked{{end}}> </div> <br/> </div> <!-- ldap group end --> <div class="ldap inline field {{if not (eq .type 2)}}hide{{end}}"> <div class="ui checkbox"> <label for="use_paged_search"><strong>{{.i18n.Tr "admin.auths.use_paged_search"}}</strong></label>
-
-
-
@@ -91,12 +91,8 @@ export function initAdminCommon() {} } function onVerifyGroupMembershipChange() { if ($('#groups_enabled').is(':checked')) { $('#groups_enabled_change').show(); } else { $('#groups_enabled_change').hide(); } function onEnableLdapGroupsChange() { $('#ldap-group-options').toggle($('.js-ldap-group-toggle').is(':checked')); } // New authentication
-
@@ -139,7 +135,7 @@ export function initAdminCommon() {} if (authType === '2' || authType === '5') { onSecurityProtocolChange(); onVerifyGroupMembershipChange(); onEnableLdapGroupsChange(); } if (authType === '2') { onUsePagedSearchChange();
-
@@ -150,15 +146,15 @@ export function initAdminCommon() {$('#use_paged_search').on('change', onUsePagedSearchChange); $('#oauth2_provider').on('change', () => onOAuth2Change(true)); $('#oauth2_use_custom_url').on('change', () => onOAuth2UseCustomURLChange(true)); $('#groups_enabled').on('change', onVerifyGroupMembershipChange); $('.js-ldap-group-toggle').on('change', onEnableLdapGroupsChange); } // Edit authentication if ($('.admin.edit.authentication').length > 0) { const authType = $('#auth_type').val(); if (authType === '2' || authType === '5') { $('#security_protocol').on('change', onSecurityProtocolChange); $('#groups_enabled').on('change', onVerifyGroupMembershipChange); onVerifyGroupMembershipChange(); $('.js-ldap-group-toggle').on('change', onEnableLdapGroupsChange); onEnableLdapGroupsChange(); if (authType === '2') { $('#use_paged_search').on('change', onUsePagedSearchChange); }
-