diff --git a/conf/config.yaml b/conf/config.yaml index 91162b7..542b802 100644 --- a/conf/config.yaml +++ b/conf/config.yaml @@ -81,4 +81,4 @@ ldap: last-name: "sn" sync: false # If true, the user will be synchronized to the database when the user logs in. If false, the user will be synchronized to the database when the user be created. admin-group: "cn=admin,dc=example,dc=com" # The group name of the admin group, if the user is in this group, the user will be an admin. - + allow-group: "cn=users,dc=example,dc=com" # The group name of the users group, if the user is in this group, the user will be an login. diff --git a/config/ldap.go b/config/ldap.go index f1ead74..b32ae63 100644 --- a/config/ldap.go +++ b/config/ldap.go @@ -11,6 +11,7 @@ type LdapUser struct { LastName string `mapstructure:"last-name"` Sync bool `mapstructure:"sync"` // Will sync the user's information to the internal database AdminGroup string `mapstructure:"admin-group"` // Which group is the admin group + AllowGroup string `mapstructure:"allow-group"` // Which group is allowed to login } // type LdapGroup struct { diff --git a/service/ldap.go b/service/ldap.go index f9c54f6..98f50a7 100644 --- a/service/ldap.go +++ b/service/ldap.go @@ -137,6 +137,17 @@ func (ls *LdapService) Authenticate(username, password string) (*model.User, err return nil, ErrLdapUserDisabled } cfg := &Config.Ldap + + // Skip allow-group check for admins + isAdmin := ls.isUserAdmin(cfg, ldapUser) + + // non-admins only check if allow-group is configured + if !isAdmin && cfg.User.AllowGroup != "" { + if !ls.isUserInGroup(cfg, ldapUser, cfg.User.AllowGroup) { + return nil, errors.New("user not in allowed group") + } + } + err = ls.verifyCredentials(cfg, ldapUser.Dn, password) if err != nil { return nil, err @@ -148,6 +159,46 @@ func (ls *LdapService) Authenticate(username, password string) (*model.User, err return user, nil } +// isUserInGroup checks if the user is a member of the specified group. by_sw +func (ls *LdapService) isUserInGroup(cfg *config.Ldap, ldapUser *LdapUser, groupDN string) bool { + // Check "memberOf" directly + if len(ldapUser.MemberOf) > 0 { + for _, group := range ldapUser.MemberOf { + if strings.EqualFold(group, groupDN) { + return true + } + } + } + + // For "member" attribute, perform a reverse search on the group + member := "member" + userDN := ldap.EscapeFilter(ldapUser.Dn) + groupDN = ldap.EscapeFilter(groupDN) + groupFilter := fmt.Sprintf("(%s=%s)", member, userDN) + + // Create the LDAP search request + groupSearchRequest := ldap.NewSearchRequest( + groupDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, // Unlimited search results + 0, // No time limit + false, // Return both attributes and DN + groupFilter, + []string{"dn"}, + nil, + ) + + // Perform the group search + groupResult, err := ls.searchResult(cfg, groupSearchRequest) + if err != nil { + return false + } + + // If any results are returned, the user is part of the group + return len(groupResult.Entries) > 0 +} + // mapToLocalUser checks whether the user exists locally; if not, creates one. // If the user exists and Ldap.Sync is enabled, it updates local info. func (ls *LdapService) mapToLocalUser(cfg *config.Ldap, lu *LdapUser) (*model.User, error) {