Example: JAAS SampleThreadSubjectLogin

This example shows you the implementation of the SampleThreadSubjectLogin class.

Note: By using the code examples, you agree to the terms of the Code license and disclaimer information.
//////////////////////////////////////////////////////////////////////
//
// 5722-JV1
// (C) Copyright IBM Corp. 2000
//
//////////////////////////////////////////////////////////////////////
//
// File Name:   SampleThreadSubjectLogin.java
//
// Class:       SampleThreadSubjectLogin
//
/////////////////////////////////////////////////////////////////////
//
// CHANGE ACTIVITY:
//
//
// END CHANGE ACTIVITY
//
////////////////////////////////////////////////////////////////////

import com.ibm.security.auth.ThreadSubject;

import com.ibm.as400.access.*;

import java.io.*;

import java.util.*;

import java.security.Principal;

import javax.security.auth.*;

import javax.security.auth.callback.*;

import javax.security.auth.login.*;


/**
 * This SampleThreadSubjectLogin application authenticates a single
 * user, swaps the OS thread identity to the authenticated user,
 * and then writes "Hello World" into a privately authorized
 * file, thread.txt, in the user's test directory. 
 *
 * The user is requested to enter the user id and password to
 * authenticate.
 *
 * If successful, the user name and number of Credentials
 * are displayed.
 *
 *

 Setup and run instructions:

 1) Create a new user, JAAS13, by invoking
 "CRTUSRPRF USRPRF(JAAS13) PASSWORD() TEXT('JAAS sample user id')"
 with *USER class authority.

 2) Allocate a dummy test file, "yourTestDir/thread.txt", and
 privately grant JAAS13 *RWX authority to it for write access.
 
 3) Copy SampleThreadSubjectLogin.java into your test directory.

 4) Change the current directory to your test directory and compile the
 java source code.

   Enter -
 
   strqsh
 
   cd 'yourTestDir'

   javac -J-Djava.version=1.3
     -classpath /qibm/proddata/os400/java400/ext/jaas13.jar:
                /QIBM/ProdData/HTTP/Public/jt400/lib/jt400.jar:.
     -d ./classes
     *.java

 5) Copy threadLogin.config, threadJaas.policy, and threadJava2.policy
 into your test directory.
  
 6) If not already done, add the symbolic link to the extension
 directory for the jaas13.jar file.
 The extension class loader should normally load the JAR file.    
 
 ADDLNK OBJ('/QIBM/ProdData/OS400/Java400/ext/jaas13.jar')
   NEWLNK('/QIBM/ProdData/Java400/jdk13/lib/ext/jaas13.jar')
 

 7) If not already done to run this sample, add the symbolic link to the extension
 directory for the jt400.jar and jt400ntv.jar files. This causes these
 files to be loaded by the extension class loader. The application class loader
 can also load these files by including them in the CLASSPATH.  
 If these files are loaded from the class path directory, 
 do not add the symbolic link to the extension directory. 
 The jaas13.jar file requires these JAR files for the credential
 implementation classes which are part of the IBM Toolbox
 for Java (5722-JC1) Licensed Program Product.  
 (See the IBM Toolbox for Java topic for documentation
 on the credential classes found in the left frame
 under Security Classes => Authentication. Select the link to the
 ProfileTokenCredential class. At the top select 'This Package' for the
 entire com/ibm/as400/security/auth Java package. Javadoc for the
 authentication classes can also be found by selecting 'Javadoc' =>
 'Access Classes' on the left frame. Select 'All Packages' at the top
 and look for the com.ibm.as400.security.* packages)
  
  
 ADDLNK OBJ('/QIBM/ProdData/HTTP/Public/jt400/lib/jt400.jar')
   NEWLNK('/QIBM/ProdData/Java400/jdk13/lib/ext/jt400.jar')
 
 ADDLNK OBJ('/QIBM/ProdData/OS400/jt400/lib/jt400Native.jar')
   NEWLNK('/QIBM/ProdData/Java400/jdk13/lib/ext/jt400Native.jar')

/////////////////////////////////////
 IMPORTANT NOTES -
/////////////////////////////////////

 When updating the Java2 policy files
 for a real application remember to grant the 
 appropriate permissions to the actual locations of the IBM Toolbox
 for Java JAR files. Even though they are symbolically linked to
 the extension directories previously listed which are granted
 java.security.AllPermission in the
 ${java.home}/lib/security/java.policy file, authorization is based on 
 the actual location of the JAR files.

 For example, to successfully use the credential classes
 in IBM Toolbox for Java, you would add the below to your application's
 Java2 policy file -
 
 grant  codeBase "file:/QIBM/ProdData/HTTP/Public/jt400/lib/jt400.jar"
 {
   permission javax.security.auth.AuthPermission "modifyThreadIdentity";
   permission java.lang.RuntimePermission "loadLibrary.*";
   permission java.lang.RuntimePermission "writeFileDescriptor";
   permission java.lang.RuntimePermission "readFileDescriptor";
 }

 You also need to add these permissions for the application's 
 codeBase since the operations performed by the IBM Toolbox
 for Java JAR files do not run in privileged mode.

 This sample already grants these permissions to all java classes by 
 omitting the codeBase parameter in the threadJava2.policy file.

 8) Make sure the Host Servers are started and running.
 The ProfileTokenCredential classes which reside in IBM Toolbox for Java,
 i.e. jt400.jar, are used as the credentials that are attached
 to the authenticated subject by the SampleThreadSubjectLogin.java
 program. The IBM Toolbox for Java credential classes require access
 to the Host Servers. 
 
 9) Invoke SampleThreadSubjectLogin while signed on as a user that
 does not have access to 'yourTestDir/thread.txt'.
 
 10) Start the sample by entering the following CL commands  =>

 CHGCURDIR DIR('yourTestDir')

 JAVA CLASS(SampleThreadSubjectLogin)
  CLASSPATH('yourTestDir/classes')
  PROP((java.version '1.3')
       (java.security.manager)
       (java.security.auth.login.config
        'yourTestDir/threadLogin.config')
       (java.security.policy
        'yourTestDir/threadJava2.policy')
       (java.security.auth.policy
        'yourTestDir/threadJaas.policy'))

 Enter the user id and password when prompted from step 1.
 
 11) Check yourTestDir/thread.txt for the "Hello World" entry.  

 *
 **/


