Browse Source

feat:ldap allow-group (#388)

nomoneynolife 4 months ago
parent
commit
6e3b16d425
3 changed files with 53 additions and 1 deletions
  1. 1 1
      conf/config.yaml
  2. 1 0
      config/ldap.go
  3. 51 0
      service/ldap.go

+ 1 - 1
conf/config.yaml

@@ -81,4 +81,4 @@ ldap:
81 81
     last-name: "sn"
82 82
     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.
83 83
     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.
84
-
84
+    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. 

+ 1 - 0
config/ldap.go

@@ -11,6 +11,7 @@ type LdapUser struct {
11 11
 	LastName        string `mapstructure:"last-name"`
12 12
 	Sync            bool   `mapstructure:"sync"`        // Will sync the user's information to the internal database
13 13
 	AdminGroup      string `mapstructure:"admin-group"` // Which group is the admin group
14
+	AllowGroup      string `mapstructure:"allow-group"` // Which group is allowed to login
14 15
 }
15 16
 
16 17
 // type LdapGroup struct {

+ 51 - 0
service/ldap.go

@@ -137,6 +137,17 @@ func (ls *LdapService) Authenticate(username, password string) (*model.User, err
137 137
 		return nil, ErrLdapUserDisabled
138 138
 	}
139 139
 	cfg := &Config.Ldap
140
+
141
+	// Skip allow-group check for admins
142
+    isAdmin := ls.isUserAdmin(cfg, ldapUser)
143
+    
144
+    // non-admins only check if allow-group is configured
145
+    if !isAdmin && cfg.User.AllowGroup != "" {
146
+        if !ls.isUserInGroup(cfg, ldapUser, cfg.User.AllowGroup) {
147
+            return nil, errors.New("user not in allowed group")
148
+        }
149
+    }
150
+
140 151
 	err = ls.verifyCredentials(cfg, ldapUser.Dn, password)
141 152
 	if err != nil {
142 153
 		return nil, err
@@ -148,6 +159,46 @@ func (ls *LdapService) Authenticate(username, password string) (*model.User, err
148 159
 	return user, nil
149 160
 }
150 161
 
162
+// isUserInGroup checks if the user is a member of the specified group. by_sw
163
+func (ls *LdapService) isUserInGroup(cfg *config.Ldap, ldapUser *LdapUser, groupDN string) bool {
164
+    // Check "memberOf" directly
165
+    if len(ldapUser.MemberOf) > 0 {
166
+        for _, group := range ldapUser.MemberOf {
167
+            if strings.EqualFold(group, groupDN) {
168
+                return true
169
+            }
170
+        }
171
+    }
172
+
173
+    // For "member" attribute, perform a reverse search on the group
174
+    member := "member"
175
+    userDN := ldap.EscapeFilter(ldapUser.Dn)
176
+    groupDN = ldap.EscapeFilter(groupDN)
177
+    groupFilter := fmt.Sprintf("(%s=%s)", member, userDN)
178
+
179
+    // Create the LDAP search request
180
+    groupSearchRequest := ldap.NewSearchRequest(
181
+        groupDN,
182
+        ldap.ScopeWholeSubtree,
183
+        ldap.NeverDerefAliases,
184
+        0,     // Unlimited search results
185
+        0,     // No time limit
186
+        false, // Return both attributes and DN
187
+        groupFilter,
188
+        []string{"dn"},
189
+        nil,
190
+    )
191
+
192
+    // Perform the group search
193
+    groupResult, err := ls.searchResult(cfg, groupSearchRequest)
194
+    if err != nil {
195
+        return false
196
+    }
197
+
198
+    // If any results are returned, the user is part of the group
199
+    return len(groupResult.Entries) > 0
200
+}
201
+
151 202
 // mapToLocalUser checks whether the user exists locally; if not, creates one.
152 203
 // If the user exists and Ldap.Sync is enabled, it updates local info.
153 204
 func (ls *LdapService) mapToLocalUser(cfg *config.Ldap, lu *LdapUser) (*model.User, error) {