CSS

Friday, October 12, 2012

CAS Single Sign On Server Seam 3 Security Integration

Where I work at we use the CAS Single Signon Server so that our users only have to sign into one of our Web applications. They are then signed into all of our applications. This makes integration between them very easy. For my newest application We decided to use JSF and Seam Security.

Holder for CAS Returned Information

The following class is used to hold the information returned in the CAS Assertion. I have pulled out the getters, setters, equals and toString.

package org.yfu.util.cas;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;

import org.apache.commons.lang.StringUtils;
import org.jasig.cas.client.validation.Assertion;
import org.jboss.solder.logging.Logger;
import org.picketlink.idm.api.User;

public class CASUserBean implements Serializable, User 
{
 
 private static final long serialVersionUID = 401263788421715826L;
 
 @Inject
 private Logger log;
 
 
 private static final String STUDENT = "student";
// private static final String HOST_FAMILY = "hostfamily";
// private static final String PARTNER = "partner";
 private static final String VOLUNTEER = "volunteer";
 private static final String STAFF = "staff";
 private static final String REWARDS = "Rewards";
 private static final String TELEPHONE_NUMBER = "telephoneNumber";
 private static final String DEPARTMENT = "department";
 private static final String MAIL = "mail";
 private static final String TITLE = "title";
 private static final String ELECTRONIC_SIGNATURE = "electronicSignature";
 private static final String USER_LEVEL = "userLevel";
 private static final String POSTAL_CODE = "postalCode";
 private static final String SECURITY_QUESTION_ANSWER = "securityQuestionAnswer";
 private static final String SECURITY_QUESTION = "securityQuestion";
 private static final String PFO = "pfo";
 private static final String DISTINGUISHED_NAME = "distinguishedName";
 private static final String USER_PRINCIPAL_NAME = "userPrincipalName";
 private static final String NAME = "Name";


 private Integer pfoId;
 private String username;
 private String name;
 private List<String> groups = new ArrayList<String>();;
 private String email;
 private String securityQuestion;
 private String securityQuestionAnswer;
 private String postalCode;
 private String electronicSignature;
 private String title;
 private String studentID;
 private String department;
 private String telephone;
 private String userLevel;
 private Date validUntilDate; 
 private Date validFromDate;
 private String distinguishedName;
 private boolean loggedIn;
 private boolean staff;
 private boolean volunteer;
 
 
 public CASUserBean() {

 }
 
 public void init(Assertion assertion) {
  if (assertion != null && assertion.getPrincipal() != null) {
   this.validFromDate = assertion.getValidFromDate(); 
   this.validUntilDate = assertion.getValidUntilDate();
   Map<String, Object> attribs = assertion.getPrincipal().getAttributes();
   this.department = (String) attribs.get(DEPARTMENT);
   this.distinguishedName = (String) attribs.get(DISTINGUISHED_NAME);
   this.electronicSignature = (String) attribs.get(ELECTRONIC_SIGNATURE);
   this.email = (String) attribs.get(MAIL);
   this.name = (String) attribs.get(NAME);
   this.postalCode = (String) attribs.get(POSTAL_CODE);
   this.securityQuestion = (String) attribs.get(SECURITY_QUESTION);
   this.securityQuestionAnswer = (String) attribs.get(SECURITY_QUESTION_ANSWER);
   this.studentID = (String) attribs.get(STUDENT);
   this.telephone = (String) attribs.get(TELEPHONE_NUMBER);
   this.title = (String) attribs.get(TITLE);
   this.userLevel = (String) attribs.get(USER_LEVEL);
   this.username = (String) attribs.get(USER_PRINCIPAL_NAME);
   this.setLoggedIn(true);
   
   String value = (String) attribs.get(PFO);
   
   if (org.apache.commons.lang.StringUtils.isNotBlank(value)
     && org.apache.commons.lang.StringUtils.isNumeric(value)) {
    this.pfoId = Integer.valueOf(value);
   }
   
      
   groups = new ArrayList<String>();
   Object ldapGroups = attribs.get("group");
   
   if (ldapGroups instanceof List) {
    List<?> list = (List<?>) ldapGroups;
       for (Object obj : list ) {
        String group = (String) obj;
        addRole(group);
    }
   } else {
    addRole((String) ldapGroups);
   }
   
      
      value =  getDistinguishedName();
      
      if (StringUtils.contains(value, "DC=YFUUSA,DC=Local")) {
       groups.add(STAFF);
      }
      
      setStaff(getGroups().contains(STAFF));
      setVolunteer(getGroups().contains(VOLUNTEER));
  }
 }

