Windows Security
Windows Authentication
Implementing Windows security generally involves two processes, determining who the user is using their Windows userid then using information in Active Directory to determine what roles a user has.
The first process can be implemented independently of the second, that is a user can be identified by their Windows userid but their roles can be read from a database or the user.properties file rather than from Active Directory.
The authentication of the Windows user information if performed using the SMB protocol.
Access to the role information in Active Directory is performed using the LDAP protocol.
The configuration for Windows and Active Directory authentication is done via the security.xml file as all the authentication is.
First up we'll look at how to provide the user with access to the system without having to enter a username/password via Windows integrated authentication. Then we'll look at extending this to also obtain the access levels for the users from the domain.
The latest windows authentication bundle is downloadable here
Debugging
You may want to turn on the logging of the security processing during the setting up of the authentication, since it'd disabled by default.
To do this ensure that the following two lines appear in logging.properties and any others referencing security are removed (in case they reduce the logging)
Enable security process logging
log4j.logger.org.acegisecurity=DEBUG
log4j.logger.com.cohga.server.security=DEBUG
Then when someone is logging in the log file will show the progress and what roles they were granted, which should help you to understand what role names must be used in the access control lists
Integrated Authentication
To implement Windows integrated authentication and allow internal users to login to Weave automatically using their Windows userid involves editing the security.xml file to replace the default login form with handling from the NTLM processor.
Looking at the default security.xml file it contains the following near the top:
Default request filter chain
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/server/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,jsonExceptionTranslationFilter,filterInvocationInterceptor
/**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
</value>
</property>
</bean>
What this section does is to determine which filters are applied to any incoming requests, passing everything that matched /server/** through the first list, and everything else through the second.
What we want to do to enable Windows authentication is add an additional filter to perform the NTLM authentication steps when required.
That should be the final new section we need to add, since the sections that it references should already exist. So all that remains is to add the first section we added to the list of filters:
Adding NTLM filter to request filter chain
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/server/**=httpSessionContextIntegrationFilter,ntlmProcessingFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,jsonExceptionTranslationFilter,filterInvocationInterceptor
/**=httpSessionContextIntegrationFilter,ntlmProcessingFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
</value>
</property>
</bean>
Here we've added the ntlmProcessingFilter to the list of filters that will be applied to the incoming requests.
Now we need to create the ntlmProcessingFilter filter and configure it to use the local domain.
To do this we add a new section to the security.xml file to define a new NtlmProcessingFilter
The root NTLM filter definition
<bean id="ntlmProcessingFilter" class="org.acegisecurity.ui.ntlm.NtlmProcessingFilter">
<property name="defaultDomain"><value>DOMAINNAME</value></property>
<property name="domainController"><value>172.16.0.30</value></property>
<property name="authenticationEntryPoint" ref="ntlmEntryPoint"/>
<property name="authenticationManager" ref="ntlmAuthenticationManager"/>
</bean>
The two values in there DOMAINNAME and 172.16.0.30 need to be replaced with values that are appropriate for your environment.
This is the filter that we added to the filter list, now we need to create two more sections that this filter references, the ntlmEntryPoint and the ntlmAuthenticationManager.
The ntlmEntryPoint takes care of the communication for the server to obtain the Windows userid from the browser.
The ntlmAuthenticationManager then sends that information through a list of AuthenticationProviders to validate that it's correct.
NTLM entry point for obtaining the user information from browser
<bean id="ntlmEntryPoint" class="org.acegisecurity.ui.ntlm.NtlmProcessingFilterEntryPoint"/>
NTLM authentication manager for verifying the user information from browser
<bean id="ntlmAuthenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="smbAuthenticationProvider"/>
<bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
<property name="key" value="changeThis"/>
</bean>
<bean class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
<property name="key" value="changeThis"/>
</bean>
</list>
</property>
</bean>
The ntlmEntryPoint does not require any configuration, but we can see that the ntlmAuthenticationManager references yet another item that we need to add, the smbAuthenticationManager.
The smbAuthenticationManager provides authentication via the SMB protocol of the authentication information extracted by the NtlmProcessingFilter.
The AnonymousAuthenticationProvider and RememberMeAuthenticationProvider ensure that anonymous users can connect and users that have indicated that they want to be remembered (if the login form is used) can be logged in from the cookie that was previously set. If neither of these things are applicable then it's possible to remove these 2 authentication providers and rely on the smbAuthenticationProvider.
Setting up the smbAuthenticationProvider is just a matter of configuring the SmbNtlmAuthenticationProvider with the authorizationProvider provider to be used.
A SMB NTLM aware authentication provider
<bean id="smbAuthenticationProvider" class="org.acegisecurity.providers.smb.SmbNtlmAuthenticationProvider">
<property name="authorizationProvider">
<ref local="nullDaoAuthenticationProvider"/>
</property>
</bean>
In this case we're referencing yet another item, the nullDaoAuthenticationProvider authentication provider.
The nullDaoAuthenticationProvider is a simple authentication provider that uses a separate UserDetailsService to retrieve the information about what roles a user has, and if you're using the default security.xml file for this that will be the users.properties file.
Alternatively the UserDetailsService could be accessing a database to retrieve the users roles, and later we'll be looking at changing this to use Active Directory (via LDAP) to determine the users roles.
A SMB simple password authenticator
<bean id="nullDaoAuthenticationProvider" class="org.acegisecurity.providers.smb.NullPasswordDaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="userCache">
<bean class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
<property name="cache">
<bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
</property>
<property name="cacheName" value="userCache"/>
</bean>
</property>
</bean>
</property>
</bean>
If you're starting with the default security.xml file that should be the final new section we need to add, since the userDetailsService that it references should already exist. And you could restart the server and assuming that the users.properties file has an entry for each user they should be able to log in without having to enter a username/password.
Customising the the SMB authentication process
Depending upon the version of active directory you're running you may need to specify a username/password for the ntlmProcessingFilter, so if you find authentication errors in the weave.log file after enabling integrated authentication then change the ntlmProcessingFilter to the following and set the appropriate username/password.
Setting username/password for domain access
<bean id="ntlmProcessingFilter" class="org.acegisecurity.ui.ntlm.NtlmProcessingFilter">
<property name="defaultDomain"><value>DOMAINNAME</value></property>
<property name="domainController"><value>172.16.0.30</value></property>
<property name="authenticationEntryPoint" ref="ntlmEntryPoint"/>
<property name="authenticationManager" ref="ntlmAuthenticationManager"/>
<property name = "JCifsProperties">
<map>
<entry key="jcifs.smb.client.username">
<value>username</value>
</entry>
<entry key="jcifs.smb.client.password">
<value>password</value>
</entry>
</map>
</property>
</bean>
Additional properties that can effect the SMB authentication process can be found here.
Selectively applying NTLM authentication
You can specify what IP addresses you want NTLM authentication to apply to, or not apply to, allowing you to support NTLM authentication for internal users and bypass it for external ones, for example (this prevents external users from being presented with a username/password dialogue box that they will probably not have valid values for).
To do this you need to replace the ntlmProcessingFilter, rather than using the org.acegisecurity.ui.ntlm.NtlmProcessingFilter class you should use the org.acegisecurity.ui.ntlm.IPFilteredNtlmProcessingFilter, this implementation of the NtlmProcessingFilter can then be provided with additional configuration items specifying which IP addresses should/shouldn't be provided with the option to authenticate using NTLM.
All the previous configuration items still apply, and should still be set, for the IPFilteredNtlmProcessingFilter. This new version just provides additional configuration options.
The new configuration items that the IPFilteredNtlmProcessingFilter provides are excludedIpAddresses and includedIpAddresses, and are set as a list of IP addresses or address ranges.
Selectively applying NTLM authentication
<bean id="ntlmProcessingFilter" class="org.acegisecurity.ui.ntlm.IPFilteredNtlmProcessingFilter">
<property name="defaultDomain"><value>DOMAINNAME</value></property>
<property name="domainController"><value>172.16.0.30</value></property>
<property name="authenticationEntryPoint" ref="ntlmEntryPoint"/>
<property name="authenticationManager" ref="ntlmAuthenticationManager"/>
<property name="excludedIpAddresses">
<list>
<value>192.168.2.0/24</value>
<value>138.19.19.50</value>
</list>
</property>
<property name="includedIpAddresses">
<list>
<value>172.16.0.0/16</value>
</list>
</property>
</bean>
You don't need to provide both excludedIpAddresses and includedIpAddresses, in fact it's more than likely that you'll only want to provide one, either listing those addresses that should be NTLM authenticated, and everyone else isn't, or listing those addresses that should not be NTLM authenticated and everyone else should. But, if you do provide both then the exclude list is checked first. Also, if the include list is set then the IP address must appear in the list for NTLM authentication to be attempted.
The IPFilteredNtlmProcessingFilter class is provided in version 1.0.7 or later of the org.acegisecurity.ntlm bundle