public class SampleThreadSubjectLogin {
/**
 * Attempt to authenticate the user.
 *
 * @param args
 *      Input arguments for this application (ignored).
 *
 */
    public static void main(String[] args) {

    // use the configured LoginModules for the "AS400ToolboxApp" entry
    LoginContext lc = null;
    try {
        // if provided, the same subject is used for multiple login attempts
        lc = new LoginContext("AS400ToolboxApp",
                  new Subject(),  
                  new SampleCBHandler());
    } catch (LoginException le) {
        le.printStackTrace();
        System.exit(-1);
    }

    // the user has 3 attempts to authenticate successfully
    int i;
    for (i = 0; i < 3; i++) {
        try {

        // attempt authentication
        lc.login();

        // if we return with no exception, authentication succeeded
        break;

        } catch (AccountExpiredException aee) {

        System.out.println("Your account has expired");
        System.exit(-1);

        } catch (CredentialExpiredException cee) {

        System.out.println("Your credentials have expired.");
        System.exit(-1);

        } catch (FailedLoginException fle) {

        System.out.println("Authentication Failed");
        try {
            Thread.currentThread().sleep(3000);
        } catch (Exception e) {
            // ignore
        }

        } catch (Exception e) {

        System.out.println("Unexpected Exception - unable to continue");
        e.printStackTrace();
        System.exit(-1);
        }
    }

    // did they fail three times?
    if (i == 3) {
        System.out.println("Sorry authentication failed");
        System.exit(-1);
    }   


    // display authenticated principals & credentials
    System.out.println("Authentication Succeeded");

    System.out.println("Principals:");

    Iterator itr = lc.getSubject().getPrincipals().iterator();

    while (itr.hasNext())
        System.out.println(itr.next());

    itr = lc.getSubject().getPrivateCredentials().iterator();

    while (itr.hasNext())
        System.out.println(itr.next());

    itr = lc.getSubject().getPublicCredentials().iterator();

    while (itr.hasNext())
        System.out.println(itr.next());


        // let's do some Principal-based work:
    ThreadSubject.doAsPrivileged(lc.getSubject(), new java.security.PrivilegedAction() {
           public Object run() {
           System.out.println("\nYour java.home property: "
                  +System.getProperty("java.home"));
           System.out.println("\nYour user.home property: "
                      +System.getProperty("user.home"));
           File f = new File("thread.txt");
           System.out.print("\nthread.txt does ");
           if (!f.exists()) System.out.print("not ");
           System.out.println("exist in your current directory");
           
           try {
               // write "Hello World number   x"  into thread.txt
               PrintStream ps = new PrintStream(new FileOutputStream("thread.txt", true), true);

               long flen = f.length();
               ps.println("Hello World number    " +
                  Long.toString(flen/22) +
                  "\n");
               ps.close();
           } catch (Exception e) {
               e.printStackTrace();
           }
           
           System.out.println("\nOh, by the way, " + SampleThreadSubjectLogin.getCurrentUser());
           try {
               Thread.currentThread().sleep(2000);
           } catch (Exception e) {
            // ignore
               }
           System.out.println("\n\nHello World!\n");
           return null;
           }
       }, null);

       System.exit(0);
    
    }// end main()


// Returns the current OS identity for the main thread of the application.
// (This routine uses classes from IBM Toolbox for Java)    
// Note - Applications running on a secondary thread cannot use this API to determine the current user.
    static public String getCurrentUser() {

    try {
        AS400 localSys = new AS400("localhost", "*CURRENT", "*CURRENT");

        int ccsid = localSys.getCcsid();
        ProgramCall qusrjobi = new ProgramCall(localSys);
        ProgramParameter[] parms = new ProgramParameter[6];

        int rLength = 100;
        parms[0] = new ProgramParameter(rLength);
        parms[1] = new ProgramParameter(new AS400Bin4().toBytes(rLength));
        parms[2] = new ProgramParameter(new AS400Text(8, ccsid, localSys).toBytes("JOBI0600"));
        parms[3] = new ProgramParameter(new AS400Text(26,ccsid, localSys).toBytes("*"));
        parms[4] = new ProgramParameter(new AS400Text(16,ccsid, localSys).toBytes(""));
        parms[5] = new ProgramParameter(new AS400Bin4().toBytes(0));

        qusrjobi.setProgram(QSYSObjectPathName.toPath("QSYS", "QUSRJOBI", "PGM"), parms);
        AS400Text uidText = new AS400Text(10, ccsid, localSys);

    // Invoke the QUSRJOBI API
        qusrjobi.run();

        byte[] uidBytes = new byte[10];
        System.arraycopy((qusrjobi.getParameterList())[0].getOutputData(), 90, uidBytes, 0, 10);

        return ((String)(uidText.toObject(uidBytes))).trim();
    }


    catch (Exception e) {
        e.printStackTrace();
    }

    return "";
    }

} //end SampleThreadSubjectLogin class