 private void addRole(String group) {
  log.trace(group);
  group = StringUtils.substringAfter(group, "CN=");
  log.trace(group);
  group = StringUtils.substringBefore(group, ",");
  log.trace(group);
  if (StringUtils.isNotBlank(group)) {
   groups.add(group);
  }
 }

 @Override
 public String getKey() {
  return getDistinguishedName();
 }

 @Override
 public String getId() {
  return "" + getPfoId();
 }
}

Producer For CASUserBean

Nothing much interesting here.  Just a simple producer.  You could get away with out this I have it since I started not using Seam Security.

package org.yfu.util.cas;

import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.New;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpSession;

import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.validation.Assertion;
import org.jboss.solder.servlet.http.HttpSessionStatus;

@RequestScoped @Alternative 
public class CASUserProducer {

 
 @Inject private HttpSessionStatus sessionStatus;
 
 public CASUserProducer() {
  
 }
 
 @Produces
 @Named("casUser")
 @SessionScoped
 public CASUserBean getCasUser(@New CASUserBean user) {
  CASUserBean ret = user;
  
  ret.setName("Not Logged in");
  
  if (sessionStatus.isActive()) {
   HttpSession session = sessionStatus.get();
   Assertion assertion = (Assertion) session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
   if (assertion != null && assertion.getPrincipal() != null) {
    user.init(assertion);
    ret = user;
   }
  } 
  return ret;
 }

}

Seam Security Authenticator

The need this little class so that we can tell Seam Security we are logged in.  We also set the CASUserBean as our User.
package org.yfu.util.cas;

import javax.inject.Inject;
import javax.inject.Named;

import org.jboss.seam.security.BaseAuthenticator;
import org.jboss.seam.security.Identity;

public class CASSeamAuthenticator extends BaseAuthenticator
{

 
 @Inject  @Named("casUser")  private CASUserBean casUser;
 
 public CASSeamAuthenticator() {
 }

 @Override
 public void authenticate() {
  if (casUser != null && casUser.isLoggedIn()) {
   setStatus(AuthenticationStatus.SUCCESS);
   setUser(casUser);   
  }  

 }

 @Override
 public void postAuthenticate() {

 }


}


A Servlet Filter

We now create a Servlet filter that will be placed after our CAS Servlet Filters.  This is where we actually do the login (see line 38).
package org.jasig.cas.client.seam3.authentication;

import java.io.IOException;

import javax.inject.Inject;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.validation.Assertion;
import org.jboss.seam.security.Identity;
import org.yfu.util.cas.CASUserBean;


public class Seam3SecurityAuthenticationFilter implements Filter {


 @Inject private Identity identity;
 
 
 @Override
 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
   FilterChain chain) throws IOException, ServletException {

        final HttpServletRequest request = (HttpServletRequest) servletRequest;
        final HttpServletResponse response = (HttpServletResponse) servletResponse;
        final HttpSession session = request.getSession(false);
        final Assertion assertion = session != null ? (Assertion) session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : null;

        if (!identity.isLoggedIn() && assertion != null) {
         identity.login();  // this is alway successful
         
         CASUserBean user = (CASUserBean) identity.getUser();
         
         // Now we will add LDAP Groups as Groups with a group type of group.  
         
         for (String group : user.getGroups()) {
    identity.addGroup(group, "group");
   }
         
         
         
        }
         
        
        chain.doFilter(request, response);
  
 }


 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
  
 }


 @Override
 public void destroy() {
  
 }

 
}

Include the filter in the web.xml

