Skip to content

Commit bb30fde

Browse files
ghsolomonlbwexler
andauthored
Remove default use of LDAP_MATCHING_RULE_IN_CHAIN (#441)
* Remove default use of LDAP_MATCHING_RULE_IN_CHAIN with option to enable * Handle duplicate entries in list returned by lookupGroupMembersRecursive * Improve efficiency --------- Co-authored-by: lbwexler <[email protected]>
1 parent 4cf8538 commit bb30fde

File tree

3 files changed

+38
-11
lines changed

3 files changed

+38
-11
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22

33
## 29.0-SNAPSHOT - unreleased
44

5+
### 💥 Breaking Changes (upgrade difficulty: 🟢 LOW - LDAP search behavior change)
6+
7+
* `LdapService` no longer uses `LDAP_MATCHING_RULE_IN_CHAIN` by default. See change to
8+
`xhLdapConfig` if you need to revert to the previous behavior (not expected in most cases).
9+
510
### 🎁 New Features
611

712
* Added new endpoints to support searching the contents of `JSONBlob` entries, JSON-based user
813
preferences, and JSON-based app configs.
14+
* Added `xhLdapConfig.useMatchingRuleInChain` flag to enable use of `LDAP_MATCHING_RULE_IN_CHAIN`.
915

1016
### ⚙️ Technical
1117

grails-app/init/io/xh/hoist/BootStrap.groovy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ class BootStrap implements LogSupport {
233233
enabled: false,
234234
timeoutMs: 60000,
235235
cacheExpireSecs: 300,
236+
useMatchingRuleInChain: false,
236237
servers: [
237238
[
238239
host: '',

grails-app/services/io/xh/hoist/ldap/LdapService.groovy

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,21 @@ import static io.xh.hoist.util.DateTimeUtils.SECONDS
2121
* - timeoutMs - time to wait for any individual search to resolve.
2222
* - cacheExpireSecs - length of time to cache results. Set to -1 to disable caching.
2323
* - skipTlsCertVerification - true to accept untrusted certificates when binding
24-
* - servers - list of servers to be queried, each containing:
25-
* - host
26-
* - baseUserDn
27-
* - baseGroupDn
24+
* - useMatchingRuleInChain - true to use Microsoft Active Directory's proprietary
25+
* "LDAP_MATCHING_RULE_IN_CHAIN" rule (the magic `1.2.840.113556.1.4.1941` string below).
26+
* This can be a more efficient way to resolve users in nested groups but should be used
27+
* with caution, as it can trigger a large database walk, which has a significant
28+
* performance impact that is unnecessary when queries are not expected to return deeply
29+
* nested groups.
30+
* - servers - list of servers to be queried, each containing:
31+
* - host
32+
* - baseUserDn
33+
* - baseGroupDn
2834
* - 'xhLdapUsername' - dn of query user.
2935
* - 'xhLdapPassword' - password for user
3036
*
3137
* This service will cache results, per server, for the configured interval.
3238
* This service may return partial results if any particular server fails to return results.
33-
*
34-
* Note that the implementation of `lookupGroupMembers()` is currently specific to Microsoft Active
35-
* Directory, due to the use of the proprietary "LDAP_MATCHING_RULE_IN_CHAIN" rule OID (the magic
36-
* `1.2.840.113556.1.4.1941` string below). This is an efficient way to resolve users in nested
37-
* groups, but would require an alternate implementation if this service is required to work with
38-
* more generic LDAP deployments.
3939
*/
4040
class LdapService extends BaseService {
4141

@@ -157,7 +157,27 @@ class LdapService extends BaseService {
157157

158158
private List<LdapPerson> lookupGroupMembersInternal(String dn, boolean strictMode) {
159159
// See class-level comment regarding this AD-specific query
160-
searchMany("(|(memberOf=$dn) (memberOf:1.2.840.113556.1.4.1941:=$dn))", LdapPerson, strictMode)
160+
config.useMatchingRuleInChain ?
161+
searchMany("(|(memberOf=$dn) (memberOf:1.2.840.113556.1.4.1941:=$dn))", LdapPerson, strictMode) :
162+
lookupMembersRecursive(dn, strictMode).values().asList()
163+
}
164+
165+
private Map<String, LdapPerson> lookupMembersRecursive(
166+
String dn,
167+
boolean strictMode,
168+
Map<String, LdapPerson> members = new HashMap(),
169+
Set<String> visited = new HashSet<String>()
170+
) {
171+
if (visited.add(dn)) {
172+
// Add direct users
173+
searchMany("(memberOf=$dn)", LdapPerson, strictMode)
174+
.each { members[it.distinguishedname] = it}
175+
176+
// Recursively add nested groups
177+
searchMany("(memberOf=$dn)", LdapGroup, strictMode)
178+
.each {lookupMembersRecursive(it.distinguishedname, strictMode, members, visited)}
179+
}
180+
return members
161181
}
162182

163183
private <T extends LdapObject> List<T> doQuery(Map server, String baseFilter, Class<T> objType, boolean strictMode) {

0 commit comments

Comments
 (0)