Index: src/main/java/hudson/security/LDAPSecurityRealm.java
===================================================================
--- src/main/java/hudson/security/LDAPSecurityRealm.java (revision 10457)
+++ src/main/java/hudson/security/LDAPSecurityRealm.java (working copy)
@@ -42,6 +42,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+
/**
* {@link SecurityRealm} implementation that uses LDAP for authentication.
*
@@ -78,6 +79,15 @@
* @see FilterBasedLdapUserSearch
*/
public final String userSearch;
+
+ /**
+ * This defines the organizational unit that contains groups.
+ *
+ * Normally "ou=groups"
+ *
+ * @see FilterBasedLdapUserSearch
+ */
+ public final String groupSearchBase;
/*
Other configurations that are needed:
@@ -106,7 +116,7 @@
private final String managerPassword;
@DataBoundConstructor
- public LDAPSecurityRealm(String server, String rootDN, String userSearchBase, String userSearch, String managerDN, String managerPassword) {
+ public LDAPSecurityRealm(String server, String rootDN, String userSearchBase, String userSearch, String groupSearchBase, String managerDN, String managerPassword) {
this.server = server.trim();
if(Util.fixEmptyAndTrim(rootDN)==null) rootDN=Util.fixNull(inferRootDN(server));
this.rootDN = rootDN.trim();
@@ -112,6 +122,8 @@
this.rootDN = rootDN.trim();
this.userSearchBase = userSearchBase.trim();
if(Util.fixEmptyAndTrim(userSearch)==null) userSearch="uid={0}";
+ if(Util.fixEmptyAndTrim(groupSearchBase)==null) groupSearchBase="ou=groups";
+ this.groupSearchBase = groupSearchBase.trim();
this.userSearch = userSearch.trim();
this.managerDN = Util.fixEmpty(managerDN);
if(Util.fixEmpty(managerPassword)==null)
@@ -165,6 +177,7 @@
BeanBuilder builder = new BeanBuilder();
builder.parse(Hudson.getInstance().servletContext.getResourceAsStream("/WEB-INF/security/LDAPBindSecurityRealm.groovy"),binding);
final WebApplicationContext appContext = builder.createApplicationContext();
+ correctAuthoritiesPopulator(appContext);
return new SecurityComponents(
findBean(AuthenticationManager.class, appContext),
@@ -182,6 +195,15 @@
}
/**
+ * Adjust the authoritiesPopulator bean to have the correct groupSearchBase
+ * @param appContext
+ */
+ private void correctAuthoritiesPopulator(WebApplicationContext appContext) {
+ DeferredCreationLdapAuthoritiesPopulator factory = (DeferredCreationLdapAuthoritiesPopulator) appContext.getBean("authoritiesPopulator");
+ factory.setGroupSearchBase(groupSearchBase);
+ }
+
+ /**
* If the security realm is LDAP, try to pick up e-mail address from LDAP.
*/
public static final class MailAdressResolverImpl extends MailAddressResolver {
Index: src/main/java/hudson/security/DeferredCreationLdapAuthoritiesPopulator.java
===================================================================
--- src/main/java/hudson/security/DeferredCreationLdapAuthoritiesPopulator.java (revision 0)
+++ src/main/java/hudson/security/DeferredCreationLdapAuthoritiesPopulator.java (revision 0)
@@ -0,0 +1,135 @@
+/**
+ *
+ */
+package hudson.security;
+
+import org.acegisecurity.GrantedAuthority;
+import org.acegisecurity.ldap.InitialDirContextFactory;
+import org.acegisecurity.ldap.LdapDataAccessException;
+import org.acegisecurity.providers.ldap.LdapAuthoritiesPopulator;
+import org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator;
+import org.acegisecurity.userdetails.ldap.LdapUserDetails;
+import org.springframework.util.Assert;
+
+/**
+ * Implementation of {@link LdapAuthoritiesPopulator} that defers creation of a
+ * {@link DefaultLdapAuthoritiesPopulator} until one is needed. This is done to
+ * ensure that the groupSearchBase property can be set.
+ *
+ * @author justinedelson
+ *
+ */
+public class DeferredCreationLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
+
+ /**
+ * A default role which will be assigned to all authenticated users if set.
+ */
+ private String defaultRole = null;
+
+ /**
+ * An initial context factory is only required if searching for groups is
+ * required.
+ */
+ private InitialDirContextFactory initialDirContextFactory = null;
+
+ /**
+ * Controls used to determine whether group searches should be performed
+ * over the full sub-tree from the base DN.
+ */
+ private boolean searchSubtree = false;
+
+ /**
+ * The ID of the attribute which contains the role name for a group
+ */
+ private String groupRoleAttribute = "cn";
+
+ /**
+ * The base DN from which the search for group membership should be
+ * performed
+ */
+ private String groupSearchBase = null;
+
+ /**
+ * The pattern to be used for the user search. {0} is the user's DN
+ */
+ private String groupSearchFilter = "(member={0})";
+
+ private String rolePrefix = "ROLE_";
+
+ private boolean convertToUpperCase = true;
+
+ /**
+ * Constructor.
+ *
+ * @param initialDirContextFactory
+ * supplies the contexts used to search for user roles.
+ * @param groupSearchBase
+ * if this is an empty string the search will be performed from
+ * the root DN of the context factory.
+ */
+ public DeferredCreationLdapAuthoritiesPopulator(
+ InitialDirContextFactory initialDirContextFactory, String groupSearchBase) {
+ this.setInitialDirContextFactory(initialDirContextFactory);
+ this.setGroupSearchBase(groupSearchBase);
+ }
+
+ public GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails)
+ throws LdapDataAccessException {
+ return create().getGrantedAuthorities(userDetails);
+ }
+
+ public void setConvertToUpperCase(boolean convertToUpperCase) {
+ this.convertToUpperCase = convertToUpperCase;
+ }
+
+ public void setDefaultRole(String defaultRole) {
+ this.defaultRole = defaultRole;
+ }
+
+ public void setGroupRoleAttribute(String groupRoleAttribute) {
+ this.groupRoleAttribute = groupRoleAttribute;
+ }
+
+ public void setGroupSearchBase(String groupSearchBase) {
+ this.groupSearchBase = groupSearchBase;
+ }
+
+ public void setGroupSearchFilter(String groupSearchFilter) {
+ this.groupSearchFilter = groupSearchFilter;
+ }
+
+ public void setInitialDirContextFactory(InitialDirContextFactory initialDirContextFactory) {
+ this.initialDirContextFactory = initialDirContextFactory;
+ }
+
+ public void setRolePrefix(String rolePrefix) {
+ this.rolePrefix = rolePrefix;
+ }
+
+ public void setSearchSubtree(boolean searchSubtree) {
+ this.searchSubtree = searchSubtree;
+ }
+
+ /**
+ * Create a new DefaultLdapAuthoritiesPopulator object.
+ *
+ * @return a DefaultLdapAuthoritiesPopulator.
+ */
+ private DefaultLdapAuthoritiesPopulator create() {
+ Assert
+ .hasText(groupSearchBase,
+ "groupSearchBase must be non-null by the time DefaultLdapAuthoritiesPopulator is instantiated.");
+ DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(
+ initialDirContextFactory, groupSearchBase);
+ populator.setConvertToUpperCase(convertToUpperCase);
+ if (defaultRole != null) {
+ populator.setDefaultRole(defaultRole);
+ }
+ populator.setGroupRoleAttribute(groupRoleAttribute);
+ populator.setGroupSearchFilter(groupSearchFilter);
+ populator.setRolePrefix(rolePrefix);
+ populator.setSearchSubtree(searchSubtree);
+ return populator;
+ }
+
+}
Index: src/main/resources/hudson/security/LDAPSecurityRealm/config.jelly
===================================================================
--- src/main/resources/hudson/security/LDAPSecurityRealm/config.jelly (revision 10457)
+++ src/main/resources/hudson/security/LDAPSecurityRealm/config.jelly (working copy)
@@ -13,6 +13,9 @@
+
+
+