Thursday, November 8, 2012

Jasig CAS

In most cases the login code of your application does not require thinking a lot. Simply use the standard module available on your framework, that is all you need.

But sometimes, you need to integrate third-party applications with your own. For example a cms, forums, ticketing systems, etc. Your users can stand a couple of months (in the best case) entering two times the user/password. But you know it is something you need to change.

At the beginning someone could be tempted to implement your own solution because seems it is very simple. Simply read one cookie from here and put there, and then redirect to there and back to... Well, at the end you will see yourself hacking every third-party application, and learning new concepts as man-in-the-middle, spoofing, etc. That is good, of course. But could be even more good if you can learn it with a bit less pressure because you have been so optimistic when you schedule this feature.

What I use in that cases is a SSO server. This allows me to leverage of SSO functionality on my application in a fraction of time if I'd try to implement by myself.
Jasig CAS (Central Authentication System) lets you to integrate it easily on your java web applications, but also in other platforms as PHP, .NET an others. The protocol is well documented and can be implemented in any platform if it is not available. On the other side, you have a lots of CASified applications which is how they name the clients that are been integrated into CAS.

Here we will see how can we use CAS on our JBoss6 applications. But, first of all you should start reading https://wiki.jasig.org/display/CASUM/Demo and try to become familiar with the CAS use.

Then, when you feel prepared to try it out with your JBoss/JAAS application follow the next steps. Remember you need to have a CAS server already up and running and reachable from http://yourcasserver/cas so, lets go:
  • Copy the cas-client-core-<version>.jar and cas-client-integration-jboss-<version>.jar to the server/default/lib dir.
  • Configure de JAAS login-config module as follows:
    <application-policy name="cas">
       <authentication>
          <login-module code="org.jasig.cas.client.jaas.CasLoginModule" flag="required">
             <module-option name="ticketValidatorClass">
               org.jasig.cas.client.validation.Cas20ServiceTicketValidator
             </module-option>
             <module-option name="casServerUrlPrefix">
                http://yourcasserver/cas
             </module-option>
             <module-option name="tolerance">20000</module-option>
             <module-option name="defaultRoles">admin,user</module-option>
             <module-option name="roleAttributeNames">role,list</module-option>
             <module-option name="principalGroupName">CallerPrincipal</module-option>
             <module-option name="roleGroupName">Roles</module-option>
             <module-option name="cacheAssertions">true</module-option>
             <module-option name="cacheTimeout">480</module-option>
          </login-module>
       </authentication>
    </application-policy>
  • Modify the deploy/jbossweb.sar/server.xml and uncomment:
    <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
  • Finally in the web.xml of your xml you need to configure the servlet filters:
    <!-- Facilitates CAS single sign-out --> <listener> <listener-class> org.jasig.cas.client.session.SingleSignOutHttpSessionListener </listener-class> </listener> <!-- Following is needed only if CAS single-sign out is desired --> <filter> <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> </filter> <!-- Only 2 CAS filters are required for JAAS support --> <filter> <filter-name>CASWebAuthenticationFilter</filter-name> <filter-class>org.jasig.cas.client.jboss.authentication.WebAuthenticationFilter</filter-class> </filter> <filter> <filter-name>CASAuthenticationFilter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://cas.example.com/cas/login</param-value> </init-param> </filter> <!-- Other filters as needed --> <!-- CAS client filter mappings --> <!-- The order of the following filters is vitally important --> <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CASWebAuthenticationFilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> <filter-mapping> <filter-mapping> <filter-name>CASAuthenticationFilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping>

    In your WEB-INF/jboss-web.xml select the appropriate security domain:
    <jboss-web>
    <security-domain>java:/jaas/cas</security-domain> </jboss-web>
  • The first time, may be you could want to activate the trace logging to inspect any error you could get in your JBoss app. In server/default/deploy/jboss-logging.xml do it by adding:
    <logger category="org.jasig">
       <level name="TRACE" />
     </logger>
That is all. Start the server and make some tests. You have all of this more detailed in:

Friday, October 12, 2012

How not to design a IndexedDB HTML5 API

Today I'm playing a little with the IndexedDB API and I was a bit confused about how it has been designed.
The antecedents: Since the IndexedDB API is asyncronous, when you call to a method, for example open(), it won't be blocked waiting for the results of the function. Instead, you will get a request object, where you can associate callbacks to receive errors, results, or other kind of events. You can see here how it is done:
  1. var request = indexedDB.open("TestIndexed", 1);  
  2. request.onsuccess = function(evt) {
  3.     console.log("db sucessfully opened");
  4.     db = request.result;                                                            
  5. };
As you can see, we call to the open() method, immediately it returns a request object where we can install an onsuccess callback that will be called if the open operation was successfully completed. What confused me was that if the open() method send an event before you has been installed the callback you will lose the event. We can argue that this shouldn't ocurr because if you install the event just after the method call, you will stay almost sure that the database operation will spend more time that assign the callbacks.
I agree with that, but anyway I don't like that my API interface works only because I suppose some methods will take more time than others.

The problem is that if you put some code between the database open() and the callbacks, you will get in trouble. Every browser will take more or less time on the open() operation. For example, on my firefox browser took arround 70 miliseconds and in chrome took arround 120 milisecs. So, if you have something like this:
  1. var request = indexedDB.open("TestIndexed", 1);  
  2. foo();
  3. request.onsuccess = function (evt) {
  4.     db = request.result;                                                            
  5. };
Depending the time take the foo() method, this code might or might not work on a specific browser.
I know it hasn't sense do something like that. But for that reason, the design of the API should reinforce you to prepare all your data and events before the method call. It is really ugly prepare your events after calling the method. A better approach to this scenario could be:

  1. var request = indexedDB.open("TestIndexed", 1)
  2. request.onsuccess = function(evt) {
  3.     console.log("db sucessfully opened");
  4.     db = request.result;                                                            
  5. };
  6. request.onerror = function(evt) {
  7.     console.log("error");                                                      
  8. };
  9. request.perform() // This invoke the operation
Or even better:

  1. var callbacks = {};
  2. callbacks.onsuccess = function (evt) {
  3.     db = request.result;                                                            
  4. };
  5. indexedDB.open("TestIndexed", 1, callbacks);  
In this case you are being forced to define your callbacks before calling the method, so you will never lose any event, and it is more clear, elegant and follow the same convention that others functions in  Javascript.

In order to solve that issue, I'm using this class to call to IndexedDB API methods:
  1. var AsyncCall = {
  2.     call: function(request, callbacks) {
  3.         for(var c in callbacks) {
  4.             request[c] = callbacks[c];
  5.         }
  6.     }
  7. };
Now, we can use the IndexedDB API as follow:
  1. AsyncCall.call(indexedDB.open("TestIndexed", 1), {
  2.     onsuccess: function(evt) {
  3.         console.log("ok");
  4.     },
  5.     onerror: function(evt) {
  6.         console.log("error");
  7.     }
  8. });