Skip to content

Commit cfc6a17

Browse files
committed
add group name attribute for FromGroupSearchLDAPGroupMembershipStrategy
1 parent bd1a491 commit cfc6a17

File tree

11 files changed

+94
-15
lines changed

11 files changed

+94
-15
lines changed

src/main/java/hudson/security/LDAPSecurityRealm.java

+18-10
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
/*
22
* The MIT License
3-
*
3+
*
44
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe,
55
* Olivier Lamy
66
* Copyright (c) 2017 CloudBees, Inc.
7-
*
7+
*
88
* Permission is hereby granted, free of charge, to any person obtaining a copy
99
* of this software and associated documentation files (the "Software"), to deal
1010
* in the Software without restriction, including without limitation the rights
1111
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1212
* copies of the Software, and to permit persons to whom the Software is
1313
* furnished to do so, subject to the following conditions:
14-
*
14+
*
1515
* The above copyright notice and this permission notice shall be included in
1616
* all copies or substantial portions of the Software.
17-
*
17+
*
1818
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1919
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2020
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -70,6 +70,9 @@
7070
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
7171
import org.springframework.security.ldap.userdetails.LdapUserDetails;
7272
import org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;
73+
74+
import com.iwombat.util.StringUtil;
75+
7376
import org.apache.commons.collections.map.LRUMap;
7477
import org.apache.commons.io.IOUtils;
7578
import org.apache.commons.lang.StringUtils;
@@ -293,7 +296,7 @@ public class LDAPSecurityRealm extends AbstractPasswordBasedSecurityRealm {
293296
justification = "This public field is exposed to the plugin's API")
294297
@Deprecated @Restricted(NoExternalUse.class)
295298
public transient String userSearch;
296-
299+
297300
/**
298301
* This defines the organizational unit that contains groups.
299302
*
@@ -326,7 +329,7 @@ public class LDAPSecurityRealm extends AbstractPasswordBasedSecurityRealm {
326329
* @deprecated use {@link #groupMembershipStrategy}
327330
*/
328331
@Deprecated @Restricted(NoExternalUse.class)
329-
@SuppressFBWarnings(value = "UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD",
332+
@SuppressFBWarnings(value = "UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD",
330333
justification = "This public field is exposed to the plugin's API")
331334
public transient String groupMembershipFilter;
332335

@@ -363,7 +366,7 @@ group target (CN is a reasonable default)
363366
public transient String managerDN;
364367

365368
@Deprecated @Restricted(NoExternalUse.class)
366-
@SuppressFBWarnings(value = "UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD",
369+
@SuppressFBWarnings(value = "UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD",
367370
justification = "This public field is exposed to the plugin's API")
368371
private transient String managerPassword;
369372

@@ -1391,10 +1394,15 @@ public static final class AuthoritiesPopulatorImpl extends DefaultLdapAuthoritie
13911394
boolean convertToUpperCase = true;
13921395
private GrantedAuthority defaultRole = null;
13931396

1394-
public AuthoritiesPopulatorImpl(ContextSource contextSource, String groupSearchBase) {
1397+
public AuthoritiesPopulatorImpl(ContextSource contextSource, String groupSearchBase, LDAPGroupMembershipStrategy ldapGroupMembershipStrategy) {
13951398
super(contextSource, fixNull(groupSearchBase));
1396-
13971399
super.setRolePrefix("");
1400+
if (ldapGroupMembershipStrategy instanceof FromGroupSearchLDAPGroupMembershipStrategy) {
1401+
FromGroupSearchLDAPGroupMembershipStrategy fromGroupSearchLDAPGroupMembershipStrategy = (FromGroupSearchLDAPGroupMembershipStrategy) ldapGroupMembershipStrategy;
1402+
if (StringUtils.isNotBlank(fromGroupSearchLDAPGroupMembershipStrategy.getAttribute())) {
1403+
super.setGroupRoleAttribute(fromGroupSearchLDAPGroupMembershipStrategy.getAttribute());
1404+
}
1405+
}
13981406
super.setConvertToUpperCase(false);
13991407
}
14001408

@@ -1524,7 +1532,7 @@ public FormValidation doValidate(StaplerRequest req) throws Exception {
15241532
String user = json.getString("testUser");
15251533
String password = json.getString("testPassword");
15261534
JSONObject realmCfg = json.getJSONObject("securityRealm");
1527-
1535+
15281536
// instantiate the realm
15291537
LDAPSecurityRealm realm = req.bindJSON(LDAPSecurityRealm.class, realmCfg);
15301538
return validate(realm, user, password);

src/main/java/jenkins/security/plugins/ldap/FromGroupSearchLDAPGroupMembershipStrategy.java

+23-3
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,39 @@ public class FromGroupSearchLDAPGroupMembershipStrategy extends LDAPGroupMembers
5959
*/
6060
private final String filter;
6161

62-
@DataBoundConstructor
62+
/**
63+
* The LDAP attribute name which contains the group name (default = "cn").
64+
*/
65+
private final String attribute;
66+
6367
public FromGroupSearchLDAPGroupMembershipStrategy(String filter) {
6468
this.filter = filter;
69+
this.attribute = "";
70+
}
71+
72+
@DataBoundConstructor
73+
public FromGroupSearchLDAPGroupMembershipStrategy(String filter, String attribute) {
74+
this.filter = filter;
75+
this.attribute = attribute;
6576
}
6677

6778
public String getFilter() {
6879
return filter;
6980
}
7081

82+
public String getAttribute() {
83+
return attribute;
84+
}
85+
7186
@Override
7287
public void setAuthoritiesPopulator(LdapAuthoritiesPopulator authoritiesPopulator) {
73-
if (authoritiesPopulator instanceof LDAPSecurityRealm.AuthoritiesPopulatorImpl && StringUtils.isNotBlank(filter)) {
74-
((LDAPSecurityRealm.AuthoritiesPopulatorImpl) authoritiesPopulator).setGroupSearchFilter(filter);
88+
if (authoritiesPopulator instanceof LDAPSecurityRealm.AuthoritiesPopulatorImpl) {
89+
if (StringUtils.isNotBlank(filter)) {
90+
((LDAPSecurityRealm.AuthoritiesPopulatorImpl) authoritiesPopulator).setGroupSearchFilter(filter);
91+
}
92+
if (StringUtils.isNotBlank(attribute)) {
93+
((LDAPSecurityRealm.AuthoritiesPopulatorImpl) authoritiesPopulator).setGroupRoleAttribute(attribute);
94+
}
7595
}
7696
super.setAuthoritiesPopulator(authoritiesPopulator);
7797
}

src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,7 @@ public ApplicationContext createApplicationContext(LDAPSecurityRealm realm) {
623623
// this is when we need to find it.
624624
bindAuthenticator.setUserSearch(ldapUserSearch);
625625

626-
LDAPSecurityRealm.AuthoritiesPopulatorImpl ldapAuthoritiesPopulator = new LDAPSecurityRealm.AuthoritiesPopulatorImpl(contextSource, getGroupSearchBase());
626+
LDAPSecurityRealm.AuthoritiesPopulatorImpl ldapAuthoritiesPopulator = new LDAPSecurityRealm.AuthoritiesPopulatorImpl(contextSource, getGroupSearchBase(), getGroupMembershipStrategy());
627627
ldapAuthoritiesPopulator.setSearchSubtree(true);
628628
ldapAuthoritiesPopulator.setGroupSearchFilter("(| (member={0}) (uniqueMember={0}) (memberUid={1}))");
629629
if (realm.isDisableRolePrefixing()) {

src/main/resources/jenkins/security/plugins/ldap/FromGroupSearchLDAPGroupMembershipStrategy/config.jelly

+3
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,7 @@ THE SOFTWARE.
2828
<f:entry field="filter" title="${%Group membership filter}">
2929
<f:textbox/>
3030
</f:entry>
31+
<f:entry field="attribute" title="${%Group name attribute}">
32+
<f:textbox/>
33+
</f:entry>
3134
</j:jelly>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<div>
2+
The LDAP attribute name which contains the group name (default = "cn").
3+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<div>
2+
Le nom de l'attribut LDAP contenant le nom du groupe (valeur par défaut = "cn")
3+
</div>

src/main/resources/jenkins/security/plugins/ldap/LDAPConfiguration/config.jelly

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,4 @@
5151
<f:checkbox />
5252
</f:entry>
5353
</f:advanced>
54-
</j:jelly>
54+
</j:jelly>

src/test/java/hudson/security/LDAPEmbeddedTest.java

+37
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,43 @@ public void userLookup_rolesFromUserRecord_modern() throws Exception {
228228
assertThat(userGetAuthorities(details), containsInAnyOrder("HMS_Victory"));
229229
}
230230

231+
@Test
232+
@LDAPSchema(ldif = "sevenSeas", id = "sevenSeas", dn = "o=sevenSeas")
233+
public void userLookup_rolesFromGroupSearchWithGroupAttribute() throws Exception {
234+
LDAPSecurityRealm realm = new LDAPSecurityRealm(
235+
ads.getUrl(),
236+
null,
237+
null,
238+
null,
239+
null,
240+
null,
241+
new FromGroupSearchLDAPGroupMembershipStrategy(null, "description"),
242+
"uid=admin,ou=system",
243+
Secret.fromString("pass"),
244+
false,
245+
false,
246+
new LDAPSecurityRealm.CacheConfiguration(100, 1000),
247+
new LDAPSecurityRealm.EnvironmentProperty[0],
248+
"cn",
249+
null,
250+
IdStrategy.CASE_INSENSITIVE,
251+
IdStrategy.CASE_INSENSITIVE);
252+
r.jenkins.setSecurityRealm(realm);
253+
User user = User.get("hhornblo", true, Collections.emptyMap());
254+
List<String> authorities = user.getAuthorities();
255+
assertThat(user.getAuthorities(), containsInAnyOrder("HMS_Lydia", "ROLE_HMS_LYDIA"));
256+
assertThat(user.getDisplayName(), is("Horatio Hornblower"));
257+
assertThat(user.getProperty(Mailer.UserProperty.class).getAddress(), is("[email protected]"));
258+
UserDetails details = realm.authenticate2("hhornblo", "pass");
259+
assertThat(userGetAuthorities(details), containsInAnyOrder("HMS_Lydia", "ROLE_HMS_LYDIA"));
260+
user = User.get("hnelson", true, Collections.emptyMap());
261+
assertThat(user.getAuthorities(), containsInAnyOrder("HMS_Victory", "ROLE_HMS_VICTORY"));
262+
assertThat(user.getDisplayName(), is("Horatio Nelson"));
263+
assertThat(user.getProperty(Mailer.UserProperty.class).getAddress(), is("[email protected]"));
264+
details = realm.authenticate2("hnelson", "pass");
265+
assertThat(userGetAuthorities(details), containsInAnyOrder("HMS_Victory", "ROLE_HMS_VICTORY"));
266+
}
267+
231268
private Set<String> userGetAuthorities(UserDetails details) {
232269
Set<String> authorities = new LinkedHashSet<>();
233270
for (GrantedAuthority a : details.getAuthorities()) {

src/test/java/jenkins/security/plugins/ldap/CasCTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,6 @@ public void configure_ldap() {
3535
assertEquals("(&(cn={0})(objectclass=group))", configuration.getGroupSearchFilter());
3636
final FromGroupSearchLDAPGroupMembershipStrategy strategy = ((FromGroupSearchLDAPGroupMembershipStrategy) configuration.getGroupMembershipStrategy());
3737
assertEquals("(&(objectClass=group)(|(cn=GROUP_1)(cn=GROUP_2)))", strategy.getFilter());
38+
assertEquals("description", strategy.getAttribute());
3839
}
3940
}

src/test/resources/hudson/security/sevenSeas.ldif

+3
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ dn: cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas
168168
objectclass: groupOfUniqueNames
169169
objectclass: top
170170
cn: HMS Lydia
171+
description: HMS_Lydia
171172
uniquemember: cn=Horatio Hornblower,ou=people,o=sevenSeas
172173
uniquemember: cn=William Bush,ou=people,o=sevenSeas
173174
uniquemember: cn=Thomas Quist,ou=people,o=sevenSeas
@@ -236,6 +237,7 @@ dn: cn=HMS Victory,ou=crews,ou=groups,o=sevenSeas
236237
objectclass: groupOfUniqueNames
237238
objectclass: top
238239
cn: HMS Victory
240+
description: HMS_Victory
239241
uniquemember: cn=Horatio Nelson,ou=people,o=sevenSeas
240242
uniquemember: cn=Thomas Masterman Hardy,ou=people,o=sevenSeas
241243
uniquemember: cn=Cornelius Buckley,ou=people,o=sevenSeas
@@ -320,6 +322,7 @@ dn: cn=HMS Bounty,ou=crews,ou=groups,o=sevenSeas
320322
objectclass: groupOfUniqueNames
321323
objectclass: top
322324
cn: HMS Bounty
325+
description: HMS_Bounty
323326
uniquemember: cn=William Bligh,ou=people,o=sevenSeas
324327
uniquemember: cn=Fletcher Christian,ou=people,o=sevenSeas
325328
uniquemember: cn=John Fryer,ou=people,o=sevenSeas

src/test/resources/jenkins/security/plugins/ldap/casc.yml

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ jenkins:
1111
groupMembershipStrategy:
1212
fromGroupSearch:
1313
filter: "(&(objectClass=group)(|(cn=GROUP_1)(cn=GROUP_2)))"
14+
attribute: "description"
1415
cache:
1516
size: 100
1617
ttl: 10

0 commit comments

Comments
 (0)