Friday, December 31, 2021

Ldap Optimization

Multi-factor Authentication with Spring Security and LDAP

 
































 

Required Libraries

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
           <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-ldap</artifactId>
           <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>${spring.version}</version>
        </dependency>


Spring Context

<!-- Activate Spring Annotation -->
<context:annotation-config />

<!-- Load property file properties -->
<context:property-placeholder location="classpath:*.properties" />

<!-- Spring Authentication Provider -->

<security:authentication-manager>
<security:authentication-provider ref="LDAPProvider" />
</security:authentication-manager>

<!-- Spring Authentication Provider bean definition -->

<beans:bean id="LDAPProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<beans:constructor-arg>
<beans:bean
class="org.springframework.security.ldap.authentication.BindAuthenticator">
<beans:constructor-arg ref="contextSource"></beans:constructor-arg>
<beans:property name="userSearch" ref="userSearch"></beans:property>
</beans:bean>
</beans:constructor-arg>
       <beans:constructor-arg>
            <beans:bean  class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
                <beans:constructor-arg ref="contextSource"/>
                <beans:constructor-arg value="ou=groups"/>
                <beans:property name="groupRoleAttribute" value="memberOf"/>
            </bean>
        </beans:constructor-arg>

<!-- You can write custom Auth populater here -->

</beans:bean>

<!-- Spring Authentication Provider bean implementation -->

<beans:bean id="userSearch" class="com.mycompany.service.CustomLdapUserSearch">
<beans:constructor-arg value=""></beans:constructor-arg>
<beans:constructor-arg value="(sAMAccountName={0})"></beans:constructor-arg>
<beans:constructor-arg ref="contextSource"></beans:constructor-arg>
<beans:property name="searchSubtree" value="true"></beans:property>
</beans:bean>
<!-- LDAP Spring context -->

<beans:bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<beans:constructor-arg value="${ldap.server}" />
<beans:property name="userDn" value="CN=<????>,OU=<????>,OU=<????>,DC=<???>,DC=<????>,DC=<???>" />
<beans:property name="password" value="<????>" />
</beans:bean>


<!-- Spring Security Auth failure message definitions -->


    <beans:bean id="authenticationFailureHandlerException"
class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<beans:property name="exceptionMappings">
<beans:props>
            <beans:prop key="org.springframework.security.authentication.BadCredentialsException">/?error=Invalid user name or password. Please re-enter.</beans:prop>
            <beans:prop key="org.springframework.security.authentication.CredentialsExpiredException">/?error=Your credentials has been expired</beans:prop>
            <beans:prop key="org.springframework.security.authentication.LockedException">/?error=Your account has been locked</beans:prop>
            <beans:prop key="org.springframework.security.authentication.DisabledException">/?error=Your account has been disabled</beans:prop>
</beans:props>
</beans:property>
</beans:bean>

<beans:bean id="authenticationFailureHandler" 
class="com.company.service.AuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/"></beans:property>
</beans:bean>


<!-- Spring Security Role definitions for each page -->

    <security:http auto-config="true" use-expressions="true" access-denied-page="/" disable-url-rewriting="true">

<security:intercept-url pattern="/viewRePorts./**" access="hasAnyRole('VIEW_TRANSACTION_2FAYES','ADJUST_TRANSACTION_2FAYES')"/>
<security:intercept-url pattern="/viewRePorts" access="hasAnyRole('VIEW_TRANSACTION_2FAYES','ADJUST_TRANSACTION_2FAYES')"/>
<security:intercept-url pattern="/viewRePorts./**" access="hasAnyRole('VIEW_TRANSACTION_2FAYES','ADJUST_TRANSACTION_2FAYES')"/>

 <!-- Spring Security Login Form -->

<!--  always-use-default-target="true" : This is to strictly follow the 2 factor validation flow -->
<!--  default-target-url="/otpVerification" : This is the 2 factor page just after the successful login -->
    <security:form-login 
            always-use-default-target="true"
        login-processing-url="/j_spring_security_check" 
        login-page="/" 
        default-target-url="/otpVerification"
authentication-failure-url="/?error=Error while login. Please try Again"
authentication-failure-handler-ref="authenticationFailureHandlerException"
username-parameter="j_username" 
        password-parameter="j_password" />
<security:logout invalidate-session="true" logout-success-url="/?error=You've been logged out successfully." />
 <!-- Redirect to login page if session is invalid -->
 <!-- Redirect to login page if session is expired -->

<security:session-management  invalid-session-url="/">
        <security:concurrency-control expired-url="/" />
</security:session-management>

</security:http>


HTML Form

 <form  name='f' action="<c:url value='j_spring_security_check' />" method='POST'>
        <input type="text" class="form-control" autocomplete="off" type='text' name='j_username' id='j_username' placeholder="User Name">
        <input type="password" class="form-control" autocomplete="off" type='password' name='j_password' id='j_password' placeholder="Password">
        <button name="Submit" type="submit" value="Login" >Sign In</button>
    </form>


Java Implementation ( Once 2 factor is success, then only set the roles )

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
List<GrantedAuthority> updatedAuthorities = new ArrayList<>(auth.getAuthorities());
updatedAuthorities.add(new SimpleGrantedAuthority('VIEW_TRANSACTION_2FAYES'));
updatedAuthorities.add(new SimpleGrantedAuthority('ADJUST_TRANSACTION_2FAYES'));
Authentication newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(), auth.getCredentials(), updatedAuthorities);
SecurityContextHolder.getContext().setAuthentication(newAuth);