This example shows you the implementation of the SampleThreadSubjectLogin class.
//////////////////////////////////////////////////////////////////////
//
// 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