I included the CAS filters so that you can see them in order. The order of the filter mappings is vitally important.

 
  CASWebAuthenticationFilter
  org.jasig.cas.client.jboss.authentication.WebAuthenticationFilter7
  
   casServerLoginUrl
   https://login.yfu.org/cas/login
  
  
   serverName
   ${cas.serverName}
  
 
 
  CASAuthenticationFilter
  org.jasig.cas.client.authentication.AuthenticationFilter
  
   casServerLoginUrl
   https://login.yfu.org/cas/login
  
  
   serverName
   ${cas.serverName}
  
 
 
  CASSeam3SecurityFilter
  org.jasig.cas.client.seam3.authentication.Seam3SecurityAuthenticationFilter
 
 
 
 
  CASWebAuthenticationFilter
  /secured/*
 
 
  CASAuthenticationFilter
  /secured/*
 
 
  CASSeam3SecurityFilter
  /secured/*
 

Wednesday, September 26, 2012

jQuery Character Counter for Multiple Textareas

The problem I had was the user is displayed a form where there are multiple textareas that they had to type at least 140 characters in all the textareas.

Below is the jQuery code I wrote to do it:

table is the id of a table that that all the textareas are a descendent of
cntOut is the id of a span I stick the count into.

jQuery('#table').on("blur keyup", "textarea", function(e) { var cnt = 0; jQuery("textarea").each( function(o) { cnt += this.textLength; }); jQuery(PrimeFaces.escapeClientId('#cntOut')).text(cnt); });

Thursday, May 17, 2012

DB2 Converting YYYYMMDD to timestamp

So my company has a iSeries server that includes a legacy application that stored dates as YYYYMMDD numeric(8,0). Time are stored as HHMM numeric(4,0). I have finally been able to create a function that take these 2 types of columns and return a timestamp.

 On of the cool things about the iSeries is that a data file that is written to my a legacy RPG program can be munipultated using SQL. From a client like JDBC or some DB2 client those file look just like a table. This is why the legacy applications did not store the date in a TIMESTAMP column. below is the function.

CREATE FUNCTION COMMON.TIMESTAMP_FROM_YFU (
YFUDATE NUMERIC(8, 0) ,
YFUTIME NUMERIC(4, 0) )
RETURNS TIMESTAMP  
LANGUAGE SQL
SPECIFIC COMMON.TIMESTAMP_FROM_YFU
DETERMINISTIC
CONTAINS SQL
RETURNS NULL ON NULL INPUT
NO EXTERNAL ACTION
SET OPTION  ALWBLK = *ALLREAD ,
ALWCPYDTA = *OPTIMIZE ,
COMMIT = *NONE ,
DECRESULT = (31, 31, 00) ,
DFTRDBCOL = *NONE ,
DYNDFTCOL = *NO ,
DYNUSRPRF = *USER ,
SRTSEQ = *HEX  
RETURN TIMESTAMP_FORMAT ( VARCHAR ( YFUDATE ) || DIGITS ( YFUTIME ) , 'YYYYMMDDHH24MI' )  ;
 
COMMENT ON SPECIFIC FUNCTION COMMON.TIMESTAMP_FROM_YFU
IS 'Timestamp from YFU Date and time' ;

Monday, May 14, 2012

Death to a Deadlock

One of the systems I have been working on has has had a deadlock that happens intermittently for the last 6 years.  Well today I finally found it.  We have a thread that runs in our application that times how long a web request takes.  If the request takes more then 30 minuets it assumes a deadlock and kills the application.  The thread also send an email to the programmers.  Below is the code I inserted in the monitoring thread code that allowed me to find the deadlock.



public static StringBuffer getDeadTraces(StringBuffer errorStringBuff) {
        StringBuffer deadtraces = new StringBuffer();
	try {
	    long[] ids = ManagementFactory.getThreadMXBean().findDeadlockedThreads();
	    if(null != ids && ids.length > 0) {
	         Map map = Thread.getAllStackTraces();
		 Set set = map.entrySet();
		 for (long id : ids) {
		    for (Entry entry : set) {
			 Thread thread = entry.getKey();
			if (thread.getId() == id) {
				deadtraces.append("\n===================  ");
				deadtraces.append(thread.getName());
				StringBufferUtils.append(deadtraces, "  ===================","\n");
							
				for (StackTraceElement el : entry.getValue()) {
					StringBufferUtils.append(deadtraces, el.toString(),"\n");                    						
				}												
			}
		    }				 
	         }
	    }
	} catch (Exception e) {
		errorStringBuff.append("Error trying to create a snapshot: " + 
		e.getMessage() + org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(e));
	}
	return deadtraces;
}


Friday, April 20, 2012

FindBugs

I just found the FindBugs project.  It is a great way of finding possible bugs in Java programs.  I spend some of my time taking care of a cranky old WebObjects + Java website.  I run the program against its code base and it found 368 possible errors.   Some were the relatively benign "Repeated conditional test" while others where the more serious "Incorrect lazy initialization and update of static field ".