| This page (and all pages in the Tech: namespace) is a developer discussion about a feature that is either proposed for inclusion in JAMWiki or one that has already been implemented. This page is NOT documentation of JAMWiki functionality - for a list of documentation, see Category:JAMWiki.
Status of this feature: IMPLEMENTED. Improved Spring Security integration was included as part of the JAMWiki 0.7.0 release.
|
| Contents |
|---|
After some trial and error, I finally got a working configuration with only some restrictions (take a look at the bottom of this page...) --hp 18-Mar-2008 04:46 PDT
First of all I made an custom configuration for all Ldap-Settings:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
<beans>
<bean id="initialDirContextFactory"
class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
<constructor-arg value="ldap://${ldap_host}"/>
<property name="managerDn" value="${ldap_user}" />
<property name="managerPassword" value="${ldap_password}" />
<property name="extraEnvVars">
<map>
<entry key="java.naming.referral" value="follow" />
<entry key="java.naming.security.authentication" value="simple" />
</map>
</property>
</bean>
<bean id="userSearch" class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0" value="${ldap_base_dn}" />
<constructor-arg index="1" value="(sAMAccountName={0})" />
<constructor-arg index="2" ref="initialDirContextFactory" />
<property name="searchSubtree" value="true" />
<property name="derefLinkFlag" value="true" />
</bean>
<bean id="ldapAuthProvider" class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<property name="userSearch" ref="userSearch"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="my.JAMWikiLdapAuthoritiesPopulator">
<constructor-arg>
<ref local="initialDirContextFactory"/>
</constructor-arg>
<constructor-arg value="${ldap_group_base_dn}" />
<property name="groupRoleAttribute" value="cn" />
<property name="additionalRoles">
<list>
<bean class="org.acegisecurity.GrantedAuthorityImpl">
<constructor-arg value="ROLE_USER"/>
</bean>
<bean class="org.acegisecurity.GrantedAuthorityImpl">
<constructor-arg value="ROLE_NO_ACCOUNT"/>
</bean>
</list>
</property>
<property name="roleMap">
<map>
<entry key="exampleGroup1">
<list>
<value>ROLE_EDIT_NEW</value>
<value>ROLE_EDIT_EXISTING</value>
</list>
</entry>
<entry key="exampleAdminGroup">
<list>
<value>ROLE_ADMIN</value>
</list>
</entry>
</map>
</property>
<property name="searchSubtree" value="true" />
</bean>
</constructor-arg>
</bean>
</beans>
In applicationContext-acegi-security.xml I imported this config and made only few changes on the existing lines, to use the ldapAuthProvider:
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> <property name="providers"> <list> <ref bean="ldapAuthProvider"/> <!-- <ref local="daoAuthenticationProvider" />--> <ref local="anonymousAuthenticationProvider" /> <ref local="rememberMeAuthenticationProvider" /> </list> </property> </bean>
Of course the Implementation of my.JAMWikiLdapAuthoritiesPopulator is necessary:
package my;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.naming.directory.SearchControls;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.ldap.InitialDirContextFactory;
import org.acegisecurity.ldap.LdapDataAccessException;
import org.acegisecurity.ldap.LdapTemplate;
import org.acegisecurity.providers.ldap.LdapAuthoritiesPopulator;
import org.acegisecurity.userdetails.ldap.LdapUserDetails;
import org.jamwiki.utils.WikiLogger;
public class JAMWikiLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
private static final WikiLogger logger = WikiLogger.getLogger(JAMWikiLdapAuthoritiesPopulator.class.getName());
private final String groupSearchBase;
private String groupSearchFilter = "(member={0})";
private String groupRoleAttribute = "cn";
private LdapTemplate ldapTemplate;
private SearchControls searchControls = new SearchControls();
private String rolePrefix = "ROLE_";
private boolean convertToUpperCase = true;
private List additionalRoles = new ArrayList();
private Map roleMap;
public JAMWikiLdapAuthoritiesPopulator(
InitialDirContextFactory initialDirContextFactory,
String groupSearchBase) {
this.ldapTemplate = new LdapTemplate(initialDirContextFactory);
this.ldapTemplate.setSearchControls(searchControls);
this.groupSearchBase = groupSearchBase;
}
public GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails)
throws LdapDataAccessException {
String userDn = userDetails.getDn();
logger.finest("Getting authorities for user " + userDn);
Set roles = getGroupMembershipRoles(userDn, userDetails.getUsername());
List extraRoles = getAdditionalRoles();
if (extraRoles != null) {
roles.addAll(extraRoles);
}
return (GrantedAuthority[]) roles.toArray(new GrantedAuthority[roles.size()]);
}
protected List getMappedRolesFor(String role) {
List ret = new ArrayList();
List list = (List) roleMap.get(role);
if (list != null) {
for (Iterator it = list.iterator(); it.hasNext(); ) {
ret.add(new GrantedAuthorityImpl((String) it.next()));
}
}
return ret;
}
public Set getGroupMembershipRoles(String userDn, String username) {
Set authorities = new HashSet();
if (groupSearchBase == null) {
return authorities;
}
logger.finest("Searching for roles for user '" + username + "', DN = " + "'" + userDn + "', with filter "
+ groupSearchFilter + " in search base '" + getGroupSearchBase() + "'");
Set userRoles = ldapTemplate.searchForSingleAttributeValues(getGroupSearchBase(), groupSearchFilter,
new String[]{userDn, username}, groupRoleAttribute);
logger.fine("Roles from search: " + userRoles);
for (Iterator it = userRoles.iterator(); it.hasNext(); ) {
String role = (String) it.next();
if (convertToUpperCase) {
role = role.toUpperCase();
}
authorities.add(new GrantedAuthorityImpl(rolePrefix + role));
authorities.addAll(getMappedRolesFor(role.toUpperCase()));
}
return authorities;
}
public String getGroupSearchBase() {
return groupSearchBase;
}
public void setAdditionalRoles(List additionalRoles) {
this.additionalRoles.addAll(additionalRoles);
}
public List getAdditionalRoles() {
return additionalRoles;
}
public void setGroupSearchFilter(String groupSearchFilter) {
this.groupSearchFilter = groupSearchFilter;
}
public void setGroupRoleAttribute(String groupRoleAttribute) {
this.groupRoleAttribute = groupRoleAttribute;
}
public void setSearchSubtree(boolean searchSubtree) {
int searchScope = searchSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE;
searchControls.setSearchScope(searchScope);
}
public void setConvertToUpperCase(boolean convertToUpperCase) {
this.convertToUpperCase = convertToUpperCase;
}
public void setRolePrefix(String rolePrefix) {
this.rolePrefix = rolePrefix;
}
public void setRoleMap(Map roleMap) {
this.roleMap = new HashMap();
for (Iterator it = roleMap.entrySet().iterator(); it.hasNext(); ) {
Map.Entry entry = (Map.Entry) it.next();
String key = (String) entry.getKey();
String upperCase = key.toUpperCase();
this.roleMap.put(upperCase, entry.getValue());
}
}
}
The Implementation is quite similar to DefaultLdapAuthoritiesPopulator from Acegi with slight modifications:
* it allows a list of additionalRoles instead of a defaultRole * it allows a map<string, list<string>> for mapping ldap-groups to roles
With this configuration I was able to log-in and out again, because of Authentication.getPrincipal() is no instance of WikiUserAuth anymore (as with daoAuthenticationProvider), the Username is null.
By adding just a few lines to WikiUserAuth.initWikiUserAuth, the Username is displayed after Login:
public static WikiUserAuth initWikiUserAuth(Authentication auth) throws AuthenticationCredentialsNotFoundException {
if (auth == null) {
throw new AuthenticationCredentialsNotFoundException("No authentication credential available");
}
if (auth.getPrincipal() instanceof WikiUserAuth) {
// logged-in user
return (WikiUserAuth)auth.getPrincipal();
}
WikiUserAuth user = new WikiUserAuth();
// -- new from here
try {
user = new WikiUserAuth(auth.getName());
} catch (Exception e) {
logger.severe(e.getMessage());
user = new WikiUserAuth();
}
// ... to here
user.setAuthorities(auth.getAuthorities());
return user;
}
As part of the Spring Security 2.0 update a new filter was added to the security configuration so that authenticated users will automatically have a JAMWiki user record created for them, thus allowing integration with LDAP, CAS, OpenID, etc. In addition, Spring Security 2.0 provides vastly simplified configuration for integrating with LDAP, CAS, OpenID, etc. Basic LDAP support was tested and verified as part of the JAMWiki 0.7.0 development process. -- Ryan • (comments) • 28-Feb-2009 07:47 PST