Monday 7 October 2013

[JasperServer 5.2] Reading additional attribute from Apache LDAP with CAS (Timezone and Locale)

After struggling and googling with no result, finally we found a way to read additional attribute from LDAP and set it to JasperServer. What I am sharing here again is based on CAS Server 3.5.2 and CAS Client above 3.1.5 (Jasper bundled).

On CAS Server part:
1. Open file "/WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp"
2. Below "<cas:user>...</cas:user> add tag to read attribute

<cas:attributes>
  <c:if test="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes) >= 1}">
    <c:forEach var="attr" items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}"
                    varStatus="loopStatus" begin="0" end="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes)-1}"
                    step="1">
        <cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>
    </c:forEach>
  </c:if>
</cas:attributes>

3. Open file "/WEB-INF/deployerConfigContext.xml"
4. Find bean id "attributeRepository" property "resultAttributeMapping" and add attribute that you need here. In my case it will be timezone and locale

<property name="resultAttributeMapping">
  <map>
    <!-- Mapping beetween LDAP entry attributes (key) and Principal's (value) -->
    <entry value="locale" key="locale" />
    <entry value="timezone" key="timezone" />
    <entry value="mail" key="mail" />
    <entry value="userpassword" key="userpassword" />
    <entry value="userstatus" key="userstatus" />
    <entry value="name" key="cn" />
  </map>
</property>

5. Start the server once you are ready

