java.security.PrivilegedAction
TopsecurityLoginModule.java
... basicly as for authentication onlyTopsecurityCallbackHandler.java
... basicly as for authentication onlyTopsecurityPrincipal.java
... basicly as for authentication onlyTopsecurityAction.java
- new!TopsecurityClientAuthorization.java
- to initiate the actual JAAS
login and call a .doAsPrivileged
or .doAs
method
on the client Subject
.Subject
, priviledged 'Action' class on the server.LoginContext
, a Subject
is
retrieved. It's .doAsPrivileged
method is called - with
the priviledged Action
code you want to run. Then JAAS takes
over, checks user privileges with the policy file, throws exception if insufficient.
Then the .run() method is executes - constantly checking code privileges in the
policy file.
TopsecurityClientAuthorization.java
package dk.topsecurity; import java.io.*; import java.util.*; import java.security.*; import javax.security.auth.login.*; import javax.security.auth.*; import javax.security.auth.callback.*; import javax.security.auth.spi.*; /** * <p> This demonstrates use of the JAAS API, by attempting to * authenticate and then authorize a user. Results of the security * check are then printed out on the commandline. * </p> */ public class TopsecurityClientAuthorization { /** * Interacts with user via commandline and attempts to * authenticate the user and authorize them to perform * a priveleged action.<p> * * @param args Commandline input arguments (there are none). */ public static void main(String[] args) { LoginContext logctx = null; //1ST PART - ONLY ASSOCIATED WITH AUTHENTICATION try { /* Constructor for LoginContext is intialized with a string containing * classname for the LoginModule implementation as specified in the JAAS * login configuration file. Second parameter is an object of type * CallbackHandler, which will actually handle the authentication attempts. */ logctx = new LoginContext( "TopsecurityJAASModule", new TopsecurityCallbackHandler()); } catch( LoginException le ) { System.err.println("\t[TopsecurityClient] LoginContext creation failed. "+ le.getMessage()); System.exit( -1 ); } catch( SecurityException se ) { System.err.println("\t[TopsecurityClient] LoginContext creation failed. "+ se.getMessage()); System.exit(-1); }//end try/catch try { logctx.login(); // Attempts to authenticate user and succeesfull if no exception is thrown System.out.println("\t[TopsecurityClient] Authentication succeeded."); } catch( LoginException le ) { System.err.println("\t[TopsecurityClient] Authentication failed. " + le.getMessage()); System.exit( -1 ); }//end try/catch |
//2ND PART - ONLY ASSOCIATED WITH AUTHORISATION try { Subject subj = logctx.getSubject(); // Extract the Subject TopsecurityPrincipal.setDebug( false ); // not interested in what Principal is created PrivilegedAction action = new TopsecurityAction(); // Try executing TopsecurityAction as the authenticated Subject Subject.doAsPrivileged( subj, action, null ); // Go! System.out.println( "\t[TopsecurityClient] Authorization succeeded\n"); //3RD PART - DOING A GRACEFULL LOGOUT try { logctx.logout(); } catch(LoginException le) { System.err.println("\t[TopsecurityClient] Logout: " + le.getMessage()); } } catch( Exception ex ) { System.err.println("\t[TopsecurityClient] Authorization failed: " + ex.getMessage() + "\n" ); }//end try/catch |
}//end main() }//end main class |
Subject
and
need to implementing the interface java.security.PrivilegedAction
.
Then its .run method is executed. The class used here is listed below:
TopsecurityAction.java
package dk.topsecurity; import java.io.File; import java.security.PrivilegedAction; /** * <p>The class implements PrivilegedAction, which requires authentication * and authorisation through JAAS to be executed. Also the actions * <code>System.getProprety</code> and file operations require a security * manager with proper security policy file definitions to be executed.</p> */ public class TopsecurityAction implements PrivilegedAction { /** * Privileged method */ public Object run() { System.out.println( "Authorization successfull.\n\n" + "Classpath property value is: " + System.getProperty("java.class.path" ) ); File file = new File( "topsecurity.txt" ); System.out.print( "\ntopsecurity.txt does " ); if( !file.exists() ) System.out.print( "not " ); System.out.println( "exist in the current working directory.\n" ); return null; }//end Object run() }//end class TopsecurityAction |
System.getProperty
and File
operations.Topsecurity.policy
/* Java 2 Access Control Policy for the JAAS Application */ /* grant the sample LoginModule permissions */ grant codebase "file:./topsecuritymodule.jar" { permission javax.security.auth.AuthPermission "modifyPrincipals"; }; grant codebase "file:./topsecurityauthorization.jar" { permission javax.security.auth.AuthPermission "createLoginContext.TopsecurityJAASModule"; permission javax.security.auth.AuthPermission "doAsPrivileged"; }; grant codebase "file:./topsecurityauthentication.jar" { permission javax.security.auth.AuthPermission "createLoginContext.TopsecurityJAASModule"; permission javax.security.auth.AuthPermission "doAsPrivileged"; }; /* User-Based Access Control Policy for the TopsecurityAction class * instantiated by TopsecurityClientAuthorization */ grant codebase "file:./topsecurityaction.jar", Principal dk.topsecurity.TopsecurityPrincipal "user1" { permission java.util.PropertyPermission "java.class.path", "read"; permission java.io.FilePermission "topsecurity.txt", "read"; }; |
TopsecurityCallbackHandler.java
,
TopsecurityLoginModule.java
, TopsecurityPrincipal.java
which are just listed for completeness. Not much happening here, which was not
covered during the authentication example. The CallbackHandler
just handles an additional credential...TopsecurityCallbackHandler.java
package dk.topsecurity; import java.io.*; import java.util.*; import javax.security.auth.login.*; import javax.security.auth.*; import javax.security.auth.callback.*; /** * <p>This class implements the CallbackHandler so that it can handle the JAAS * login callback methods.</p> * */ class TopsecurityCallbackHandler implements CallbackHandler { /** * Processes an array of callbacks to handle the login process.<p> * * @param callbacks an array of <code>Callback</code> objects containing * the information requested by an underlying security * service to be retrieved or displayed. * @exception java.io.IOException if an input or output error occurs. <p> * @exception UnsupportedCallbackException if the implementation of this * method does not support one or more of the Callbacks * specified in the <code>callbacks</code> parameter. * * </p> */ public void handle( Callback[] callbacks ) throws IOException, UnsupportedCallbackException { for( int i = 0; i < callbacks.length; i++ ) { if( callbacks[i] instanceof NameCallback ) { NameCallback nc = (NameCallback)callbacks[i]; System.err.print( nc.getPrompt() ); // prompt the user for a username System.err.flush(); nc.setName( (new BufferedReader (new InputStreamReader(System.in))).readLine() ); } else if( callbacks[i] instanceof PasswordCallback ) { PasswordCallback pc = (PasswordCallback)callbacks[i]; System.err.print(pc.getPrompt()); // prompt the user for sensitive information System.err.flush(); String pw = (new BufferedReader (new InputStreamReader(System.in))).readLine(); char[] passwd = new char[pw.length()]; pw.getChars(0, passwd.length, passwd, 0); pc.setPassword( passwd ); } else { throw new UnsupportedCallbackException ( callbacks[i], "Unrecognized Callback" ); }//end if/else }//end for() }//handle() }//end class TopsecurityCallbackHandler |
TopsecurityLoginModule.java
. Not much new here either.
TopsecurityLoginModule.java
package dk.topsecurity; import java.util.*; import java.io.IOException; import javax.security.auth.*; import javax.security.auth.callback.*; import javax.security.auth.login.*; import javax.security.auth.spi.*; /** * <p> LoginModule authenticates users, using a password. * * <p> Includes a sequence (arrays) of users and their passwords. Indexes in the * arrays are the same for corresponding user and password. * * <p> On successfull authentication, a <code>TopsecurityPrincipal</code> object * with the user name is added to the Subject. */ public class TopsecurityLoginModule implements LoginModule { /** * <p> LoginModule recognizes the debug option (in the 'initialize' method). * If initialized true in the login Configuration, debug will be output to System.out. */ private boolean debug = false; // validation objects private Subject subject; private TopsecurityPrincipal entity; private CallbackHandler callbackHandler; private Map sharedState; private Map options; // tracking authentication status private static final int NOT = 0, OK = 1, COMMIT = 2; private int status; // login info private static final String[] userNames = { "guest", "user1", "user2" }; private static final String[] passwords = { "guest", "pass1", "pass2" }; // current user private String username; private char[] password; /** * Initializes the LoginModule.<p> * * @param subject the Subject to be authenticated as provided through the JAAS interface. <p> * @param callbackHandler a CallbackHandler for retrieving username and password from the user <p> * @param sharedState shared LoginModule state. <p> * @param options options specified in the login Configuration for this particular LoginModule * (in the java.security.auth.login.config file). */ public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { status = NOT; this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = sharedState; this.options = options; // sets options according to the java.security.auth.login.config file TopsecurityPrincipal.setDebug( "true".equalsIgnoreCase((String)options.get("debug")) ); }//end initialize() /** * Simple console trace to system.out for debug purposes only. * <p> * * @param msg the message to be printed to the console * */ private void debugOut(String msg) { if( debug ) System.out.println("\t[TopsecurityLoginModule] " + msg); } /** * Authenticate the user based on a username and password. * If the combination of user name/password occurs anywhere in the list, * authentication succeeds - otherwise not. * * <p> * * @return true in all cases since this LoginModule should not be ignored. * @exception FailedLoginException if authentication fails. <p> * @exception LoginException if this LoginModule is unable to perform the authentication. */ public boolean login() throws LoginException { // prompt for a user name and password if( callbackHandler == null ) throw new LoginException( "Error: no CallbackHandler available to retrieve user credentials"); Callback[] callbacks = new Callback[2]; callbacks[0] = new NameCallback( "\nuser name: " ); callbacks[1] = new PasswordCallback( "password: ", false ); try { callbackHandler.handle(callbacks); //get the user credentials username = ((NameCallback)callbacks[0]).getName(); char[] tmpPassword = ((PasswordCallback)callbacks[1]).getPassword(); if (tmpPassword == null) // treat a NULL password as an empty password tmpPassword = new char[0]; password = new char[tmpPassword.length]; System.arraycopy( tmpPassword, 0,password, 0, tmpPassword.length ); ((PasswordCallback)callbacks[1]).clearPassword(); //wipe out occurrences in memory } catch( java.io.IOException ioe ) { throw new LoginException(ioe.toString()); } catch( UnsupportedCallbackException uce ) { throw new LoginException( "Error: " + uce.getCallback().toString() + " not available to authenticate user." ); }//end try/catch // verify the username/password String passwordString = new String( password ); debugOut("user entered user name: " + username ); debugOut("user entered password: " + passwordString + "\n"); for( int i = 0; i < userNames.length; i++ ) { if( username.equals( userNames[i] ) && passwordString.equals( passwords[i] ) ) { // because the username/passwords matches in list, authentication succeeded!!! debugOut("User #" + i + " authentication succeeded." ); status = OK; return true; } else { // authentication failed -- clean out state debugOut("User #" + i + " authentication failed." ); }//end if/else }//end for(;;) return false; }//end login() /** * <p> Method called if the LoginContext's * overall authentication succeeded (the relevant REQUIRED, * REQUISITE, SUFFICIENT and OPTIONAL LoginModules * mentioned in file java.security.auth.login.config.. did succeed). * * <p> If this LoginModule's authentication succeeded * (status stored in the variable 'status' by the * .login() method), then the .commit() method associates a * TopsecurityPrincipal with the Subject located in the * LoginModule. If this LoginModule's authentication failed, * any state originally saved is removed. * * <p> * * @exception LoginException if the commit fails. * @return true if this LoginModule's own .login() and .commit() * attempts succeeded, false otherwise. */ public boolean commit() throws LoginException { if(status == NOT || subject == null) { return false; } else { // add a Principal (authenticated identity) to the Subject // assume the user we authenticated is the TopsecurityPrincipal entity = new TopsecurityPrincipal(username); Set entities = subject.getPrincipals(); if( !entities.contains(entity) ) entities.add(entity); debugOut("added TopsecurityPrincipal to Subject"); // in any case, clean out state username = null; password = null; status = COMMIT; return true; }//end if }//end commit() /** * <p> This method is called if the LoginContext's * overall authentication failed. * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules * mentioned in file java.security.auth.login.config.. did not succeed). * * <p> If this LoginModule's authentication succeeded * (status stored in the variable 'status' by the * .login() method and .commit() methods), * then the .abort() method cleans up any state that was originally saved. * * <p> * * @exception LoginException if the abort fails. * @return false if this LoginModule's own login and/or commit attempts * failed, true otherwise. */ public boolean abort() throws LoginException { if(status == NOT) { return false; } else if( status == OK ) { // login succeeded but overall authentication failed username = null; if( password != null ) password = null; entity = null; } else { // overall authentication succeeded and commit succeeded, // but someone else's commit failed logout(); }//end if/else status = NOT; return true; }//end abort() /** * Logout of the user. * * <p>Removes the TopsecurityPrincipal added by the .commit() method.<p> * * @exception LoginException if the logout fails. * @return true in all cases since this LoginModule should not be ignored. */ public boolean logout() throws LoginException { subject.getPrincipals().remove(entity); status = NOT; username = null; if( password != null ) password = null; entity = null; return true; }//end logout() }//end class TopsecurityLoginModule |
TopsecurityPrincipal.java
package dk.topsecurity; import java.security.Principal; /** * <p> TopsecurityPrincipal class implements the * java.security.Principal interface and represents a user, who is * successfully authenticated in the LoginModule.. * * <p> Principals such as this <code>TopsecurityPrincipal</code> * may be associated with a particular <code>Subject</code> * to augment that <code>Subject</code> with an additional * identity. Authorization decisions can then be based upon * the Principals associated with a <code>Subject</code>. * * @see java.security.Principal * @see javax.security.auth.Subject */ public class TopsecurityPrincipal implements Principal, java.io.Serializable { /** * Debug Flag */ private static boolean DEBUG; /** * The name of the principal */ private String name; /** * Create TopsecurityPrincipal representing user with username provided.<p> * * @param name the username for this user. * @exception NullPointerException if the <code>name</code> is <code>null</code>. */ public TopsecurityPrincipal( String name ) { if( name == null ) throw new NullPointerException( "illegal null input" ); this.name = name; if( DEBUG ) System.out.println( "\t[TopsecurityPrincipal] Principal " + name + " successfully created." ); }//end TopsecurityPrincipal( String ) /** * Returns username for this <code>TopsecurityPrincipal</code>.<p> * * @return the username for this <code>TopsecurityPrincipal</code> */ public String getName() { return name; }//end getName() /** * Toggles debug status on or off.<p> * * @param debug flag to set the debug status */ public static void setDebug( boolean debug ) { DEBUG = debug; }//end setDebug( boolean ) /** * Return a string representation of this <code>TopsecurityPrincipal</code>.<p> * * @return a string representation of this <code>TopsecurityPrincipal</code>. */ public String toString() { return( "TopsecurityPrincipal: " + name ); }//end toString() /** * Compares the specified Object with this <code>TopsecurityPrincipal</code>. * True if the given object is also a <code>TopsecurityPrincipal</code> * with the same username.<p> * * @param o Object to be compared for equality with .this * @return true if the specified Object is equal equal to .this */ public boolean equals( Object o ) { if( o == null ) return false; if( this == o ) return true; if( !(o instanceof TopsecurityPrincipal) ) return false; TopsecurityPrincipal that = (TopsecurityPrincipal)o; if( this.getName().equals(that.getName()) ) return true; return false; }//end equals() /** * Return a hash code for this <code>TopsecurityPrincipal</code>.<p> * * @return a hash code for this <code>TopsecurityPrincipal</code>. */ public int hashCode() { return name.hashCode(); }//end hashCode() }//end class TopsecurityPrincipal |
TopsecurityJAASModule
, which is the name that
TopsecurityClient.java
uses to refer to this entry. The entry
specifies that the LoginModule should be used to perform the authentication.
This module is required in order for the authentication to be considered
successful. It is successful if the user enters correct credentials according
to what is expected in the LoginModule
.
TopsecurityLogin.config
/** Login Configuration File for JAAS Sample App **/ TopsecurityJAASModule { dk.topsecurity.TopsecurityLoginModule required debug=true; }; |
build.xml
<project name="javacert" default="all" basedir=".\"> <target name="clean"> <delete quiet="true" dir=".\build"/> <delete quiet="true" dir=".\javadoc"/> <mkdir dir="./build"/> </target> <target name="compile"> <javac srcdir="./source" destdir=".\build"/> </target> <target name="package"> <jar destfile="./topsecurityaction.jar" basedir="./build" includes="dk/topsecurity/TopsecurityAction.class"/> <jar destfile="./topsecuritymodule.jar" basedir="./build" includes="dk/topsecurity/TopsecurityLoginModule.class dk/topsecurity/TopsecurityPrincipal.class dk/topsecurity/TopsecurityCallbackHandler.class"/> <jar destfile="./topsecurityauthorization.jar" basedir="./build" includes="dk/topsecurity/TopsecurityClientAuthorization.class"/> <jar destfile="./topsecurityauthentication.jar" basedir="./build" includes="dk/topsecurity/TopsecurityClientAuthentication.class"/> </target> <target name="all" depends="clean,compile,package"> </target> </project> |