Hi,
I've found out about subsonic only a few weeks ago and I really love it.
I also wanted to use Active Directory (AD) for authentication and authorization. After playing around with the source a bit I found out that using AD for authentication is no problem. Authorization would also work based on group memberships but unfortunately subsonic does not use the GrantedAuthority instances available in acegi for all decisions that involve roles. Instead, the information is taken from the net.sourceforge.subsonic.domain.User which is populated from the role information in the database.
So supporting LDAP authorization would require some more code changes. Authentication on the other hand is pretty easy to do with the following changes to applicationContext-security.xml:
- Code: Select all
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<!-- add the following line for LDAP authentication -->
<ref local="activeDirectoryAuthenticationProvider"/>
<!-- comment out the following line to disable database authentication -->
<!-- <ref local="daoAuthenticationProvider"/> -->
<bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
<property name="key" value="subsonic"/>
</bean>
<bean class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
<property name="key" value="subsonic"/>
</bean>
</list>
</property>
</bean>
Add the following bean definitions:
- Code: Select all
<bean id="initialDirContextFactory"
class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
<constructor-arg
value="ldap://domain.com.hostname:389/cn=Users,dc=domain,dc=com" />
<property name="managerDn">
<value>
<!-- the DN of a user that is able to read data from LDAP -->
<![CDATA[CN=LDAP,CN=Users,DC=domain,DC=com]]>
</value>
</property>
<property name="managerPassword">
<!-- the password of the DN set above -->
<value>secretpassword</value>
</property>
<property name="extraEnvVars">
<map>
<entry>
<key>
<value>java.naming.referral</value>
</key>
<value>follow</value>
</entry>
</map>
</property>
</bean>
<bean id="activeDirectoryAuthenticationProvider"
class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
<constructor-arg>
<ref local="bindAuthenticator" />
</constructor-arg>
<constructor-arg>
<ref local="userDetailsServiceBasedAuthoritiesPopulator" />
</constructor-arg>
</bean>
<bean id="bindAuthenticator"
class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg>
<ref local="initialDirContextFactory" />
</constructor-arg>
<property name="userSearch">
<ref local="userSearch" />
</property>
</bean>
<bean id="userDetailsServiceBasedAuthoritiesPopulator"
class="net.sourceforge.subsonic.ldap.UserDetailsServiceBasedAuthoritiesPopulator">
<property name="userDetailsService" ref="securityService"/>
</bean>
<bean id="userSearch"
class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg>
<value></value>
</constructor-arg>
<constructor-arg>
<value>(sAMAccountName={0})</value>
</constructor-arg>
<constructor-arg>
<ref local="initialDirContextFactory" />
</constructor-arg>
<property name="searchSubtree">
<value>true</value>
</property>
<property name="derefLinkFlag">
<value>true</value>
</property>
</bean>
In addition to these configuration settings a new class needs to be added to the WEB-INF classes directory compiled from this source:
- Code: Select all
package net.sourceforge.subsonic.ldap;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.ldap.LdapDataAccessException;
import org.acegisecurity.providers.ldap.LdapAuthoritiesPopulator;
import org.acegisecurity.userdetails.UserDetailsService;
import org.acegisecurity.userdetails.ldap.LdapUserDetails;
/**
* An {@link LdapAuthoritiesPopulator} that retrieves the roles from the
* database using the {@link UserDetailsService} instead of retrieving the roles
* from LDAP. An instance of this class can be configured for the
* {@link org.acegisecurity.providers.ldap.LdapAuthenticationProvider} when
* authentication should be done using LDAP and authorization using the
* information stored in the database.
*
* @author Thomas M. Hofmann
*/
public class UserDetailsServiceBasedAuthoritiesPopulator implements
LdapAuthoritiesPopulator {
private UserDetailsService userDetailsService;
public GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails)
throws LdapDataAccessException {
return userDetailsService.loadUserByUsername(userDetails.getUsername())
.getAuthorities();
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
}
When you make these changes you will need to make sure that the user is added both in AD and in subsonic. The roles / user rights are stilled defined in subsonic only. Only the password / credentials are taken from LDAP so that your users do not need to change there passwords at several places / applications.
Thomas