Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

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)

Code Block
none
none
titleEnable security process logging
linenumberstruenone
log4j.logger.org.acegisecurity=DEBUG
log4j.logger.com.cohga.server.security=DEBUG

...

Looking at the default security.xml file it contains the following near the top:

Code Block
xml
xml
titleDefault request filter chain
linenumberstruexml
	<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>

...

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:

Code Block
xml
xml
titleAdding NTLM filter to request filter chain
linenumberstruexml
	<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>

...

To do this we add a new section to the security.xml file to define a new NtlmProcessingFilter

Code Block
xml
xml
titleThe root NTLM filter definition
linenumberstruexml
	<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 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.

Code Block
xml
xml
titleNTLM entry point for obtaining the user information from browser
linenumberstruexml
	<bean id="ntlmEntryPoint" class="org.acegisecurity.ui.ntlm.NtlmProcessingFilterEntryPoint"/>
Code Block
xml
xml
titleNTLM authentication manager for verifying the user information from browser
linenumberstruexml
	<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>

...

Setting up the smbAuthenticationProvider is just a matter of configuring the SmbNtlmAuthenticationProvider with the authorizationProvider provider to be used.

Code Block
xml
xml
titleA SMB NTLM aware authentication provider
linenumberstruexml
	<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.

Code Block
xml
xml
titleA SMB simple password authenticator
linenumberstruexml
	<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>

...

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.

Code Block
xml
xml
titleSetting username/password for domain access
linenumberstruexml
	<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>

...

The new configuration items that the IPFilteredNtlmProcessingFilter provides are excludedIpAddresses and includedIpAddresses, and are set as a list of IP addresses or address ranges.

Code Block
xml
xml
titleSelectively applying NTLM authentication
linenumberstruexml
	<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>

...

As of version 1.3.4 of the org.acegisecurity.ntlm bundle there's an additional property that can be set for the IPFilteredNtlmProcessingFilter, and that's defaultRole, which when set will add the role (exactly as it appears in the security.xml file) to the list of roles the user has. This allows you to utilise multiple Active Directory domain to authenticate user and provide access control based on what domain the user was authenticated against.
Note: If you're using LDAP to provide the users roles then it's also possible to set a defaultRole in the LDAP populator.

Code Block
xml
xml
titleUsing multiple domain for authentication
linenumberstruexml
	<bean id="ntlmProcessingFilterInternal" class="org.acegisecurity.ui.ntlm.IPFilteredNtlmProcessingFilter">
		<property name="defaultDomain"><value>INTERNAL</value></property>
		<property name="domainController"><value>172.16.0.30</value></property>
		<property name="authenticationEntryPoint" ref="ntlmEntryPoint"/>
		<property name="authenticationManager" ref="ntlmAuthenticationManager"/>
		<property name="includedIpAddresses">
			<list>
				<value>172.16.0.0/16</value>
			</list>
		</property>
		<property name="defaultRole"><value>ROLE_INTERNAL</value></property>
	</bean>

	<bean id="ntlmProcessingFilterExternal" class="org.acegisecurity.ui.ntlm.IPFilteredNtlmProcessingFilter">
		<property name="defaultDomain"><value>EXTERNAL</value></property>
		<property name="domainController"><value>201.20.109.76</value></property>
		<property name="authenticationEntryPoint" ref="ntlmEntryPoint"/>
		<property name="authenticationManager" ref="ntlmAuthenticationManager"/>
		<property name="includedIpAddresses">
			<list>
				<value>201.20.0.0/16</value>
			</list>
		</property>
		<property name="defaultRole"><value>ROLE_EXTERNAL</value></property>
	</bean>

Not that to enable this both filters need to be added to the filter chain:

Code Block
xml
xml
titleAdding NTLM filter to request filter chain
linenumberstruexml
	<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
		<property name="filterInvocationDefinitionSource">
			<value>
				CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
				PATTERN_TYPE_APACHE_ANT
				/server/**=httpSessionContextIntegrationFilter,ntlmProcessingFilter1,ntlmProcessingFilter2,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,jsonExceptionTranslationFilter,filterInvocationInterceptor
				/**=httpSessionContextIntegrationFilter,ntlmProcessingFilter1,ntlmProcessingFilter2,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
			</value>
		</property>
	</bean>

...

So we fist need to change the ntlmAuthenticationManager to

Code Block
xml
xml
titleEnabling LDAP support for authenticating users
linenumberstruexml
	<bean id="ntlmAuthenticationManager" class="org.acegisecurity.providers.ProviderManager">
		<property name="providers">
			<list>
				<ref local="ldapAuthenticationProvider"/>
				<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>

And then setup the new ldapAuthenticationProvider as follows:

Code Block
xml
xml
titleLDAP authentication provider that can handle NTLM authenticated users
linenumberstruexml
	<bean id="ldapAuthenticationProvider" class="org.acegisecurity.ui.ntlm.ldap.authenticator.NtlmAwareLdapAuthenticationProvider">
		<constructor-arg>
			<ref local="authenticatorLdap"/>
		</constructor-arg>
		<constructor-arg>
			<ref local="populatorLdap"/>
		</constructor-arg>
	</bean>

...

The authentication would be configured as follows:

Code Block
xml
xml
titleThe authenticator that will search an LDAP directory for the user
linenumberstruexml
	<bean id="authenticatorLdap" class="org.acegisecurity.ui.ntlm.ldap.authenticator.NtlmAwareLdapAuthenticatorImpl">
		<constructor-arg>
			<ref local="initialDirContextFactory"/>
		</constructor-arg>
		<property name="userSearch">
			<ref local="userSearchLdap"/>
		</property>
	</bean>

...

The initialDirContextFactory is also used by the userSearchLdap and the populaterLdap beans so we'll look at that first

Code Block
xml
xml
titleSetting up a connection to the LDAP server
linenumberstruexml
	<bean id="initialDirContextFactory" class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
		<constructor-arg value="ldap://192.168.0.16:389/"/>
		<property name="managerDn">
			<value>CN=Administrator,OU=Users,DC=cohga,DC=local</value>
		</property>
		<property name="managerPassword">
			<value>password</value>
		</property>
	</bean>

...

The two final beans, the userSearchLdap and populatorLdap also require information that is specific to the environment you're running within, the userSearchLdap beans would be something like the following:

Code Block
xml
xml
titleSetting up an LDAP search for a user
linenumberstruexml
	<bean id="userSearchLdap" class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
		<constructor-arg>
			<value>OU=Users,DC=cohga,DC=local</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>
	</bean>

...

Finally the populatorLdap is responsible for mapping the username to the roles and would be configured as follows

Code Block
xml
xml
titleSetting up an LDAP serach for groups
linenumberstruexml
	<bean id="populatorLdap" class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
		<constructor-arg>
			<ref local="initialDirContextFactory"/>
		</constructor-arg>
		<constructor-arg>
			<value>OU=Weave,DC=cohga,DC=local</value>
		</constructor-arg>
		<property name="groupRoleAttribute">
			<value>cn</value>
		</property>
		<property name="searchSubtree">
			<value>true</value>
		</property>
		<property name="rolePrefix">
			<value>ROLE_</value>
		</property>
		<property name="convertToUpperCase">
			<value>true</value>
		</property>
		<property name="groupSearchFilter">
			<value>(member={0})</value>
		</property>
		<property name="defaultRole">
			<value>ROLE_USERS</value>
		</property>
	</bean>

...

To implement the new authorities populator you should change:

Code Block
xml
xml
titleOld DefaultLdapAuthoritiesPopulator
linenumberstruexml
<bean id="populator" class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
	...
</bean>

to

Code Block
xml
xml
titleNew SupplementedLdapAuthoritiesPopulator with reference to user details service
linenumberstruexml
<bean id="populator" class="org.acegisecurity.providers.ldap.populator.SupplementedLdapAuthoritiesPopulator">
	<property name="userDetailsService" ref="ldapDetailsService"/>
	...
</bean>

This will change it from DefaultLdapAuthoritiesPopulator to SupplementedLdapAuthoritiesPopulator and add a reference to the service that will provide the additional role information, which can be included by adding:

Code Block
xml
xml
titleNew user details service reading roles from a file
linenumberstruexml
<bean id="ldapDetailsService" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
	<property name="userProperties">
		<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
			<property name="location" value="ldap.properties"/>
		</bean>
	</property>
</bean>

...

Alternatively you could also use the following if you just had a couple of users:

Code Block
xml
xml
titleNew user details service reading roles from its configuration
linenumberstruexml
<bean id="ldapDetailsService" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
	<property name="userMap">
		<value>
			shaun=password,ROLE_TEST
		</value>
	</property>
</bean>

...