/**
 * A CallbackHandler is passed to underlying security
 * services so that they may interact with the application
 * to retrieve specific authentication data,
 * such as user names and passwords, or to display certain
 * information, such as error and warning messages.
 * 
 * CallbackHandlers are implemented in an application
 * and platform-dependent fashion. The implementation decides
 * how to retrieve and display information depending on the
 * Callbacks passed to it.
 *
 * This class provides a sample CallbackHandler for applications
 * running in an i5/OS™ environment. However, it is not intended
 * to fulfill the requirements of production applications.
 * As indicated, the CallbackHandler is ultimately considered to
 * be application-dependent, as individual applications have
 * unique error checking, data handling, and user
 * interface requirements.
 *
 * The following callbacks are handled:
 * 
 *
 * For simplicity, prompting is handled interactively through
 * standard input and output. However, it is worth noting
 * that when standard input is provided by the console, this
 * approach allows passwords to be viewed as they are
 * typed. This should be avoided in production
 * applications.
 *
 * This CallbackHandler also allows a name and password
 * to be acquired through an alternative mechanism
 * and set directly on the handler to bypass the need for
 * user interaction on the respective Callbacks.
 *
 */
class SampleCBHandler implements CallbackHandler {
    private String name_ = null;
    private String password_ = null;
/**
 * Constructs a new SampleCBHandler.
 *
 */
public SampleCBHandler() {
    this(null, null);   
}
/**
 * Constructs a new SampleCBHandler.
 *
 * A name and password can optionally be specified in
 * order to bypass the need to prompt for information
 * on the respective Callbacks.
 *
 * @param name
 *      The default value for name callbacks. A null
 *      value indicates that the user should be
 *      prompted for this information. A non-null value
 *      cannot be zero length or exceed 10 characters.
 *
 * @param password
 *      The default value for password callbacks. A null
 *      value indicates that the user should be
 *      prompted for this information. A non-null value
 *      cannot be zero length or exceed 10 characters.
 */
public SampleCBHandler(String name, String password) {
    if (name != null)
        if ((name.length()==0) || (name.length()>10))
            throw new IllegalArgumentException("name");
    name_ = name;

    if (password != null)
        if ((password.length()==0) || (password.length()>10))
            throw new IllegalArgumentException("password");
    password_ = password;
}
/**
 * Handle the given name callback. 
 *
 * First check to see if a name has been passed in
 * on the constructor.  If so, assign it to the
 * callback and bypass the prompt.
 *
 * If a value has not been preset, attempt to prompt
 * for the name using standard input and output.
 *
 * @param c
 *      The NameCallback.
 *
 * @exception java.io.IOException
 *      If an input or output error occurs.
 *
 */
private void handleNameCallback(NameCallback c) throws IOException {
    // Check for cached value
    if (name_ != null) {
        c.setName(name_);
        return;
    }
    // No preset value; attempt stdin/out
    c.setName(
        stdIOReadName(c.getPrompt(), 10));
}
/**
 * Handle the given name callback.
 *
 * First check to see if a password has been passed
 * in on the constructor.  If so, assign it to the
 * callback and bypass the prompt.
 *
 * If a value has not been preset, attempt to prompt
 * for the password using standard input and output.
 *
 * @param c
 *      The PasswordCallback.
 *
 * @exception java.io.IOException
 *      If an input or output error occurs.
 *
 */
private void handlePasswordCallback(PasswordCallback c) throws IOException {
    // Check for cached value
    if (password_ != null) {
        c.setPassword(password_.toCharArray());
        return;
    }
    
    // No preset value; attempt stdin/out
    // Note - Not for production use.
    //        Password is not concealed by standard console I/O
    if (c.isEchoOn())
        c.setPassword(
            stdIOReadName(c.getPrompt(), 10).toCharArray());
    else
    {
        
        // Note - Password is not concealed by standard console I/O
        c.setPassword(stdIOReadName(c.getPrompt(), 10).toCharArray());
        
    }
}
/**
 * Handle the given text output callback.
 *
 * If the text is informational or a warning,
 * text is written to standard output. If the
 * callback defines an error message, text is
 * written to standard error.
 *
 * @param c
 *      The TextOutputCallback.
 *
 * @exception java.io.IOException
 *      If an input or output error occurs.
 *
 */
private void handleTextOutputCallback(TextOutputCallback c) throws IOException {
    if (c.getMessageType() == TextOutputCallback.ERROR)
        System.err.println(c.getMessage());
    else
        System.out.println(c.getMessage());
}
/**
 * Retrieve or display the information requested in the
 * provided Callbacks.
 *
 * The handle method implementation
 * checks the instance(s) of the Callback
 * object(s) passed in to retrieve or display the
 * requested information.
 *
 * @param callbacks
 *      An array of Callback objects provided
 *      by an underlying security service which contains
 *      the information requested to be retrieved or displayed.
 *
 * @exception java.io.IOException
 *      If an input or output error occurs.
 *
 * @exception UnsupportedCallbackException
 *      If the implementation of this method does not support
 *      one or more of the Callbacks specified in the
 *      callbacks parameter.
 *
 */
public void handle(Callback[] callbacks)
    throws IOException, UnsupportedCallbackException
{
    for (int i=0; i<callbacks.length; i++) {
        Callback c = callbacks[i];

        if (c instanceof NameCallback)
            handleNameCallback((NameCallback)c);
        else if (c instanceof PasswordCallback)
            handlePasswordCallback((PasswordCallback)c);
        else if (c instanceof TextOutputCallback)
            handleTextOutputCallback((TextOutputCallback)c);
        else
            throw new UnsupportedCallbackException
                (callbacks[i]);
    }
}
/**
 * Displays the given string using standard output,
 * followed by a space to separate from subsequent
 * input.
 *
 * @param prompt
 *      The text to display.
 *
 * @exception IOException
 *      If an input or output error occurs.
 *
 */
private void stdIOPrompt(String prompt) throws IOException {
    System.out.print(prompt + ' ');
    System.out.flush();
}
/**
 * Reads a String from standard input, stopped at
 * maxLength or by a newline.
 *
 * @param prompt
 *      The text to display to standard output immediately
 *      prior to reading the requested value.
 *
 * @param maxLength
 *      Maximum length of the String to return.
 *
 * @return
 *      The entered string. The value returned does
 *      not contain leading or trailing whitespace
 *      and is converted to uppercase.
 *
 * @exception IOException
 *      If an input or output error occurs.
 *
 */
private String stdIOReadName(String prompt, int maxLength) throws IOException {
    stdIOPrompt(prompt);
    String s =
        (new BufferedReader
            (new InputStreamReader(System.in))).readLine().trim();
    if (s.length() < maxLength)
        s = s.substring(0,maxLength);
    return s.toUpperCase();
}

}//end SampleCBHandler class
Related concepts
Example: IBM i5/OS PASE native method for Java
Related tasks
Example: Run the Java Performance Data Converter
Related reference
Example: Internationalization of dates using the java.util.DateFormat class
Example: Internationalization of numeric display using the java.util.NumberFormat class
Example: Internationalization of locale-specific data using the java.util.ResourceBundle class
Example: Access property
Example: BLOB
Example: CallableStatement interface for IBM Developer Kit for Java
Example: Remove values from a table through another statement's cursor
Example: CLOB
Example: Create a UDBDataSource and bind it with JNDI
Example: Create a UDBDataSource, and obtain a user ID and password
Example: Create a UDBDataSourceBind and set DataSource properties
Example: DatabaseMetaData interface for IBM Developer Kit for Java - Return a list of tables
Example: Datalink
Example: Distinct types
Example: Embed SQL Statements in your Java application
Example: End a transaction
Example: Invalid user ID and password
Example: JDBC
Example: Multiple connections that work on a transaction
Example: Obtain an initial context before binding UDBDataSource
Example: ParameterMetaData
Example: Change values with a statement through another statement's cursor
Example: ResultSet interface for IBM Developer Kit for Java
Example: ResultSet sensitivity
Example: Sensitive and insensitive ResultSets
Example: Set up connection pooling with UDBDataSource and UDBConnectionPoolDataSource
Example: SQLException
Example: Suspend and resume a transaction
Example: Suspended ResultSets
Example: Test the performance of connection pooling
Example: Test the performance of two DataSources
Example: Update BLOBs
Example: Update CLOBs
Example: Use a connection with multiple transactions
Example: Use BLOBs
Example: Use CLOBs
Example: Use JTA to handle a transaction
Example: Use metadata ResultSets that have more than one column
Example: Use native JDBC and IBM Toolbox for Java JDBC concurrently
Example: Use PreparedStatement to obtain a ResultSet
Create and populate a DB2CachedRowSet
Example: Use the Statement object's executeUpdate method
Examples: JAAS HelloWorld
Sample: IBM JGSS non-JAAS client program
Sample: IBM JGSS non-JAAS server program
Sample: IBM JGSS JAAS-enabled client program
Sample: IBM JGSS JAAS-enabled server program
Examples: IBM Java Secure Sockets Extension
Example: Call a CL program with java.lang.Runtime.exec()
Example: Call a CL command with java.lang.Runtime.exec()
Example: Call another Java program with java.lang.Runtime.exec()
Example: Call Java from C
Example: Call Java from RPG
Example: Use input and output streams for interprocess communication
Example: Java Invocation API
Examples: Use the Java Native Interface for native methods
Example: Use sockets for interprocess communication
Examples: Change your Java code to use client socket factories
Examples: Change your Java code to use server socket factories
Examples: Change your Java client to use secure sockets layer
Examples: Change your Java server to use secure sockets layer