Skip to content

Commit

Permalink
Add restricted user filter to LDAP authentication (go-gitea#10600)
Browse files Browse the repository at this point in the history
* Add restricted user filter to LDAP authentification

* Fix unit test cases
  • Loading branch information
lafriks authored Mar 5, 2020
1 parent be544e8 commit 37c3db7
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 52 deletions.
7 changes: 7 additions & 0 deletions cmd/admin_auth_ldap.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ var (
Name: "admin-filter",
Usage: "An LDAP filter specifying if a user should be given administrator privileges.",
},
cli.StringFlag{
Name: "restricted-filter",
Usage: "An LDAP filter specifying if a user should be given restricted status.",
},
cli.BoolFlag{
Name: "allow-deactivate-all",
Usage: "Allow empty search results to deactivate all users.",
Expand Down Expand Up @@ -235,6 +239,9 @@ func parseLdapConfig(c *cli.Context, config *models.LDAPConfig) error {
if c.IsSet("admin-filter") {
config.Source.AdminFilter = c.String("admin-filter")
}
if c.IsSet("restricted-filter") {
config.Source.RestrictedFilter = c.String("restricted-filter")
}
if c.IsSet("allow-deactivate-all") {
config.Source.AllowDeactivateAll = c.Bool("allow-deactivate-all")
}
Expand Down
8 changes: 8 additions & 0 deletions cmd/admin_auth_ldap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func TestAddLdapBindDn(t *testing.T) {
"--user-search-base", "ou=Users,dc=full-domain-bind,dc=org",
"--user-filter", "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)",
"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
"--restricted-filter", "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)",
"--username-attribute", "uid-bind full",
"--firstname-attribute", "givenName-bind full",
"--surname-attribute", "sn-bind full",
Expand Down Expand Up @@ -74,6 +75,7 @@ func TestAddLdapBindDn(t *testing.T) {
SearchPageSize: 99,
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)",
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)",
Enabled: true,
},
},
Expand Down Expand Up @@ -265,6 +267,7 @@ func TestAddLdapSimpleAuth(t *testing.T) {
"--user-search-base", "ou=Users,dc=full-domain-simple,dc=org",
"--user-filter", "(&(objectClass=posixAccount)(full-simple-cn=%s))",
"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)",
"--restricted-filter", "(memberOf=cn=restricted-group,ou=example,dc=full-domain-simple,dc=org)",
"--username-attribute", "uid-simple full",
"--firstname-attribute", "givenName-simple full",
"--surname-attribute", "sn-simple full",
Expand Down Expand Up @@ -292,6 +295,7 @@ func TestAddLdapSimpleAuth(t *testing.T) {
AttributeSSHPublicKey: "publickey-simple full",
Filter: "(&(objectClass=posixAccount)(full-simple-cn=%s))",
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)",
Enabled: true,
},
},
Expand Down Expand Up @@ -499,6 +503,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
"--user-search-base", "ou=Users,dc=full-domain-bind,dc=org",
"--user-filter", "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)",
"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
"--restricted-filter", "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)",
"--username-attribute", "uid-bind full",
"--firstname-attribute", "givenName-bind full",
"--surname-attribute", "sn-bind full",
Expand Down Expand Up @@ -543,6 +548,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
SearchPageSize: 99,
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)",
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)",
Enabled: true,
},
},
Expand Down Expand Up @@ -978,6 +984,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
"--user-search-base", "ou=Users,dc=full-domain-simple,dc=org",
"--user-filter", "(&(objectClass=posixAccount)(full-simple-cn=%s))",
"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)",
"--restricted-filter", "(memberOf=cn=restricted-group,ou=example,dc=full-domain-simple,dc=org)",
"--username-attribute", "uid-simple full",
"--firstname-attribute", "givenName-simple full",
"--surname-attribute", "sn-simple full",
Expand Down Expand Up @@ -1006,6 +1013,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
AttributeSSHPublicKey: "publickey-simple full",
Filter: "(&(objectClass=posixAccount)(full-simple-cn=%s))",
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)",
},
},
},
Expand Down
6 changes: 5 additions & 1 deletion docs/content/doc/usage/command-line.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ Admin operations:
- `--user-search-base value`: The LDAP base at which user accounts will be searched for. Required.
- `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate. Required.
- `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges.
- `--restricted-filter value`: An LDAP filter specifying if a user should be given restricted status.
- `--username-attribute value`: The attribute of the user’s LDAP record containing the user name.
- `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name.
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
Expand All @@ -158,6 +159,7 @@ Admin operations:
- `--user-search-base value`: The LDAP base at which user accounts will be searched for.
- `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate.
- `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges.
- `--restricted-filter value`: An LDAP filter specifying if a user should be given restricted status.
- `--username-attribute value`: The attribute of the user’s LDAP record containing the user name.
- `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name.
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
Expand All @@ -182,6 +184,7 @@ Admin operations:
- `--user-search-base value`: The LDAP base at which user accounts will be searched for.
- `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate. Required.
- `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges.
- `--restricted-filter value`: An LDAP filter specifying if a user should be given restricted status.
- `--username-attribute value`: The attribute of the user’s LDAP record containing the user name.
- `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name.
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
Expand All @@ -202,6 +205,7 @@ Admin operations:
- `--user-search-base value`: The LDAP base at which user accounts will be searched for.
- `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate.
- `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges.
- `--restricted-filter value`: An LDAP filter specifying if a user should be given restricted status.
- `--username-attribute value`: The attribute of the user’s LDAP record containing the user name.
- `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name.
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
Expand Down Expand Up @@ -313,4 +317,4 @@ var checklist = []check{
}
```

This function will receive a command line context and return a list of details about the problems or error.
This function will receive a command line context and return a list of details about the problems or error.
30 changes: 19 additions & 11 deletions integrations/auth_ldap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ import (
)

type ldapUser struct {
UserName string
Password string
FullName string
Email string
OtherEmails []string
IsAdmin bool
SSHKeys []string
UserName string
Password string
FullName string
Email string
OtherEmails []string
IsAdmin bool
IsRestricted bool
SSHKeys []string
}

var gitLDAPUsers = []ldapUser{
Expand Down Expand Up @@ -55,10 +56,11 @@ var gitLDAPUsers = []ldapUser{
Email: "fry@planetexpress.com",
},
{
UserName: "leela",
Password: "leela",
FullName: "Leela Turanga",
Email: "leela@planetexpress.com",
UserName: "leela",
Password: "leela",
FullName: "Leela Turanga",
Email: "leela@planetexpress.com",
IsRestricted: true,
},
{
UserName: "bender",
Expand Down Expand Up @@ -109,6 +111,7 @@ func addAuthSourceLDAP(t *testing.T, sshKeyAttribute string) {
"user_base": "ou=people,dc=planetexpress,dc=com",
"filter": "(&(objectClass=inetOrgPerson)(memberOf=cn=git,ou=people,dc=planetexpress,dc=com)(uid=%s))",
"admin_filter": "(memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com)",
"restricted_filter": "(uid=leela)",
"attribute_username": "uid",
"attribute_name": "givenName",
"attribute_surname": "sn",
Expand Down Expand Up @@ -173,6 +176,11 @@ func TestLDAPUserSync(t *testing.T) {
} else {
assert.True(t, tds.Find("td:nth-child(5) i").HasClass("fa-square-o"))
}
if u.IsRestricted {
assert.True(t, tds.Find("td:nth-child(6) i").HasClass("fa-check-square-o"))
} else {
assert.True(t, tds.Find("td:nth-child(6) i").HasClass("fa-square-o"))
}
}

// Check if no users exist
Expand Down
43 changes: 27 additions & 16 deletions models/login_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,13 +475,23 @@ func LoginViaLDAP(user *User, login, password string, source *LoginSource) (*Use
return nil, err
}
}
if user != nil &&
!user.ProhibitLogin && len(source.LDAP().AdminFilter) > 0 && user.IsAdmin != sr.IsAdmin {
// Change existing admin flag only if AdminFilter option is set
user.IsAdmin = sr.IsAdmin
err = UpdateUserCols(user, "is_admin")
if err != nil {
return nil, err
if user != nil && !user.ProhibitLogin {
cols := make([]string, 0)
if len(source.LDAP().AdminFilter) > 0 && user.IsAdmin != sr.IsAdmin {
// Change existing admin flag only if AdminFilter option is set
user.IsAdmin = sr.IsAdmin
cols = append(cols, "is_admin")
}
if !user.IsAdmin && len(source.LDAP().RestrictedFilter) > 0 && user.IsRestricted != sr.IsRestricted {
// Change existing restricted flag only if RestrictedFilter option is set
user.IsRestricted = sr.IsRestricted
cols = append(cols, "is_restricted")
}
if len(cols) > 0 {
err = UpdateUserCols(user, cols...)
if err != nil {
return nil, err
}
}
}
}
Expand All @@ -504,15 +514,16 @@ func LoginViaLDAP(user *User, login, password string, source *LoginSource) (*Use
}

user = &User{
LowerName: strings.ToLower(sr.Username),
Name: sr.Username,
FullName: composeFullName(sr.Name, sr.Surname, sr.Username),
Email: sr.Mail,
LoginType: source.Type,
LoginSource: source.ID,
LoginName: login,
IsActive: true,
IsAdmin: sr.IsAdmin,
LowerName: strings.ToLower(sr.Username),
Name: sr.Username,
FullName: composeFullName(sr.Name, sr.Surname, sr.Username),
Email: sr.Mail,
LoginType: source.Type,
LoginSource: source.ID,
LoginName: login,
IsActive: true,
IsAdmin: sr.IsAdmin,
IsRestricted: sr.IsRestricted,
}

err := CreateUser(user)
Expand Down
26 changes: 16 additions & 10 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -1875,15 +1875,16 @@ func SyncExternalUsers(ctx context.Context) {
log.Trace("SyncExternalUsers[%s]: Creating user %s", s.Name, su.Username)

usr = &User{
LowerName: strings.ToLower(su.Username),
Name: su.Username,
FullName: fullName,
LoginType: s.Type,
LoginSource: s.ID,
LoginName: su.Username,
Email: su.Mail,
IsAdmin: su.IsAdmin,
IsActive: true,
LowerName: strings.ToLower(su.Username),
Name: su.Username,
FullName: fullName,
LoginType: s.Type,
LoginSource: s.ID,
LoginName: su.Username,
Email: su.Mail,
IsAdmin: su.IsAdmin,
IsRestricted: su.IsRestricted,
IsActive: true,
}

err = CreateUser(usr)
Expand All @@ -1906,6 +1907,7 @@ func SyncExternalUsers(ctx context.Context) {

// Check if user data has changed
if (len(s.LDAP().AdminFilter) > 0 && usr.IsAdmin != su.IsAdmin) ||
(len(s.LDAP().RestrictedFilter) > 0 && usr.IsRestricted != su.IsRestricted) ||
!strings.EqualFold(usr.Email, su.Mail) ||
usr.FullName != fullName ||
!usr.IsActive {
Expand All @@ -1918,9 +1920,13 @@ func SyncExternalUsers(ctx context.Context) {
if len(s.LDAP().AdminFilter) > 0 {
usr.IsAdmin = su.IsAdmin
}
// Change existing restricted flag only if RestrictedFilter option is set
if !usr.IsAdmin && len(s.LDAP().RestrictedFilter) > 0 {
usr.IsRestricted = su.IsRestricted
}
usr.IsActive = true

err = UpdateUserCols(usr, "full_name", "email", "is_admin", "is_active")
err = UpdateUserCols(usr, "full_name", "email", "is_admin", "is_restricted", "is_active")
if err != nil {
log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", s.Name, usr.Name, err)
}
Expand Down
1 change: 1 addition & 0 deletions modules/auth/auth_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type AuthenticationForm struct {
SearchPageSize int
Filter string
AdminFilter string
RestrictedFilter string
AllowDeactivateAll bool
IsActive bool
IsSyncEnabled bool
Expand Down
Loading

0 comments on commit 37c3db7

Please sign in to comment.