JAAS rules
Overview
Table B.8. JAAS rules
JAAS_001: Do Not Add A Principal Or Credential To A Subject At Login Time In A Login Module
If the application instructs JAAS to login a 2-phase process is triggered:
- First, one by one, on each LoginModule the login method is invoked. The outcome of login should be stored by each login module privately.
- If all login invocations returned true, the distinct login modules are instructed to commit: this is the correct time to link relevant Principal and Credential objects to the Subject.
WRONG
public class MyLoginModule {
private Subject s;
public boolean login() throws LoginException {
if (trustedUser) {
Set principals = Subject.getPrincipals();
principals.add(new MyPrincipal("jcs"));
}
return true;
}
public boolean commit() {
}
}
RIGHT
public class MyLoginModule {
private Subject s;
private boolean trustedUser;
public boolean login() throws LoginException {
trustedUser = true;
return true;
}
public boolean commit() throws LoginException {
if (trustedUser) {
Set principals = Subject.getPrincipals();
principals.add(new MyPrincipal("jcs"));
}
}
}
JAAS_002: Only Clean A Principal Or Credential Previously Added To A Non Readonly Subject At Logout Time In A Login Module
Each LoginModule uses well-defined Principals. LoginModules should not destroy each other's Principals.
WRONG
public class MyLoginModule {
private Subject s;
public boolean logout() throws LoginException {
s.getPrincipals().clear();
}
}
RIGHT
public class MyLoginModule {
private Subject s;
public boolean logout() {
Set mine = s.getPrincipals(MyPrincipal.class);
s.getPrincipals().removeAll(mine);
}
}
JAAS_003: Return false From A login Method Only To Indicate To Ignore The Login Module
If a user is not trusted by a login module, the latter should not return false but raise a LoginException at login-time.
WRONG
public class MyLoginModule {
public boolean login() throws LoginException {
return trustedUser;
}
}
RIGHT
public class MyLoginModule {
public boolean login() throws LoginException { throw new LoginException("User not trusted");
}
}
JAAS_004: Do Not Interact With A User Directly In A Login Module
At no single moment a login module may directly contact the subject to authenticate: at all times a callback should be used to do so. Numerous callback-implementations are available, even generic TextInputCallback and TextOutputCallback.
WRONG
public class MyLoginModule {
public boolean login() throws LoginException {
System.out.println("Please provide VISA");
BufferedReader stdin = new BufferedReader(new InputStreamReader(
System.in));
String visa = stdin.readLine();
}
}
RIGHT
public class MyLoginModule {
private CallbackHandler cbh;
public boolean login() throws LoginException {
Callback visaCb = new TextInputCallback("Please provide VISA");
cbh.handle(new Callback[] { visaCb });
String visa = visaCb.getText();
}
}
JAAS_005: Ask For Credentials Only Once When Combining Multiple Login Modules
LoginModule should verify that credentials were not yet prompted for by another other modules. This dramatically enhances user-experience. The shared options Map has well defined entries to contain username and password:
- javax.security.auth.login.name
- javax.security.auth.login.password
WRONG
public class MyLoginModule {
private CallbackHandler cbh;
public boolean login() throws LoginException {
cbh.handle(/* ... */);
}
}
RIGHT
public class MyLoginModule {
private CallbackHandler cbh;
private Map sharedState;
public boolean login() throws LoginException {
String uid = (String) sharedState.get("javax.security.auth.login.name");
if ((uid == null)) {
cbh.handle(new CallBack[] {/* ... */});
}
}
}
JAAS_006: Add A Debug Option To A Login Module
Login modules function in the background, hiding a lot of detail. Both when doing development and debugging authentication-issues later on it, it is convenient to be able to turn on detailed debugging info. Consider this as lightweight debugging, serious bugs should always be cleared by attaching a JVMDI-enabled debugger to the application.
RIGHT
jaas.config
Foo {
be.jcs.auth.MyLoginModule REQUIRED debug=true;
}
MyLoginModule.java
public class MyLoginModule {
private Map options;
public boolean login() throws LoginException {
if (Boolean.valueOf((String) options.get("debug")).booleanValue()) {
logger.debug("Authenticated " + username);
}
}
}
JAAS_007: Make A Principal Serializable
At all times custom Principal implementations should implement Serializable.
WRONG
public class MyPrincipal implements Principal {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
RIGHT
public class MyPrincipal implements Principal, Serializable {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
JAAS_008: Use Principals To Name Groups And Roles
It is convenient to associate a Subject with Principals modeling groups and/or roles. Authorization will treat these accordingly.
RIGHT
jaas.config
grant Principal foo.Role "administrator" {
permission java.io.FilePermission "/etc/passwd", "read, write";
}
grant Principal foo.Team "slamDunk" {
permission be.jcs.auth.DunkPermission "dunk";
}
JAAS_009: Use A PrincipalComparator To Structure Principals Hierarchically
A Principal implementation can in fact implement the interface PrincipalComparator which can be used to support role hierarchies.
RIGHT
Following sample grants user permissions to admin.
public class MyPrincipalComparator {
public boolean implies(Subject subject) {
}
}
public class MyPrincipal extends MyPrincipalComparator implements Principal, Serializable {
}
jaas.config
grant Principal MyPrincipal "user" {
}
JAAS_010: Use A PriviledgedExceptionAction To Raise Checked Exceptions From Secured Code Fragments
A common PriviledgedAction cannot raise checked Exceptions (do not abuse the return Object to report an Exception). One should use a PriviledgedExceptionAction instead.
WRONG
public class MyPrivilegedAction implements PrivilegedAction {
public Object run() {
try {
} catch (CheckedException e) {
throw new RuntimeException(e);
}
}
}
RIGHT
public class MyPrivilegedAction implements PrivilegedExceptionAction {
public Object run() throws Exception {
}
}
JAAS_011: Use The doAsPriviledged Method Of A Subject Instead Of The doAs Method
Make sure execution of PrivilegedActions starts on a blank AccessControllerContext. Avoid to pass the currentThread's AccessControllerContext. Invoking Subject.doAsPrivileged is sufficient to force that a temporary AccessControllerContext is used to trace priviledges for a given PriviledgedAction.
RIGHT
public class MyPrivilegedAction implements PrivilegedAction {
public Object run() {
}
}
public class Foo {
public static void main(final String[] args) {
LoginContext foo = new LoginContext("Foo");
foo.login();
Subject subject = foo.getSubject();
subject.doAsPriviledged(subject, new MyPriviledgedAction(), null);
}
}