* Add setting for a JSON that maps LDAP groups to Org Teams. * Add log when removing or adding team members. * Sync is being run on login and periodically. * Existing group filter settings are reused. * Adding and removing team members. * Sync not existing LDAP group. * Login with broken group map JSON.
This commit is contained in:
parent
26718a785a
commit
832ce406ae
14 changed files with 423 additions and 65 deletions
|
@ -260,7 +260,6 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
|
||||||
if c.IsSet("skip-local-2fa") {
|
if c.IsSet("skip-local-2fa") {
|
||||||
config.SkipLocalTwoFA = c.Bool("skip-local-2fa")
|
config.SkipLocalTwoFA = c.Bool("skip-local-2fa")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,9 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"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"
|
"code.gitea.io/gitea/services/auth"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -97,7 +100,13 @@ func getLDAPServerHost() string {
|
||||||
return host
|
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")
|
session := loginUser(t, "user1")
|
||||||
csrf := GetCSRF(t, session, "/admin/auths/new")
|
csrf := GetCSRF(t, session, "/admin/auths/new")
|
||||||
req := NewRequestWithValues(t, "POST", "/admin/auths/new", map[string]string{
|
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,
|
"attribute_ssh_public_key": sshKeyAttribute,
|
||||||
"is_sync_enabled": "on",
|
"is_sync_enabled": "on",
|
||||||
"is_active": "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)
|
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)
|
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 Filter
|
||||||
auths.admin_filter = Admin Filter
|
auths.admin_filter = Admin Filter
|
||||||
auths.restricted_filter = Restricted 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.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.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.group_attribute_list_users = Group Attribute Containing List Of Users
|
||||||
auths.user_attribute_in_group = User Attribute Listed In Group
|
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.ms_ad_sa = MS AD Search Attributes
|
||||||
auths.smtp_auth = SMTP Authentication Type
|
auths.smtp_auth = SMTP Authentication Type
|
||||||
auths.smtphost = SMTP Host
|
auths.smtphost = SMTP Host
|
||||||
|
|
|
@ -145,6 +145,8 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source {
|
||||||
GroupDN: form.GroupDN,
|
GroupDN: form.GroupDN,
|
||||||
GroupFilter: form.GroupFilter,
|
GroupFilter: form.GroupFilter,
|
||||||
GroupMemberUID: form.GroupMemberUID,
|
GroupMemberUID: form.GroupMemberUID,
|
||||||
|
GroupTeamMap: form.GroupTeamMap,
|
||||||
|
GroupTeamMapRemoval: form.GroupTeamMapRemoval,
|
||||||
UserUID: form.UserUID,
|
UserUID: form.UserUID,
|
||||||
AdminFilter: form.AdminFilter,
|
AdminFilter: form.AdminFilter,
|
||||||
RestrictedFilter: form.RestrictedFilter,
|
RestrictedFilter: form.RestrictedFilter,
|
||||||
|
|
|
@ -120,3 +120,11 @@ share the following fields:
|
||||||
* Group Attribute for User (optional)
|
* Group Attribute for User (optional)
|
||||||
* Which group LDAP attribute contains an array above user attribute names.
|
* Which group LDAP attribute contains an array above user attribute names.
|
||||||
* Example: memberUid
|
* 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
|
GroupDN string // Group Search Base
|
||||||
GroupFilter string // Group Name Filter
|
GroupFilter string // Group Name Filter
|
||||||
GroupMemberUID string // Group Attribute containing array of UserUID
|
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
|
UserUID string // User Attribute listed in Group
|
||||||
SkipLocalTwoFA bool `json:",omitempty"` // Skip Local 2fa for users authenticated with this source
|
SkipLocalTwoFA bool `json:",omitempty"` // Skip Local 2fa for users authenticated with this source
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||||
"code.gitea.io/gitea/models/auth"
|
"code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/db"
|
"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 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) {
|
if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(user, source.authSource, sr.SSHPublicKey) {
|
||||||
return user, asymkey_model.RewriteAllPublicKeys()
|
return user, asymkey_model.RewriteAllPublicKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,10 +103,14 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str
|
||||||
if isAttributeSSHPublicKeySet && asymkey_model.AddPublicKeysBySource(user, source.authSource, sr.SSHPublicKey) {
|
if isAttributeSSHPublicKeySet && asymkey_model.AddPublicKeysBySource(user, source.authSource, sr.SSHPublicKey) {
|
||||||
err = asymkey_model.RewriteAllPublicKeys()
|
err = asymkey_model.RewriteAllPublicKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && len(source.AttributeAvatar) > 0 {
|
if err == nil && len(source.AttributeAvatar) > 0 {
|
||||||
_ = user_service.UploadAvatar(user, sr.Avatar)
|
_ = 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
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
100
services/auth/source/ldap/source_group_sync.go
Normal file
100
services/auth/source/ldap/source_group_sync.go
Normal file
|
@ -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,7 +12,9 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/go-ldap/ldap/v3"
|
"github.com/go-ldap/ldap/v3"
|
||||||
)
|
)
|
||||||
|
@ -26,8 +28,10 @@ type SearchResult struct {
|
||||||
SSHPublicKey []string // SSH Public Key
|
SSHPublicKey []string // SSH Public Key
|
||||||
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
|
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) {
|
func (ls *Source) sanitizedUserQuery(username string) (string, bool) {
|
||||||
|
@ -192,6 +196,71 @@ func checkRestricted(l *ldap.Conn, ls *Source, userDN string) bool {
|
||||||
return false
|
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
|
// 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 {
|
func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResult {
|
||||||
// See https://tools.ietf.org/search/rfc4513#section-5.1.2
|
// See https://tools.ietf.org/search/rfc4513#section-5.1.2
|
||||||
|
@ -308,9 +377,12 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul
|
||||||
surname := sr.Entries[0].GetAttributeValue(ls.AttributeSurname)
|
surname := sr.Entries[0].GetAttributeValue(ls.AttributeSurname)
|
||||||
mail := sr.Entries[0].GetAttributeValue(ls.AttributeMail)
|
mail := sr.Entries[0].GetAttributeValue(ls.AttributeMail)
|
||||||
uid := sr.Entries[0].GetAttributeValue(ls.UserUID)
|
uid := sr.Entries[0].GetAttributeValue(ls.UserUID)
|
||||||
|
if ls.UserUID == "dn" || ls.UserUID == "DN" {
|
||||||
|
uid = sr.Entries[0].DN
|
||||||
|
}
|
||||||
|
|
||||||
// Check group membership
|
// Check group membership
|
||||||
if ls.GroupsEnabled {
|
if ls.GroupsEnabled && ls.GroupFilter != "" {
|
||||||
groupFilter, ok := ls.sanitizedGroupFilter(ls.GroupFilter)
|
groupFilter, ok := ls.sanitizedGroupFilter(ls.GroupFilter)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
|
@ -373,6 +445,12 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul
|
||||||
Avatar = sr.Entries[0].GetRawAttributeValue(ls.AttributeAvatar)
|
Avatar = 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{
|
return &SearchResult{
|
||||||
LowerName: strings.ToLower(username),
|
LowerName: strings.ToLower(username),
|
||||||
Username: username,
|
Username: username,
|
||||||
|
@ -383,6 +461,8 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul
|
||||||
IsAdmin: isAdmin,
|
IsAdmin: isAdmin,
|
||||||
IsRestricted: isRestricted,
|
IsRestricted: isRestricted,
|
||||||
Avatar: Avatar,
|
Avatar: Avatar,
|
||||||
|
LdapTeamAdd: teamsToAdd,
|
||||||
|
LdapTeamRemove: teamsToRemove,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,7 +497,7 @@ func (ls *Source) SearchEntries() ([]*SearchResult, error) {
|
||||||
isAttributeSSHPublicKeySet := len(strings.TrimSpace(ls.AttributeSSHPublicKey)) > 0
|
isAttributeSSHPublicKeySet := len(strings.TrimSpace(ls.AttributeSSHPublicKey)) > 0
|
||||||
isAtributeAvatarSet := len(strings.TrimSpace(ls.AttributeAvatar)) > 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 {
|
if isAttributeSSHPublicKeySet {
|
||||||
attribs = append(attribs, ls.AttributeSSHPublicKey)
|
attribs = append(attribs, ls.AttributeSSHPublicKey)
|
||||||
}
|
}
|
||||||
|
@ -444,12 +524,23 @@ func (ls *Source) SearchEntries() ([]*SearchResult, error) {
|
||||||
result := make([]*SearchResult, len(sr.Entries))
|
result := make([]*SearchResult, len(sr.Entries))
|
||||||
|
|
||||||
for i, v := range 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{
|
result[i] = &SearchResult{
|
||||||
Username: v.GetAttributeValue(ls.AttributeUsername),
|
Username: v.GetAttributeValue(ls.AttributeUsername),
|
||||||
Name: v.GetAttributeValue(ls.AttributeName),
|
Name: v.GetAttributeValue(ls.AttributeName),
|
||||||
Surname: v.GetAttributeValue(ls.AttributeSurname),
|
Surname: v.GetAttributeValue(ls.AttributeSurname),
|
||||||
Mail: v.GetAttributeValue(ls.AttributeMail),
|
Mail: v.GetAttributeValue(ls.AttributeMail),
|
||||||
IsAdmin: checkAdmin(l, ls, v.DN),
|
IsAdmin: checkAdmin(l, ls, v.DN),
|
||||||
|
LdapTeamAdd: teamsToAdd,
|
||||||
|
LdapTeamRemove: teamsToRemove,
|
||||||
}
|
}
|
||||||
if !result[i].IsAdmin {
|
if !result[i].IsAdmin {
|
||||||
result[i].IsRestricted = checkRestricted(l, ls, v.DN)
|
result[i].IsRestricted = checkRestricted(l, ls, v.DN)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
@ -61,6 +62,8 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
|
||||||
})
|
})
|
||||||
|
|
||||||
userPos := 0
|
userPos := 0
|
||||||
|
orgCache := make(map[string]*models.Organization)
|
||||||
|
teamCache := make(map[string]*models.Team)
|
||||||
|
|
||||||
for _, su := range sr {
|
for _, su := range sr {
|
||||||
select {
|
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
|
// 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
|
SSPIStripDomainNames bool
|
||||||
SSPISeparatorReplacement string `binding:"AlphaDashDot;MaxSize(5)"`
|
SSPISeparatorReplacement string `binding:"AlphaDashDot;MaxSize(5)"`
|
||||||
SSPIDefaultLanguage string
|
SSPIDefaultLanguage string
|
||||||
|
GroupTeamMap string
|
||||||
|
GroupTeamMapRemoval bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates fields
|
// Validate validates fields
|
||||||
|
|
|
@ -108,31 +108,43 @@
|
||||||
<label for="attribute_avatar">{{.i18n.Tr "admin.auths.attribute_avatar"}}</label>
|
<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">
|
<input id="attribute_avatar" name="attribute_avatar" value="{{$cfg.AttributeAvatar}}" placeholder="e.g. jpegPhoto">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ldap group begin -->
|
||||||
<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><strong>{{.i18n.Tr "admin.auths.enable_ldap_groups"}}</strong></label>
|
||||||
<input id="groups_enabled" name="groups_enabled" type="checkbox" {{if $cfg.GroupsEnabled}}checked{{end}}>
|
<input type="checkbox" name="groups_enabled" class="js-ldap-group-toggle" {{if $cfg.GroupsEnabled}}checked{{end}}>
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<div class="field">
|
||||||
<label for="group_dn">{{.i18n.Tr "admin.auths.group_search_base"}}</label>
|
<label>{{.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">
|
<input name="group_dn" value="{{$cfg.GroupDN}}" placeholder="e.g. ou=group,dc=mydomain,dc=com">
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="group_filter">{{.i18n.Tr "admin.auths.valid_groups_filter"}}</label>
|
<label>{{.i18n.Tr "admin.auths.group_attribute_list_users"}}</label>
|
||||||
<input id="group_filter" name="group_filter" value="{{$cfg.GroupFilter}}" placeholder="e.g. (|(cn=gitea_users)(cn=admins))">
|
<input name="group_member_uid" value="{{$cfg.GroupMemberUID}}" placeholder="e.g. memberUid">
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="group_member_uid">{{.i18n.Tr "admin.auths.group_attribute_list_users"}}</label>
|
<label>{{.i18n.Tr "admin.auths.user_attribute_in_group"}}</label>
|
||||||
<input id="group_member_uid" name="group_member_uid" value="{{$cfg.GroupMemberUID}}" placeholder="e.g. memberUid">
|
<input name="user_uid" value="{{$cfg.UserUID}}" placeholder="e.g. uid">
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="user_uid">{{.i18n.Tr "admin.auths.user_attribute_in_group"}}</label>
|
<label>{{.i18n.Tr "admin.auths.verify_group_membership"}}</label>
|
||||||
<input id="user_uid" name="user_uid" value="{{$cfg.UserUID}}" placeholder="e.g. uid">
|
<input name="group_filter" value="{{$cfg.GroupFilter}}" placeholder="e.g. (|(cn=gitea_users)(cn=admins))">
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<div class="field">
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<!-- ldap group end -->
|
||||||
|
|
||||||
{{if .Source.IsLDAP}}
|
{{if .Source.IsLDAP}}
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
|
|
|
@ -79,31 +79,42 @@
|
||||||
<label for="attribute_avatar">{{.i18n.Tr "admin.auths.attribute_avatar"}}</label>
|
<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">
|
<input id="attribute_avatar" name="attribute_avatar" value="{{.attribute_avatar}}" placeholder="e.g. jpegPhoto">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ldap group begin -->
|
||||||
<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><strong>{{.i18n.Tr "admin.auths.enable_ldap_groups"}}</strong></label>
|
||||||
<input id="groups_enabled" name="groups_enabled" type="checkbox" {{if .groups_enabled}}checked{{end}}>
|
<input type="checkbox" name="groups_enabled" class="js-ldap-group-toggle" {{if .groups_enabled}}checked{{end}}>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="groups_enabled_change">
|
<div id="ldap-group-options" class="ui segment secondary">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="group_dn">{{.i18n.Tr "admin.auths.group_search_base"}}</label>
|
<label>{{.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">
|
<input name="group_dn" value="{{.group_dn}}" placeholder="e.g. ou=group,dc=mydomain,dc=com">
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="group_filter">{{.i18n.Tr "admin.auths.valid_groups_filter"}}</label>
|
<label>{{.i18n.Tr "admin.auths.group_attribute_list_users"}}</label>
|
||||||
<input id="group_filter" name="group_filter" value="{{.group_filter}}" placeholder="e.g. (|(cn=gitea_users)(cn=admins))">
|
<input name="group_member_uid" value="{{.group_member_uid}}" placeholder="e.g. memberUid">
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="group_member_uid">{{.i18n.Tr "admin.auths.group_attribute_list_users"}}</label>
|
<label>{{.i18n.Tr "admin.auths.user_attribute_in_group"}}</label>
|
||||||
<input id="group_member_uid" name="group_member_uid" value="{{.group_member_uid}}" placeholder="e.g. memberUid">
|
<input name="user_uid" value="{{.user_uid}}" placeholder="e.g. uid">
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="user_uid">{{.i18n.Tr "admin.auths.user_attribute_in_group"}}</label>
|
<label>{{.i18n.Tr "admin.auths.verify_group_membership"}}</label>
|
||||||
<input id="user_uid" name="user_uid" value="{{.user_uid}}" placeholder="e.g. uid">
|
<input name="group_filter" value="{{.group_filter}}" placeholder="e.g. (|(cn=gitea_users)(cn=admins))">
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<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>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<!-- ldap group end -->
|
||||||
|
|
||||||
<div class="ldap inline field {{if not (eq .type 2)}}hide{{end}}">
|
<div class="ldap inline field {{if not (eq .type 2)}}hide{{end}}">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<label for="use_paged_search"><strong>{{.i18n.Tr "admin.auths.use_paged_search"}}</strong></label>
|
<label for="use_paged_search"><strong>{{.i18n.Tr "admin.auths.use_paged_search"}}</strong></label>
|
||||||
|
|
|
@ -91,12 +91,8 @@ export function initAdminCommon() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onVerifyGroupMembershipChange() {
|
function onEnableLdapGroupsChange() {
|
||||||
if ($('#groups_enabled').is(':checked')) {
|
$('#ldap-group-options').toggle($('.js-ldap-group-toggle').is(':checked'));
|
||||||
$('#groups_enabled_change').show();
|
|
||||||
} else {
|
|
||||||
$('#groups_enabled_change').hide();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New authentication
|
// New authentication
|
||||||
|
@ -139,7 +135,7 @@ export function initAdminCommon() {
|
||||||
}
|
}
|
||||||
if (authType === '2' || authType === '5') {
|
if (authType === '2' || authType === '5') {
|
||||||
onSecurityProtocolChange();
|
onSecurityProtocolChange();
|
||||||
onVerifyGroupMembershipChange();
|
onEnableLdapGroupsChange();
|
||||||
}
|
}
|
||||||
if (authType === '2') {
|
if (authType === '2') {
|
||||||
onUsePagedSearchChange();
|
onUsePagedSearchChange();
|
||||||
|
@ -150,15 +146,15 @@ export function initAdminCommon() {
|
||||||
$('#use_paged_search').on('change', onUsePagedSearchChange);
|
$('#use_paged_search').on('change', onUsePagedSearchChange);
|
||||||
$('#oauth2_provider').on('change', () => onOAuth2Change(true));
|
$('#oauth2_provider').on('change', () => onOAuth2Change(true));
|
||||||
$('#oauth2_use_custom_url').on('change', () => onOAuth2UseCustomURLChange(true));
|
$('#oauth2_use_custom_url').on('change', () => onOAuth2UseCustomURLChange(true));
|
||||||
$('#groups_enabled').on('change', onVerifyGroupMembershipChange);
|
$('.js-ldap-group-toggle').on('change', onEnableLdapGroupsChange);
|
||||||
}
|
}
|
||||||
// Edit authentication
|
// Edit authentication
|
||||||
if ($('.admin.edit.authentication').length > 0) {
|
if ($('.admin.edit.authentication').length > 0) {
|
||||||
const authType = $('#auth_type').val();
|
const authType = $('#auth_type').val();
|
||||||
if (authType === '2' || authType === '5') {
|
if (authType === '2' || authType === '5') {
|
||||||
$('#security_protocol').on('change', onSecurityProtocolChange);
|
$('#security_protocol').on('change', onSecurityProtocolChange);
|
||||||
$('#groups_enabled').on('change', onVerifyGroupMembershipChange);
|
$('.js-ldap-group-toggle').on('change', onEnableLdapGroupsChange);
|
||||||
onVerifyGroupMembershipChange();
|
onEnableLdapGroupsChange();
|
||||||
if (authType === '2') {
|
if (authType === '2') {
|
||||||
$('#use_paged_search').on('change', onUsePagedSearchChange);
|
$('#use_paged_search').on('change', onUsePagedSearchChange);
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue