Tuesday Jun 30, 2009

Authenticating a JavaFX application using OpenSSO

At JavaONE 2009, Super Pat showed a nice JavaFX demo that used OpenSSO identity services to authenticate and authorize a user.   The demo displays a username / password dialog box to collect the user's credentials, which are sent to OpenSSO using identity services. If the user provides the correct credentials, a token is returned that represents their OpenSSO session. This token can be sent back in subsequent identity services calls to perform authorization checks. 

 I wanted to know if we could use the browser's SSO token in preference to displaying the dialog box in JavaFX. This approach has a couple of advantages:  

  • Authentication is abstracted out from our application - and is handled by OpenSSO where it belongs.   This makes it easy to modify the authentication chain. For example, we can use two factor authentication - without modifying our JavaFX code.
  • We can share the SSO session across both browser and JavaFX applications. 

The key to making this work is the Java Plugin, which provides a bridge between a Java applet (or JavaFX) and the browser. 

Here is a small JavaFX example that uses the plugin to gain access to the SSO token. The example works as follows:

  • The JavaFX applet grabs a reference to the browsers DOM using the plugin.
  • The DOM is queried for the OpenSSO cookie (iPlanetDirectoryPro).
  • If the cookie is found, the token value is sent to OpenSSO identity services to validate the session.
  • If the cookie is not found, or the OpenSSO session is not valid, the applet opens a browser window to the OpenSSO login page.  After successful authentication the user will now have a valid token in their session. 

Here is the JavaFX utility class that handles the REST identity calls to OpenSSO. We use the HttpRequest class to do the heavy lifting:

/Users/warrenstrange/src/tmp/JavaFXOpenSSOTest/src/openssotest/Util.fx
  1 /\*
  2  \* Util.fx
  3  \*
  4  \* Created on Jun 18, 2009, 5:09:55 PM
  5  \*/
  6 
  7 package openssotest;
  8 
  9 import javafx.io.http.HttpRequest;
 10 import javafx.io.http.HttpHeader;
 11 
 12 
 13 /\*\*
 14  \* Utility functions to support OpenSSO integration
 15  \*
 16  \* @author warrenstrange
 17  \*/
 18 
 19 
 20 // todo: We should get this from opensso
 21 def cookieName = "iPlanetDirectoryPro";
 22 
 23 
 24 /\*\*
 25  \* Parse the OpenSSO token from the cookie string
 26  \*/
 27 
 28 public function getTokenFromString(cookies:String) {
 29     var token = null;
 30 
 31     if( cookies != null ) {
 32         var start = cookies.indexOf(cookieName);
 33         if( start > 0 ) {
 34             var ts = start + cookieName.length()+1;
 35             var te = cookies.indexOf(";", ts);
 36             if( te < 0) {
 37                 token = cookies.substring(ts);
 38             }
 39             else
 40                 token = cookies.substring(ts,te);
 41         }
 42     }
 43     return token;
 44 }
 45 
 46 /\*\*
 47  \* Authenticate using OpenSSO identity services (/identity)
 48  \* We pass in the SSO token we get from the browser.
 49  \*
 50  \* Note this is an async process. We start the request and return it to the caller.
 51  \* When the request is complete the setAuth callback function will be
 52  \* called. This function will be passed in a boolean indicating if the
 53  \* user has been authenticated or not.
 54  \*/
 55  
 56 public function authenticate(token: String, setAuth: function(isAuth:Boolean):Void): HttpRequest {
 57    
 58     var request: HttpRequest = HttpRequest {
 59         location: "http://opensso.my2do.com:8080/opensso/identity/isTokenValid"
 60         method: HttpRequest.POST
 61 
 62         headers: [
 63             HttpHeader {
 64                 name: HttpHeader.CONTENT_TYPE;
 65                 value: "application/x-www-form-urlencoded";
 66             },
 67             HttpHeader {
 68                 name: HttpHeader.CONTENT_LENGTH;
 69                 value: "0";
 70             }
 71 
 72             HttpHeader {
 73                 name: "Cookie";
 74                 value: "{cookieName}={token}";
 75             }
 76         ];
 77      
 78         onInput: function(is: java.io.InputStream) {
 79             // grab response from opensso
 80             try {
 81                 var conv = new StreamConverter(is);
 82                 var s = conv.convertStreamToString();
 83                 println("Response back from opensso={s}");
 84                 if( s != null and s.indexOf("true") > 0) {
 85                     setAuth(true);
 86                 }
 87                 else
 88                     setAuth(false);
 89             } finally {
 90                 is.close();
 91             }
 92         }
 93 
 94         onException: function(ex: java.lang.Exception) {
 95             println("onException - exception: {ex.getClass()} {ex.getMessage()}");
 96         }
 97     };
 98     
 99     request.start();
100 
101     return request;
102 
103 }
104 
105 
106 

The Main.fx panel shows the SSO Token (if present), and the users authentication status.

 

Apologies for the lame GUI!

 

/Users/warrenstrange/src/tmp/JavaFXOpenSSOTest/src/openssotest/Main.fx
  1 /\*
  2  \* Main.fx
  3  \*
  4  \* Created on Jun 16, 2009, 11:45:58 AM
  5  \*/
  6 
  7 package openssotest;
  8 
  9 import javafx.stage.Stage;
 10 import javafx.scene.Scene;
 11 import javafx.scene.text.Text;
 12 
 13 import com.sun.javafx.runtime.adapter.Applet;
 14 
 15 import javafx.geometry.HPos;
 16 import javafx.scene.layout.Flow;
 17 import javafx.scene.control.Button;
 18 import javafx.io.http.HttpRequest;
 19 
 20 
 21 import java.net.URL;
 22 
 23 
 24 
 25 /\*\*
 26  \* Sample JavaFX application that authenticate the user using OpenSSO.
 27  \* The user's browswer is quried for the SSO token. If it is not found a new
 28  \* browwer window is opened up to the OpenSSO login page.
 29  \*
 30  \* @author warrenstrange
 31  \*/
 32 
 33 var applet: Applet = FX.getArgument("javafx.applet") as Applet;
 34 var window = netscape.javascript.JSObject.getWindow(applet);
 35 var document : org.w3c.dom.html.HTMLDocument = DOMHelper.getDocument(applet);
 36 
 37 var cookies = document.getCookie();
 38 var token = Util.getTokenFromString(cookies);
 39 
 40 
 41 var prequest: HttpRequest; // holds state of authenticate async REST call
 42 
 43 def width = 800;
 44 def height = 600;
 45 
 46 var authenticated = false;
 47 
 48 
 49 var url =new URL("http://opensso.my2do.com:8080/opensso");
 50 
 51 Stage {
 52     title: "JavaFX / OpenSSO Test"
 53     width: width
 54     height: height
 55     scene: Scene {
 56         content: [
 57 
 58             Flow {
 59                 vertical: true
 60                 height: 300 // columns will wrap at 300
 61                 hgap: 5 // horizontal gap between columns
 62                 vgap: 5 // vertical gap between nodes in a column
 63                 nodeHPos: HPos.LEFT // each node will be left-aligned within its column
 64                 content: [
 65 
 66                 Text {
 67                     content: bind if( token != null ) {
 68                             "SSOToken = {token}"
 69                         } 
 70                         else "No Token";
 71 
 72                 },
 73                 Text {
 74                     content: bind if (authenticated )
 75                         { "Authenticated!" } else "Not Authenticated";
 76 
 77                 },
 78 
 79                 Button {
 80                     text: "Authenticate Now!";
 81                     action: function() {
 82                         cookies = document.getCookie();
 83                         token = Util.getTokenFromString(cookies);
 84                         println ("Authenticate called with {token}");
 85 
 86                         // call authenticate identity service
 87                         // when complete, it will call our anon function
 88                         // if we are NOT authenticated, a new browser window
 89                         // will be launched pointing the user at the opensso
 90                         // login page
 91                         prequest = Util.authenticate(token,
 92                             function(isAuthenticated:Boolean):Void {
 93                                 authenticated = isAuthenticated;
 94                                 if( not authenticated ) {
 95                                     var c = applet.getAppletContext();
 96                                     c.showDocument(url, "_blank");                              
 97                                 }
 98                             } );
 99                         println ("Authenticate returned");
100                     }
101                 }
102 
103                 ]
104             }
105             ]
106     }
107 }
108 

 

You can download the complete netbeans project here.

 

Friday Jun 12, 2009

OpenSSO and the Quest for the Holy Grails

grailquest

With apologies to Monty Python Fans....

Did you ever want to use OpenSSO with Grails? Now you can!

The astute reader of this blog (that's you Mom!) will know that you can protect Spring applications with the OpenSSO Spring 2 Security provider

As it turns out, Grails has a plugin that let's you use Spring Security for authentication and authorization. If we marry this plugin with the provider, we ought to be able to use OpenSSO with Grails. Don't ya love it when a plan comes together?

To get started, you will need to download the OpenSSO Grails plugin from here, and install it using the grails install-plugin command. 

To give credit where credit is due, the OpenSSO plugin is largely based on the work of the Spring Security Grails plugin. The original authors are T.Yamamoto, Hatian Sun, and Burt Beckwith. My humble contribution is the OpenSSO glue code and the integration of the OpenSSO Spring Security extension. For reasons that I won't go into here, I elected to create a separate version of the plugin specifically for OpenSSO (ping me if you want the details).

I would also suggest downloading this sample Grails application that demonstrates how to use the plugin in a Grails Application.

 Yes. But what does it do?

 The OpenSSO plugin provides the following functionality to a Grails Application:

Single Sign On (SSO) with OpenSSO:

The plugin delegates logon to OpenSSO. If the user has previously authenticated to OpenSSO, the browser will present a cookie containing the SSO token. Providing the session is still valid, the user will be transparently signed on the Grails application.  If the user does not have a token, the plugin redirects them to OpenSSO. After succesfull authentication, the user is then redirected back to the application.  

This means that your application is not responsible for authentication. In fact, there are no logon or password screens to maintain as OpenSSO handles it for you. One of the nice benefits of this approach is that the authentication method and strength is factored out of the application. Want to use one time passwords? How about AD?  No problem - just configure the authentication chain in OpenSSO. No changes to your application.  

URL Policy Enforcement

 The plugin provides for enforcement of URL policy using OpenSSO.  This works quite nicely with Grails and it's "REST" like structure for controller URLs. So we can (for example), allow one group of users to /list controller items, and another group of users to /update or /create new items. Custom controller methods (beyond the standard CRUD methods) can use the same mechanism. 

Note that this largely eliminates the need for the @Secured annotations in Grails code - since the same effect can be implemented using URL policy. This externalizes the authorization into OpenSSO - which is generally a good thing. 

Controller Security Methods  

 The plugin injects several security methods into your controllers to provide access to the security context. Here is sampling of methods available to controllers:

isUserInRole("ROLE_MANAGER")   - true if a user belongs to the specified Role
isUserLogon()	 -  true if the user is logged on (authenticated)
getGrantedAuthorities() - returns an array of Strings representing the Granted Authorities (Spring terminology for role names) that this user posseses.

GSP Tags

The plugin provides access to GSP security tags. These tags can be used in your Grails view to drive the display based on the user's role or authentication status.

For example:

<g:ifNotGranted role="ROLE_MANAGERS">You are not a Manager!!! </g:ifNotGranted>

<g:ifAllGranted role="ROLE_MANAGERS">Congrats. You are a manager!</g:ifAllGranted>
Time permitting, I may create a cookbook on how to get this all working with the sample application. Drop me a line if you are interested.

PS. Just what is the wing speed velocity of swallow?  

Tuesday May 05, 2009

An OpenSSO Lab using VirtualBox

opensso logo

As part of the Reboot conference in Victoria, I developed a hands on OpenSSO lab using VirtualBox.  The virtual image is running OpenSolaris 2009.06 (dev), GlassFish v3, and OpenSSO (express build 7).  

The goal of the lab is to provide a short (it should take you 4-5 hours to complete) introduction to OpenSSO that covers basic installation, SAML federation using Fedlets, and installing Policy Agents.  

The intended audience for the lab are developers or those that want to understand the functional aspects of OpenSSO. David Goldsmith has developed a much more comprehensive OpenSSO lab that focuses on complex, load balanced configurations. If you are more interested in the infrastructure side of things, check out his blog posting on the lab.  David is a training pro - and his stuff is always top notch. 

This lab is meant to be facilitated, but if you want to follow along at home,  read on:

  • Make sure you have a computer with a MINIMUM of 2GB of RAM, 10GB free disk. Dual core CPU is strongly recommended. 
  • Download and install VirtualBox 2.2.2. or later (earlier versions are not compatible with this lab)
  • You should have an ssh client (e.g. putty.exe on Windows) installed.

The lab documents (zipped pdf) can be downloaded here.

The Virtual Image is split into four zip fragments:

http://mediacast.sun.com/users/WarrenStrange/media/xaa.zip

These fragments needs to be pasted together, and then unzipped.

On Unix-ish systems:  

cat xa[abcd].zip > bigfile.zip

On Windows use can use the copy command:

copy /b xaa.zip+xab.zip+xac.zip+xad.zip   bigfile.zip

Unzip the file, and then import the appliance into VirtualBox.

The image is a \*completed\* version of the lab (everything is installed). The idea is to give you a known starting point. If you want to experiment with the lab, start with the uninstall procedures first. You can take a VirtualBox snapshot to save the lab state, or use ZFS snapshots from within the OpenSolaris image.  

 To save some memory, the OpenSolaris GUI has been disabled. You can enable the GUI with the following command:

pfexec svcadm enable gdm  

Note:  When importing the appliance you may encounter a bug where the import "hangs".  After letting the disk fully import, you will need to kill VirtualBox and start it again. You will find that the imported disk image is now in the Media Manager - and can be added to the new Virtual machine. You should be able to boot the image after assigning the disk. 

Important Update: 

Running a Solaris 64 bit kernel under VirtualBox can occasionally trigger very high CPU consumption- making the startup of OpenSSO take a long time (10+ minutes). See this bugid for details.  To get around this problem boot the image in 32 bit mode. The easiest way to do this is to change your VirtualBox settings to select "OpenSolaris" as the OS Type instead of "OpenSolaris 64 bit". 


Thursday Feb 26, 2009

Spring comes early - an updated OpenSSO Spring Security provider


Following on the pioneering work of Robert Dale  and Miguel Alonso I have updated the OpenSSO Spring provider with additional support for authorization. You can now use Spring security JSP tags, method security annotations and Spring method security point cuts.

Where to get it!

You can download the provider and a sample Spring application from the OpenSSO Extensions project page.


The package-info header is reproduced below:

Package com.sun.identity.provider.springsecurity Description

A Spring 2 Security provider for OpenSSO.

Provides authentication and authorization plugins for the Spring 2 Security framework. For an example of how to configure this module refer to the OpenSSO / Spring example

Authentication

The provider delegates authentication to the OpenSSO instance configured in the applications AMConfig.properties. When a user tries to access an application web page, the spring provider will check for a valid SSOToken. If the user is not authenticated they will be redirected to OpenSSO. Once authentication is complete, OpenSSO will redirect the user back to the application.

Upon authentication, a Spring UserDetails object is created for the user and placed in the session. This can be used by the application to query for the user principal and other information. The spring security authentication tags can be used within a JSP, as shown in the following example:

  The Logged on Principal is <security:authentication property="principal.username"/>
  
 

Authorization - Web URL Policy

The provider delegates URL policy decisions to OpenSSO. This is different than most Spring 2 providers where the URL policy is configured in the application using annotations or spring XML configuration.

OpenSSO is queried for URL policy decisions, and will return ALLOW, DENY or null. A null return means that OpenSSO does not have a policy for the requested URL. The provider will return an ABSTAIN vote if the OpenSSO policy decision is null. If you wish to implement a policy of "Deny that which is not explicity permitted" you will want to use Springs AffirmativeBased voter in your security configuration. This ensures that at least one voter must "ALLOW" the request.

Authorization - Roles

Spring Security uses the concept of GrantedAuthorities which are analagous to roles in OpenSSO. This provider converts OpenSSO group (role) membership into Spring GrantedAuthorities. The current implementation converts an OpenSSO group membership (for example "staff") into a GrantedAuthority by concatenating the prefix "ROLE_" with the upper cased group name. For example, if a user belongs to the OpenSSO groups "staff" and "admins", they will be granted "ROLE_STAFF" and "ROLE_ADMINS" authorizations.

Authorizations can be used in JSPs using the Spring security tags. For example, the following JSP snippet will output different results depending on whether the user belongs to the staff group or not:

<security:authorize ifAllGranted="ROLE_STAFF">
    <div align="left"><h2>Congrats!! You have the Staff role</h2></div>
</security:authorize>

<security:authorize ifNotGranted="ROLE_STAFF">
    <div align="left"><h2>TOO BAD SO SAD - You do NOT have the Staff role</h2></div>
</security:authorize>
 
 

Authorizations can also be used to protect methods using Spring pointcuts or annotations. The example below demonstrates using JSR security annotations:

@RolesAllowed("ROLE_ADMIN") public String sayHello() { return "Hello" }

The above method will throw a Spring Security AccessException if the user is not in the admin group.


Monday Feb 18, 2008

Getting Groovy with OpenSSO REST Services



Recent builds of OpenSSO now include WS-\* and REST identity services. Check out Marina Sum's blog for an excellent overview.


Now it turns out that Groovy is a great way to explore REST services. Here is an example using OpenSSO (if you are trying this out at home change your base URL to reflect your OpenSSO installation).



def baseurl = "http://localhost:28080/opensso/identity/"

// Create and execute REST query string
def query = { String action, Map args ->
def s = baseurl + action;
def sep = "?"
args.each { key,value ->
def encoded = java.net.URLEncoder.encode(value,"UTF-8")
s += "${sep}${key}=${encoded}"
sep = "&"
}
return s.toURL().text
}

// authenticate to OpenSSO
def authenticate = { username, password ->
String s = query("authenticate", [ username:username, password:password] )
int i = s.indexOf('=')
if( i >= 0 )
return s.substring(i+1).trim()
else
return s;
}

// fetch the users attributes. Must supply the token returned by authenticate
def getAttributes = { token -> query("attributes", [subjectid:token]) }

// Test to see if the user identified by token is allowed to access the URI
def authorize = { token, uri, action = "GET" -> query("authorize", [subjectid:token, uri:uri, action:action]) }

def token = authenticate("user1", "password")

println getAttributes(token)
println authorize(token, "http://localhost:8080/protected")





As Mr. Powers would say, Groovy Baby"
About

Things that amuse me

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today
News

No bookmarks in folder

Blogroll