Sunday, December 2, 2007

Windows SSO with JBoss Seam

Unified authentication has been one of the frequently asked functionality for quite some time now. Users have to remember username/password for various systems or having to login for each system while all those systems are part of the same intranet and this doesn't make life/work easier.
The goal of this article is to show how it is possible to have a very simple solution to use Single Sign-On for WIntel based intranets to get access (and authenticate) to web sites without having to fill-in their user name and password (by allowing browser to use the windows' user login silently).
Please, also note, that this is merely a demo and in no way a "bullet proof" or production-ready example.

This example uses NTLM implemented by JCIFS that implements the CIFS/SMB networking protocol in 100% Java. CIFS is the standard file sharing protocol on the Microsoft Windows platform. JCIFS already has an implementation of a filter that does the authentication against windows domain contoller. This article simply shows how to make this solution work closely with JBoss Seam built-in security functionality.

For the following example I used JBoss 4.2.2 with default profile, Seam 2.0 GA, JCIFS 1.2.17. And a simple application generated by seam-gen.

  1. Of course you need to get JCIFS library, you can put the jar in /lib directory of your application and modify the 'ear' target in your build script to include the library in EAR during packaging.

  2. Next step would be configuring JCIFS http authentication filter to authenticate incoming requests against the domain controller. This is no different from the way it is done by JCIFS authentification filter:


    <filter>
    <filter-name>NtlmHttpFilter</filter-name>
    <filter
    -class>jcifs.http.NtlmHttpFilter</filter-class>
    <init-param>
    <param-name>jcifs.http.domainController</param-name>
    <param-value>your domain controller ip or name</param-value>
    </init-param>
    <init-param>
    <param-name>jcifs.smb.client.domain</param-name>
    <param-value>your domain</param-value>
    </init-param>
    <init-param>
    <param-name>jcifs.smb.lmCompatibility</param-name>
    <param-value>3</param-value>
    </init-param>
    <init-param>
    <param-name>jcifs.util.loglevel</param-name>
    <param-value>2</param-value>
    </init-param>
    </filter>
    <filter-mapping>
    <filter-name>NtlmHttpFilter</filter-name>
    <url-pattern>
    /*</url-pattern>
    </filter-mapping>



  3. Add autoLogin() method to the generated Authenticator that does the authentication without the need to fill in user name and password by getting the authentication information from the browser. Please note, we are setting a fake password as in this case we don't even know it, but by having a password set we actually make Seam recongize the identity as logged-in:


    /**
    * Performs the windows authentication.
    * @return true if auto login was successful
    */
    public boolean autoLogin() {

    // trying auto-login
    Object autoLogin = sessionContext.get("NtlmHttpAuth");
    boolean isAuthenticated = false;

    if ( autoLogin != null && (autoLogin instanceof NtlmPasswordAuthentication) ) {
    NtlmPasswordAuthentication ntlm = (NtlmPasswordAuthentication) autoLogin;
    String username = ntlm.getUsername();
    identity.setUsername( username );
    identity.setPassword("jibberish"); // trusting NTLM - not setting real password and even better if we don't
    isAuthenticated = true; // user is authenticated successfully via NTLM
    identity.addRole("admin");
    }

    return isAuthenticated;
    }

    I have also modified the generated authenticate() method so it can re-use the auto-login functionality. Note, it does not have any particular purpose in this concrete example but shows how you can re-use SSO if you login manually:

    @In Context sessionContext;

    public boolean authenticate()
    {
    boolean isAuthenticated = autoLogin();
    log.info("authenticating #0", identity.getUsername());
    if ( ! isAuthenticated ) {
    //write your authentication logic here,
    //return true if the authentication was
    //successful, false otherwise
    }
    // if we are here then the user is authenticated against NTLM or login dialog
    return true;
    }



    Furthermore, for manual form-based authentication (this is where authenticator.authenticate() is used) you can use "arbitrary user credentials from an application" as specified in the JCIFS FAQ.

  4. Now we need to make Seam to actually call our autoLogin() method when authentication is needed so that our application would have the correct identity in the session scope. This is done by making our authenticator.autoLogin() method to listen to "org.jboss.seam.notLoggedIn" event. Since the generated application already has this event registered we simply add our call to the event registration:

    <event type="org.jboss.seam.notLoggedIn">
    <action execute="#{authenticator.autoLogin}"/>
    <action execute="#{redirect.captureCurrentView}"/>
    </event>

  5. We can also protect all our pages by forcing the login in pages.xml:

    <page view-id="*" login-required="true">
    <navigation>
    <rule if-outcome="home">
    <redirect view-id="/home.xhtml"/>
    </rule>
    </navigation>
    </page>


This was the final step! To get the whole picture this is what is happening:
By forcing the login-required we are forcing the authentication in the application. Since for the first time authentication fails we we have "org.jboss.seam.notLoggedIn" event fired which will call our #{authenticator.autoLogin}. In turn autoLogin does the windows authentication and sets the user name and fake password on Identity.
Because notLoggedIn event is fired before the authentication for the page we requested fails, initializing the injected identity with the user name and password marks the identity as already logged and thus let's us reach the page without the visiting the login page.

p.s. If you authenticating against a workstation make sure you have simple file sharing switched off! This setting can be found at (Win XP) Explorer->Tools->Folder Options->View->Use simple file sharing - uncheck this checkbox!

Wednesday, September 5, 2007

Maven 2 and WebSphere - automated build and deployment of J2EE applications

After long search on different solutions on automated building and deployment of J2EE applications with maven that can work for WebSphere few posts were found. Some of them (big thanks to Peter Pilgrim) cover ways to build EJB modules that could be 'understood' by websphere (that is build websphere specific stubs and skeletons). Some posts show how to configure maven (including maven-eclipse-plugin) so that maven could work with RAD-6 and vice versa.
All we have to do is put it all together, automate deployment of EAR (remember cargo plugin doesn't support websphere yet) to websphere and hook functional tests (using selenium) into the integration test phase.
So the project consists of an ejb, war, ear, functional testing and a parent modules, like this:


 parent_project
|-ejb_module
|
|-war_module
|
|-ear_module
|
|-test_module




Looks like a complete set? Let's go through all the modules.



Saturday, March 3, 2007

No more pain in constructing collections of SelectItem's

It's been some time before I noticed something in Seam that I should have noticed long time ago... Ever experienced pain converting results of your queries into collections of Collection<SelectItem> just to show them in, for example <h:selectOneMenu>? Well this pain may be over with Seam (starting from version 1.1.5 I think) now supporting the following tag: <s:selectItems>.

What it does is it creates a collection of SelectItem (List<SelectItem>) from your List, Set, DataModel or Array. For the list of attributes refer to the reference documentation and the "ui" example of seam which is included in the Seam distribution (can be found in "examples/ui")

Wednesday, February 28, 2007

Seam 1.2 is released

It's nice to see Seam gaining more speed (especially since the JBoss Seam team has been increased). Because of the number of changes it was decided to skip the 1.1.7 release and make it 1.2 which I think is a wise decision (considering the big 'jump' in 1.1.5). More and more Seam tries gets one of the best frameworks out there closer and closer together making it look like one framework more and more with every release. Many RoR followers (and .Net actually as well) think that the enormous variety in java client-server frameworks becomes a disadvantage... well.. Seam eliminates a lot of reasons to think so - It already makes jsf/ajax4jsf/facelets look like one framework with the help of seam-gen (it's enough to have a look at the tutorial to see that). The major improvements I think are spring, ajax4jsf integration and security (full release notes can be found here). Even though the security was brought in 1.x I still think it's a major step forward. And things like (Gavin©):


entityManager.createQuery("select u from User u where u.name=#{user.name}").getResultList();


make the coding much more pleasant especially to those who did similar things in RoR.

And I would love to see the following things from my wish list implemented in some (near) future:

  • rails-like database data migrations (but less redundant)


  • rails-like test possibilities (I would love to be able to assert html as result of actions as well)