On JasperServer part:
1. Open file "/WEB-INF/applicationContext-externalAuth-CAS-db-mt.xml"
2. Find bean id "proxyAuthenticationProcessingFilter" and replace it with your custom class with full package and class name
3. Create above mentioned class inside your project (again I'm using overlay method) which extend "com.jaspersoft.jasperserver.api.security.externalAuth.cas.JSCasProcessingFilter"
4. Override method "onSuccessfulAuthentication" and read the attribute from XML Response. Code below is for your reference only:

import org.jasig.cas.client.authentication.AttributePrincipal;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.security.Authentication;
import org.springframework.security.providers.cas.CasAuthenticationToken;
import com.jaspersoft.jasperserver.api.common.util.TimeZoneContextHolder;
import com.jaspersoft.jasperserver.api.metadata.user.service.impl.HttpOnlyResponseWrapper;
import com.jaspersoft.jasperserver.api.security.externalAuth.cas.JSCasProcessingFilter;
import com.jaspersoft.jasperserver.war.common.JasperServerConstImpl;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Locale;
import java.util.TimeZone;

public class CustomJSCasProcessingFilter extends JSCasProcessingFilter {
 
  private static final String TIMEZONE_ATTRIBUTE_KEY = "timezone";
  private static final String LOCALE_ATTRIBUTE_KEY = "locale";
  private int cookieAge;
 
  @Override
  protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
      Authentication authResult) throws IOException {
    super.onSuccessfulAuthentication(request, response, authResult);
   
    //Applying locale and timezone
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpSession session = httpRequest.getSession();
   
    //New Attribute to support ldap locale and timezone
    String strLdapLocale = null;
    String strLdapTimeZone = null;
   
    HttpOnlyResponseWrapper httpOnlyResponseWrapper = new HttpOnlyResponseWrapper(
        (HttpServletResponse) response);
   
    CasAuthenticationToken casAuthenticationToken = (CasAuthenticationToken) authResult;
   
    if(casAuthenticationToken.getAssertion().getPrincipal() instanceof AttributePrincipal) {
      AttributePrincipal attributePrincipal = (AttributePrincipal) casAuthenticationToken.getAssertion().getPrincipal();

      if(!attributePrincipal.getAttributes().containsKey(LOCALE_ATTRIBUTE_KEY)) {
        strLdapLocale = Locale.getDefault().toString();
      }
      strLdapLocale = (String) attributePrincipal.getAttributes().get(LOCALE_ATTRIBUTE_KEY);
     
      if(!attributePrincipal.getAttributes().containsKey(TIMEZONE_ATTRIBUTE_KEY)) {
        strLdapTimeZone = TimeZone.getDefault().toString();
      }
      strLdapTimeZone = (String) attributePrincipal.getAttributes().get(TIMEZONE_ATTRIBUTE_KEY);

    }

    Locale sessionLocale = (Locale) session.getAttribute(JasperServerConstImpl
        .getUserLocaleSessionAttr());
   
    if (strLdapLocale != null) {
      Locale ldapLocale = toLocale(strLdapLocale);
      if (sessionLocale == null || !sessionLocale.equals(ldapLocale)) {
        session.setAttribute(JasperServerConstImpl.getUserLocaleSessionAttr(), ldapLocale);
        Cookie cookie = new Cookie(JasperServerConstImpl.getUserLocaleSessionAttr(), strLdapLocale);
        cookie.setMaxAge(cookieAge);
        httpOnlyResponseWrapper.addCookie(cookie);
      }
    }

    if (sessionLocale != null) {
      LocaleContextHolder.setLocale(sessionLocale);
    }

    if (strLdapTimeZone != null) {
      String sessionTimezone = (String) session.getAttribute(JasperServerConstImpl
          .getUserTimezoneSessionAttr());
      if (sessionTimezone == null || !sessionTimezone.equals(strLdapTimeZone)) {
        session.setAttribute(JasperServerConstImpl.getUserTimezoneSessionAttr(), strLdapTimeZone);
        Cookie cookie = new Cookie(JasperServerConstImpl.getUserTimezoneSessionAttr(), strLdapTimeZone);
        cookie.setMaxAge(cookieAge);
        httpOnlyResponseWrapper.addCookie(cookie);
        TimeZoneContextHolder.setTimeZone(TimeZone.getTimeZone(strLdapTimeZone));
      }
    }
   
  }
 
  private Locale toLocale(String str) {
    if (str == null) {
      return null;
    }
    int len = str.length();
    if (len != 2 && len != 5 && len < 7) {
        throw new IllegalArgumentException("Invalid locale format: " + str);
    }
    char ch0 = str.charAt(0);
    char ch1 = str.charAt(1);
    if (ch0 < 'a' || ch0 > 'z' || ch1 < 'a' || ch1 > 'z') {
        throw new IllegalArgumentException("Invalid locale format: " + str);
    }
    if (len == 2) {
        return new Locale(str, "");
    } else {
        if (str.charAt(2) != '_') {
            throw new IllegalArgumentException("Invalid locale format: " + str);
        }
        char ch3 = str.charAt(3);
        if (ch3 == '_') {
            return new Locale(str.substring(0, 2), "", str.substring(4));
        }
        char ch4 = str.charAt(4);
        if (ch3 < 'A' || ch3 > 'Z' || ch4 < 'A' || ch4 > 'Z') {
            throw new IllegalArgumentException("Invalid locale format: " + str);
        }
        if (len == 5) {
            return new Locale(str.substring(0, 2), str.substring(3, 5));
        } else {
            if (str.charAt(5) != '_') {
                throw new IllegalArgumentException("Invalid locale format: " + str);
            }
            return new Locale(str.substring(0, 2), str.substring(3, 5), str.substring(6));
        }
    }
  }
 
  public int getCookieAge() {
    return cookieAge;
  }

  public void setCookieAge(int cookieAge) {
    this.cookieAge = cookieAge;
  }
 
}

5. Compile the application and package it using maven
6. Deploy the application and enjoy. Locale and Timezone now setup to your LDAP attribute
7. For additional information, if you want to show these attribute, just modify file "/WEB-INF/decorators/decorator.jsp". Below this tag:

 <li id="main_logOut" class="last"><a id="main_logOut_link"><spring:message code="menu.logout"/></a></li>






Add this tag:

<li id="main_test" class="leaf"><%=session.getAttribute(JasperServerConstImpl.getUserLocaleSessionAttr())%></li>
<li id="main_test" class="last"><%=session.getAttribute(JasperServerConstImpl.getUserTimezoneSessionAttr())%></li>

8. Also you need to import the constant by putting this on top of the same file:

<%@ page import="com.jaspersoft.jasperserver.war.common.JasperServerConstImpl" %>

Cheers,
Deddy

No comments:

Post a Comment