Sunday, November 06, 2011

User impersonation with Spring and LDAP

Impersonating a user (also known as "switch a user" or "do as user") in a web application is an important feature for doing Quality Assurance (QA) or any Manual User Acceptance Test (UAT).

With Spring you can achieve it using the SwitchUserFilter. If you are using LDAP here are the relevant pieces.

...
<beans:bean id="ldapUserDetailsService" class="org.springframework.security.ldap.userdetails.LdapUserDetailsService">
  <beans:constructor-arg><beans:ref bean="ldapUserSearch"/></beans:constructor-arg>
  <beans:constructor-arg><beans:ref bean="ldapAuthoritiesPopulator"/></beans:constructor-arg>
  <beans:property name="userDetailsMapper" ref="customUserDetailsContextMapper" />
</beans:bean>
...
<beans:bean id="ldapUserSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
    <beans:constructor-arg type="String"><beans:value>ou=people,o=nestorurquiza</beans:value></beans:constructor-arg>
    <beans:constructor-arg type="String"><beans:value>mail={0}</beans:value></beans:constructor-arg>
    <beans:constructor-arg><beans:ref bean="ldapContextSource"/></beans:constructor-arg>
</beans:bean>
 ...                            
<beans:bean id="switchUserProcessingFilter" class="org.springframework.security.web.authentication.switchuser.SwitchUserFilter">
        <beans:property name="userDetailsService" ref="ldapUserDetailsService" />
        <beans:property name="switchUserUrl" value="/admin/switchUser" />
        <beans:property name="exitUserUrl" value="/admin/switchUserExit" />
        <beans:property name="targetUrl" value="/" />
</beans:bean>
...
<http auto-config="true" use-expressions="true" access-decision-manager-ref="accessDecisionManager" disable-url-rewriting="true">
    ...
 <custom-filter  after="FILTER_SECURITY_INTERCEPTOR" ref="switchUserProcessingFilter" />
    ...
    <intercept-url pattern="/admin/**" access="hasAnyRole('ROLE_ADMIN','ROLE_PREVIOUS_ADMINISTRATOR')" />
... 
<beans:bean id="ldapContextSource" class="org.springframework.ldap.core.support.LdapContextSource">

    <beans:property name="url" value="${ldap.url}" />
    <beans:property name="userDn" value="${ldap.userDn}" />
    <beans:property name="password" value="${ldap.password}" />
</beans:bean>
...
<beans:bean id="ldapAuthProvider"
        class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
  <beans:constructor-arg>
    <beans:bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
      <beans:constructor-arg ref="ldapContextSource"/>
      <beans:property name="userDnPatterns">
        <beans:list><beans:value>mail={0},ou=people,o=nestorurquiza</beans:value></beans:list>
      </beans:property>
    </beans:bean>
  </beans:constructor-arg>
  <beans:constructor-arg ref="ldapAuthoritiesPopulator"/>
</beans:bean>
...
<beans:bean id="ldapAuthoritiesPopulator" class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
      <beans:constructor-arg ref="ldapContextSource"/>
      <beans:constructor-arg value="ou=groups,o=nestorurquiza"/>
      <beans:property name="groupSearchFilter" value="uniquemember={0}" />
</beans:bean>
...    
<beans:bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
    <beans:constructor-arg ref="ldapContextSource" />
</beans:bean>
...
I18n messages:
switchUser=Impersonate
switchUserExit=Be yourself
A link to impersonate in the users listing page should be available only for admins:
<security:authorize access="hasRole('ROLE_ADMIN')">
                        | <a href="<spring:url value="/admin/switchUser?j_username=${employee.email}" htmlEscape="true" />">
                            <spring:message code="switchUser"/></a>
                </security:authorize>
A link to switch back to admin user should be available only if the user is being impersonated:
<security:authorize access="hasRole('ROLE_PREVIOUS_ADMINISTRATOR')">
                        | <a href="<spring:url value="/admin/switchUserExit" htmlEscape="true" />">
                            <spring:message code="switchUserExit"/></a>
                    </security:authorize>
The way you use it is hitting the below URLs to switch the user and come back to the original admin user context:
http://localhost:8080/nestorurquiza-app/admin/switchUser?j_username=nurquiza@nestorurquiza.com
http://localhost:8080/nestorurquiza-app/admin/switchUserExit

1 comment:

QPT said...

Thanks for sharing.

It's very useful for all.

QTPbook

Followers