Strategies the user's group membership lookup

This commit is contained in:
Stephen Connolly 2014-05-22 15:43:20 +01:00
parent d9b48cd661
commit a375a65b63
10 changed files with 407 additions and 21 deletions

View file

@ -25,6 +25,7 @@
package hudson.security;
import groovy.lang.Binding;
import hudson.DescriptorExtensionList;
import hudson.Extension;
import static hudson.Util.fixEmpty;
import static hudson.Util.fixEmptyAndTrim;
@ -68,6 +69,8 @@ import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import jenkins.model.Jenkins;
import jenkins.security.plugins.ldap.FromGroupSearchLDAPGroupMembershipStrategy;
import jenkins.security.plugins.ldap.LDAPGroupMembershipStrategy;
import org.acegisecurity.AcegiSecurityException;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
@ -287,8 +290,15 @@ public class LDAPSecurityRealm extends AbstractPasswordBasedSecurityRealm {
* {@code LDAPBindSecurityRealm.groovy}
*
* @since 1.5
* @deprecated use {@link #groupMembershipStrategy}
*/
public final String groupMembershipFilter;
@Deprecated
public transient String groupMembershipFilter;
/**
* @since 2.0
*/
public /*effectively final*/ LDAPGroupMembershipStrategy groupMembershipStrategy;
/*
Other configurations that are needed:
@ -402,8 +412,16 @@ public class LDAPSecurityRealm extends AbstractPasswordBasedSecurityRealm {
this(server, rootDN, userSearchBase, userSearch, groupSearchBase, groupSearchFilter, groupMembershipFilter, managerDN, Secret.fromString(managerPassword), inhibitInferRootDN, disableMailAddressResolver, cache, environmentProperties, null, null);
}
@DataBoundConstructor
/**
* @deprecated retained for backwards binary compatibility.
*/
@Deprecated
public LDAPSecurityRealm(String server, String rootDN, String userSearchBase, String userSearch, String groupSearchBase, String groupSearchFilter, String groupMembershipFilter, String managerDN, Secret managerPasswordSecret, boolean inhibitInferRootDN, boolean disableMailAddressResolver, CacheConfiguration cache, EnvironmentProperty[] environmentProperties, String displayNameAttributeName, String mailAddressAttributeName) {
this(server, rootDN, userSearchBase, userSearch, groupSearchBase, groupSearchFilter, new FromGroupSearchLDAPGroupMembershipStrategy(groupMembershipFilter), managerDN, managerPasswordSecret, inhibitInferRootDN, disableMailAddressResolver, cache, environmentProperties, displayNameAttributeName, mailAddressAttributeName);
}
@DataBoundConstructor
public LDAPSecurityRealm(String server, String rootDN, String userSearchBase, String userSearch, String groupSearchBase, String groupSearchFilter, LDAPGroupMembershipStrategy groupMembershipStrategy, String managerDN, Secret managerPasswordSecret, boolean inhibitInferRootDN, boolean disableMailAddressResolver, CacheConfiguration cache, EnvironmentProperty[] environmentProperties, String displayNameAttributeName, String mailAddressAttributeName) {
this.server = server.trim();
this.managerDN = fixEmpty(managerDN);
this.managerPasswordSecret = managerPasswordSecret;
@ -415,7 +433,7 @@ public class LDAPSecurityRealm extends AbstractPasswordBasedSecurityRealm {
this.userSearch = userSearch!=null ? userSearch : DescriptorImpl.DEFAULT_USER_SEARCH;
this.groupSearchBase = fixEmptyAndTrim(groupSearchBase);
this.groupSearchFilter = fixEmptyAndTrim(groupSearchFilter);
this.groupMembershipFilter = fixEmptyAndTrim(groupMembershipFilter);
this.groupMembershipStrategy = groupMembershipStrategy == null ? new FromGroupSearchLDAPGroupMembershipStrategy("") : groupMembershipStrategy;
this.disableMailAddressResolver = disableMailAddressResolver;
this.cache = cache;
this.extraEnvVars = environmentProperties == null || environmentProperties.length == 0
@ -432,6 +450,10 @@ public class LDAPSecurityRealm extends AbstractPasswordBasedSecurityRealm {
managerPasswordSecret = Secret.fromString(Scrambler.descramble(managerPassword));
managerPassword = null;
}
if (groupMembershipStrategy == null) {
groupMembershipStrategy = new FromGroupSearchLDAPGroupMembershipStrategy(groupMembershipFilter);
groupMembershipFilter = null;
}
return this;
}
@ -458,10 +480,15 @@ public class LDAPSecurityRealm extends AbstractPasswordBasedSecurityRealm {
return cache == null ? null : cache.getTtl();
}
@Deprecated
public String getGroupMembershipFilter() {
return groupMembershipFilter;
}
public LDAPGroupMembershipStrategy getGroupMembershipStrategy() {
return groupMembershipStrategy;
}
public String getGroupSearchFilter() {
return groupSearchFilter;
}
@ -569,14 +596,13 @@ public class LDAPSecurityRealm extends AbstractPasswordBasedSecurityRealm {
ldapTemplate = new LdapTemplate(findBean(InitialDirContextFactory.class, appContext));
if (groupMembershipFilter != null) {
AuthoritiesPopulatorImpl authoritiesPopulator = findBean(AuthoritiesPopulatorImpl.class, appContext);
authoritiesPopulator.setGroupSearchFilter(groupMembershipFilter);
if (groupMembershipStrategy != null) {
groupMembershipStrategy.setAuthoritiesPopulator(findBean(LdapAuthoritiesPopulator.class, appContext));
}
return new SecurityComponents(
new LDAPAuthenticationManager(findBean(AuthenticationManager.class, appContext)),
new LDAPUserDetailsService(appContext));
new LDAPUserDetailsService(appContext, groupMembershipStrategy));
}
/**
@ -712,6 +738,7 @@ public class LDAPSecurityRealm extends AbstractPasswordBasedSecurityRealm {
public static class LDAPUserDetailsService implements UserDetailsService {
public final LdapUserSearch ldapSearch;
public final LdapAuthoritiesPopulator authoritiesPopulator;
public final LDAPGroupMembershipStrategy groupMembershipStrategy;
/**
* {@link BasicAttributes} in LDAP tend to be bulky (about 20K at size), so interning them
* to keep the size under control. When a programmatic client is not smart enough to
@ -720,13 +747,22 @@ public class LDAPSecurityRealm extends AbstractPasswordBasedSecurityRealm {
private final LRUMap attributesCache = new LRUMap(32);
LDAPUserDetailsService(WebApplicationContext appContext) {
ldapSearch = findBean(LdapUserSearch.class, appContext);
authoritiesPopulator = findBean(LdapAuthoritiesPopulator.class, appContext);
this(appContext, null);
}
LDAPUserDetailsService(LdapUserSearch ldapSearch, LdapAuthoritiesPopulator authoritiesPopulator) {
this(ldapSearch, authoritiesPopulator, null);
}
LDAPUserDetailsService(LdapUserSearch ldapSearch, LdapAuthoritiesPopulator authoritiesPopulator, LDAPGroupMembershipStrategy groupMembershipStrategy) {
this.ldapSearch = ldapSearch;
this.authoritiesPopulator = authoritiesPopulator;
this.groupMembershipStrategy = groupMembershipStrategy;
}
public LDAPUserDetailsService(WebApplicationContext appContext,
LDAPGroupMembershipStrategy groupMembershipStrategy) {
this(findBean(LdapUserSearch.class, appContext), findBean(LdapAuthoritiesPopulator.class, appContext), groupMembershipStrategy);
}
public LdapUserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
@ -764,9 +800,15 @@ public class LDAPSecurityRealm extends AbstractPasswordBasedSecurityRealm {
}
}
GrantedAuthority[] extraAuthorities = authoritiesPopulator.getGrantedAuthorities(ldapUser);
GrantedAuthority[] extraAuthorities = groupMembershipStrategy == null
? authoritiesPopulator.getGrantedAuthorities(ldapUser)
: groupMembershipStrategy.getGrantedAuthorities(ldapUser);
for (GrantedAuthority extraAuthority : extraAuthorities) {
user.addAuthority(extraAuthority);
if (FORCE_GROUPNAME_LOWERCASE) {
user.addAuthority(new GrantedAuthorityImpl(extraAuthority.getAuthority().toLowerCase()));
} else {
user.addAuthority(extraAuthority);
}
}
ldapUser = user.createUserDetails();
}
@ -873,13 +915,7 @@ public class LDAPSecurityRealm extends AbstractPasswordBasedSecurityRealm {
Set<GrantedAuthority> names = super.getGroupMembershipRoles(userDn,username);
Set<GrantedAuthority> r = new HashSet<GrantedAuthority>(names.size()*2);
if (FORCE_GROUPNAME_LOWERCASE) {
for (GrantedAuthority ga : names) {
r.add(new GrantedAuthorityImpl(ga.getAuthority().toLowerCase()));
}
} else {
r.addAll(names);
}
r.addAll(names);
for (GrantedAuthority ga : names) {
String role = ga.getAuthority();
@ -954,6 +990,10 @@ public class LDAPSecurityRealm extends AbstractPasswordBasedSecurityRealm {
return FormValidation.error(Messages.LDAPSecurityRealm_InvalidPortNumber());
}
}
public DescriptorExtensionList<LDAPGroupMembershipStrategy, Descriptor<LDAPGroupMembershipStrategy>> getGroupMembershipStrategies() {
return Jenkins.getInstance().getDescriptorList(LDAPGroupMembershipStrategy.class);
}
}
/**

View file

@ -0,0 +1,79 @@
/*
* The MIT License
*
* Copyright (c) 2014, Stephen Connolly
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.security.plugins.ldap;
import hudson.Extension;
import hudson.security.LDAPSecurityRealm;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.providers.ldap.LdapAuthoritiesPopulator;
import org.acegisecurity.userdetails.ldap.LdapUserDetails;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;
/**
* Traditional strategy.
* @since 1.10
*/
public class FromGroupSearchLDAPGroupMembershipStrategy extends LDAPGroupMembershipStrategy {
/**
* The search filter to apply to groups. Only those groups matching this criteria will be considered as groups
* that the user belongs to.
*
* Note: we leave the default blank in case legacy users have customized their {@code LDAPBindSecurityRealm.groovy}
* as prior to 1.5 that was the only way to change the group membership filter.
*/
private final String filter;
@DataBoundConstructor
public FromGroupSearchLDAPGroupMembershipStrategy(String filter) {
this.filter = filter;
}
public String getFilter() {
return filter;
}
@Override
public void setAuthoritiesPopulator(LdapAuthoritiesPopulator authoritiesPopulator) {
if (authoritiesPopulator instanceof LDAPSecurityRealm.AuthoritiesPopulatorImpl && StringUtils.isNotBlank(filter)) {
((LDAPSecurityRealm.AuthoritiesPopulatorImpl) authoritiesPopulator).setGroupSearchFilter(filter);
}
super.setAuthoritiesPopulator(authoritiesPopulator);
}
@Override
public GrantedAuthority[] getGrantedAuthorities(LdapUserDetails ldapUser) {
return getAuthoritiesPopulator().getGrantedAuthorities(ldapUser);
}
@Extension
public static class DescriptorImpl extends LDAPGroupMembershipStrategyDescriptor {
@Override
public String getDisplayName() {
return Messages.FromGroupSearchLDAPGroupMembershipStrategy_DisplayName();
}
}
}

View file

@ -0,0 +1,97 @@
/*
* The MIT License
*
* Copyright (c) 2014, Stephen Connolly
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.security.plugins.ldap;
import hudson.Extension;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.userdetails.ldap.LdapUserDetails;
import org.kohsuke.stapler.DataBoundConstructor;
import javax.naming.InvalidNameException;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.ldap.LdapName;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* This strategy is rumoured to work for Active Directory!
* @since 1.10
*/
public class FromUserRecordLDAPGroupMembershipStrategy extends LDAPGroupMembershipStrategy {
private static final Logger LOGGER = Logger.getLogger(FromUserRecordLDAPGroupMembershipStrategy.class.getName());
private final String attributeName;
@DataBoundConstructor
public FromUserRecordLDAPGroupMembershipStrategy(String attributeName) {
this.attributeName = attributeName;
}
public String getAttributeName() {
return attributeName;
}
@Override
public GrantedAuthority[] getGrantedAuthorities(LdapUserDetails ldapUser) {
List<GrantedAuthority> result = new ArrayList<GrantedAuthority>();
Attribute attribute = ldapUser.getAttributes().get(attributeName);
if (attribute != null) {
try {
for (Object value: Collections.list(attribute.getAll())) {
String groupName = String.valueOf(value);
try {
LdapName dn = new LdapName(groupName);
groupName = String.valueOf(dn.getRdn(dn.size() - 1).getValue());
} catch (InvalidNameException e) {
LOGGER.log(Level.FINEST, "Expected a Group DN but found: {0}", groupName);
}
result.add(new GrantedAuthorityImpl(groupName));
}
} catch (NamingException e) {
LogRecord lr = new LogRecord(Level.FINE,
"Failed to retrieve member of attribute ({0}) from LDAP user details");
lr.setThrown(e);
lr.setParameters(new Object[]{attributeName});
LOGGER.log(lr);
}
}
return result.toArray(new GrantedAuthority[result.size()]);
}
@Extension
public static class DescriptorImpl extends LDAPGroupMembershipStrategyDescriptor {
@Override
public String getDisplayName() {
return Messages.FromUserRecordLDAPGroupMembershipStrategy_DisplayName();
}
}
}

View file

@ -0,0 +1,67 @@
/*
* The MIT License
*
* Copyright (c) 2014, Stephen Connolly
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.security.plugins.ldap;
import hudson.model.AbstractDescribableImpl;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.providers.ldap.LdapAuthoritiesPopulator;
import org.acegisecurity.userdetails.ldap.LdapUserDetails;
/**
* A strategy for determining the groups that a user belongs to.
*
* @since 1.10
*/
public abstract class LDAPGroupMembershipStrategy extends AbstractDescribableImpl<LDAPGroupMembershipStrategy> {
/**
* The standard group member of searcher.
*/
private transient LdapAuthoritiesPopulator authoritiesPopulator;
/**
* The {@link LdapAuthoritiesPopulator} to use if performing a traditional search.
*
* @return The {@link LdapAuthoritiesPopulator} to use if performing a traditional search.
*/
public LdapAuthoritiesPopulator getAuthoritiesPopulator() {
return authoritiesPopulator;
}
/**
* Override this method if you want to change the configuration of the {@link LdapAuthoritiesPopulator}.
*
* @param authoritiesPopulator the {@link LdapAuthoritiesPopulator} to use (and abuse).
*/
public void setAuthoritiesPopulator(LdapAuthoritiesPopulator authoritiesPopulator) {
this.authoritiesPopulator = authoritiesPopulator;
}
/**
* Returns the {@link GrantedAuthority}s that the specified user belongs to.
*
* @param ldapUser the specified user.
* @return the {@link GrantedAuthority}s that the specified user belongs to.
*/
public abstract GrantedAuthority[] getGrantedAuthorities(LdapUserDetails ldapUser);
}

View file

@ -0,0 +1,33 @@
/*
* The MIT License
*
* Copyright (c) 2014, Stephen Connolly
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.security.plugins.ldap;
import hudson.model.Descriptor;
/**
* The base {@link Descriptor} class for {@link LDAPGroupMembershipStrategy}.
* @since 1.10
*/
public abstract class LDAPGroupMembershipStrategyDescriptor extends Descriptor<LDAPGroupMembershipStrategy> {
}

View file

@ -46,8 +46,8 @@ THE SOFTWARE.
<f:entry field="groupSearchFilter" title="${%Group search filter}">
<f:textbox/>
</f:entry>
<f:entry field="groupMembershipFilter" title="${%Group membership filter}">
<f:textbox/>
<f:entry field="groupMembershipStrategy" title="${%Group membership}">
<f:hetero-radio field="groupMembershipStrategy" descriptors="${descriptor.groupMembershipStrategies}"/>
</f:entry>
<f:entry field="managerDN" title="${%Manager DN}">
<f:textbox autocomplete="off"/>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright 2014 Stephen Connolly.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<f:entry field="filter" title="${%Group membership filter}">
<f:textbox/>
</f:entry>
</j:jelly>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright 2014 Stephen Connolly.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<f:entry field="attribute" title="${%Group membership attribute}">
<f:textbox default="memberOf"/>
</f:entry>
</j:jelly>

View file

@ -0,0 +1,2 @@
FromUserRecordLDAPGroupMembershipStrategy.DisplayName=Parse user attribute for list of groups
FromGroupSearchLDAPGroupMembershipStrategy.DisplayName=Search for groups containing user

View file

@ -25,7 +25,11 @@
package hudson.security;
import java.util.Collections;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import jenkins.security.plugins.ldap.FromGroupSearchLDAPGroupMembershipStrategy;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
@ -50,7 +54,9 @@ public class LDAPSecurityRealm_Test { // different name so as not to clash with
assertEquals("uS", sr.userSearch);
assertEquals("gSB", sr.groupSearchBase);
assertEquals("gSF", sr.groupSearchFilter);
assertEquals("gMF", sr.groupMembershipFilter);
assertThat(sr.getGroupMembershipStrategy(), instanceOf(FromGroupSearchLDAPGroupMembershipStrategy.class));
assertThat(((FromGroupSearchLDAPGroupMembershipStrategy)sr.getGroupMembershipStrategy()).getFilter(), is("gMF"));
assertNull(sr.groupMembershipFilter);
assertEquals("mDN", sr.managerDN);
assertEquals("s3cr3t", sr.getManagerPassword());
assertTrue(sr.inhibitInferRootDN);