FileLoginModulepublic class FileLoginModule extends Object implements LoginModuleThis {@link LoginModule} performs file-based authentication.
A supplied username and password is verified against the
corresponding user credentials stored in a designated password file.
If successful then a new {@link JMXPrincipal} is created with the
user's name and it is associated with the current {@link Subject}.
Such principals may be identified and granted management privileges in
the access control file for JMX remote management or in a Java security
policy.
The password file comprises a list of key-value pairs as specified in
{@link Properties}. The key represents a user's name and the value is its
associated cleartext password. By default, the following password file is
used:
${java.home}/lib/management/jmxremote.password
A different password file can be specified via the passwordFile
configuration option.
This module recognizes the following Configuration options:
-
passwordFile
- the path to an alternative password file. It is used instead of
the default password file.
-
useFirstPass
- if
true , this module retrieves the username and password
from the module's shared state, using "javax.security.auth.login.name"
and "javax.security.auth.login.password" as the respective keys. The
retrieved values are used for authentication. If authentication fails,
no attempt for a retry is made, and the failure is reported back to
the calling application.
-
tryFirstPass
- if
true , this module retrieves the username and password
from the module's shared state, using "javax.security.auth.login.name"
and "javax.security.auth.login.password" as the respective keys. The
retrieved values are used for authentication. If authentication fails,
the module uses the CallbackHandler to retrieve a new username and
password, and another attempt to authenticate is made. If the
authentication fails, the failure is reported back to the calling
application.
-
storePass
- if
true , this module stores the username and password
obtained from the CallbackHandler in the module's shared state, using
"javax.security.auth.login.name" and
"javax.security.auth.login.password" as the respective keys. This is
not performed if existing values already exist for the username and
password in the shared state, or if authentication fails.
-
clearPass
- if
true , this module clears the username and password
stored in the module's shared state after both phases of authentication
(login and commit) have completed.
|
Fields Summary |
---|
private static final String | DEFAULT_PASSWORD_FILE_NAME | private static final String | USERNAME_KEY | private static final String | PASSWORD_KEY | private static final ClassLogger | logger | private boolean | useFirstPass | private boolean | tryFirstPass | private boolean | storePass | private boolean | clearPass | private boolean | succeeded | private boolean | commitSucceeded | private String | username | private char[] | password | private JMXPrincipal | user | private Subject | subject | private CallbackHandler | callbackHandler | private Map | sharedState | private Map | options | private String | passwordFile | private String | passwordFileDisplayName | private boolean | userSuppliedPasswordFile | private boolean | hasJavaHomePermission | private Properties | userCredentials |
Methods Summary |
---|
public boolean | abort()Abort user authentication (Authentication Phase 2).
This method is called if the LoginContext's overall authentication
failed (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL
LoginModules did not succeed).
If this LoginModule's own authentication attempt
succeeded (checked by retrieving the private state saved by the
login and commit methods),
then this method cleans up any state that was originally saved.
if (logger.debugOn()) {
logger.debug("abort",
"Authentication has not completed successfully");
}
if (succeeded == false) {
return false;
} else if (succeeded == true && commitSucceeded == false) {
// Clean out state
succeeded = false;
cleanState();
user = null;
} else {
// overall authentication succeeded and commit succeeded,
// but someone else's commit failed
logout();
}
return true;
| private void | attemptAuthentication(boolean usePasswdFromSharedState)Attempt authentication
// get the username and password
getUsernamePassword(usePasswdFromSharedState);
String localPassword = null;
// userCredentials is initialized in login()
if (((localPassword = userCredentials.getProperty(username)) == null) ||
(! localPassword.equals(new String(password)))) {
// username not found or passwords do not match
if (logger.debugOn()) {
logger.debug("login", "Invalid username or password");
}
throw new FailedLoginException("Invalid username or password");
}
// Save the username and password in the shared state
// only if authentication succeeded
if (storePass &&
!sharedState.containsKey(USERNAME_KEY) &&
!sharedState.containsKey(PASSWORD_KEY)) {
sharedState.put(USERNAME_KEY, username);
sharedState.put(PASSWORD_KEY, password);
}
// Create a new user principal
user = new JMXPrincipal(username);
if (logger.debugOn()) {
logger.debug("login",
"User '" + username + "' successfully validated");
}
| private void | cleanState()Clean out state because of a failed authentication attempt
username = null;
if (password != null) {
Arrays.fill(password, ' ");
password = null;
}
if (clearPass) {
sharedState.remove(USERNAME_KEY);
sharedState.remove(PASSWORD_KEY);
}
| public boolean | commit()Complete user authentication (Authentication Phase 2).
This method is called if the LoginContext's
overall authentication has succeeded
(all the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL
LoginModules have succeeded).
If this LoginModule's own authentication attempt
succeeded (checked by retrieving the private state saved by the
login method), then this method associates a
JMXPrincipal with the Subject located in the
LoginModule . If this LoginModule's own
authentication attempted failed, then this method removes
any state that was originally saved.
if (succeeded == false) {
return false;
} else {
if (subject.isReadOnly()) {
cleanState();
throw new LoginException("Subject is read-only");
}
// add Principals to the Subject
if (!subject.getPrincipals().contains(user)) {
subject.getPrincipals().add(user);
}
if (logger.debugOn()) {
logger.debug("commit",
"Authentication has completed successfully");
}
}
// in any case, clean out state
cleanState();
commitSucceeded = true;
return true;
| private void | getUsernamePassword(boolean usePasswdFromSharedState)Get the username and password.
This method does not return any value.
Instead, it sets global name and password variables.
Also note that this method will set the username and password
values in the shared state in case subsequent LoginModules
want to use them via use/tryFirstPass.
if (usePasswdFromSharedState) {
// use the password saved by the first module in the stack
username = (String)sharedState.get(USERNAME_KEY);
password = (char[])sharedState.get(PASSWORD_KEY);
return;
}
// acquire username and password
if (callbackHandler == null)
throw new LoginException("Error: no CallbackHandler available " +
"to garner authentication information from the user");
Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback("username");
callbacks[1] = new PasswordCallback("password", false);
try {
callbackHandler.handle(callbacks);
username = ((NameCallback)callbacks[0]).getName();
char[] tmpPassword = ((PasswordCallback)callbacks[1]).getPassword();
password = new char[tmpPassword.length];
System.arraycopy(tmpPassword, 0,
password, 0, tmpPassword.length);
((PasswordCallback)callbacks[1]).clearPassword();
} catch (IOException ioe) {
LoginException le = new LoginException(ioe.toString());
throw (LoginException) EnvHelp.initCause(le, ioe);
} catch (UnsupportedCallbackException uce) {
LoginException le = new LoginException(
"Error: " + uce.getCallback().toString() +
" not available to garner authentication " +
"information from the user");
throw (LoginException) EnvHelp.initCause(le, uce);
}
| public void | initialize(javax.security.auth.Subject subject, javax.security.auth.callback.CallbackHandler callbackHandler, java.util.Map sharedState, java.util.Map options)Initialize this LoginModule .
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;
this.options = options;
// initialize any configured options
tryFirstPass =
"true".equalsIgnoreCase((String)options.get("tryFirstPass"));
useFirstPass =
"true".equalsIgnoreCase((String)options.get("useFirstPass"));
storePass =
"true".equalsIgnoreCase((String)options.get("storePass"));
clearPass =
"true".equalsIgnoreCase((String)options.get("clearPass"));
passwordFile = (String)options.get("passwordFile");
passwordFileDisplayName = passwordFile;
userSuppliedPasswordFile = true;
// set the location of the password file
if (passwordFile == null) {
passwordFile = DEFAULT_PASSWORD_FILE_NAME;
userSuppliedPasswordFile = false;
try {
System.getProperty("java.home");
hasJavaHomePermission = true;
passwordFileDisplayName = passwordFile;
} catch (SecurityException e) {
hasJavaHomePermission = false;
passwordFileDisplayName =
ConnectorBootstrap.DefaultValues.PASSWORD_FILE_NAME;
}
}
| private void | loadPasswordFile()
FileInputStream fis;
try {
fis = new FileInputStream(passwordFile);
} catch (SecurityException e) {
if (userSuppliedPasswordFile || hasJavaHomePermission) {
throw e;
} else {
FilePermission fp =
new FilePermission(passwordFileDisplayName, "read");
AccessControlException ace = new AccessControlException(
"access denied " + fp.toString());
ace.setStackTrace(e.getStackTrace());
throw ace;
}
}
BufferedInputStream bis = new BufferedInputStream(fis);
userCredentials = new Properties();
userCredentials.load(bis);
bis.close();
| public boolean | login()Begin user authentication (Authentication Phase 1).
Acquire the user's name and password and verify them against
the corresponding credentials from the password file.
try {
loadPasswordFile();
} catch (IOException ioe) {
LoginException le = new LoginException(
"Error: unable to load the password file: " +
passwordFileDisplayName);
throw (LoginException) EnvHelp.initCause(le, ioe);
}
if (userCredentials == null) {
throw new LoginException
("Error: unable to locate the users' credentials.");
}
if (logger.debugOn()) {
logger.debug("login",
"Using password file: " + passwordFileDisplayName);
}
// attempt the authentication
if (tryFirstPass) {
try {
// attempt the authentication by getting the
// username and password from shared state
attemptAuthentication(true);
// authentication succeeded
succeeded = true;
if (logger.debugOn()) {
logger.debug("login",
"Authentication using cached password has succeeded");
}
return true;
} catch (LoginException le) {
// authentication failed -- try again below by prompting
cleanState();
logger.debug("login",
"Authentication using cached password has failed");
}
} else if (useFirstPass) {
try {
// attempt the authentication by getting the
// username and password from shared state
attemptAuthentication(true);
// authentication succeeded
succeeded = true;
if (logger.debugOn()) {
logger.debug("login",
"Authentication using cached password has succeeded");
}
return true;
} catch (LoginException le) {
// authentication failed
cleanState();
logger.debug("login",
"Authentication using cached password has failed");
throw le;
}
}
if (logger.debugOn()) {
logger.debug("login", "Acquiring password");
}
// attempt the authentication using the supplied username and password
try {
attemptAuthentication(false);
// authentication succeeded
succeeded = true;
if (logger.debugOn()) {
logger.debug("login", "Authentication has succeeded");
}
return true;
} catch (LoginException le) {
cleanState();
logger.debug("login", "Authentication has failed");
throw le;
}
| public boolean | logout()Logout a user.
This method removes the Principals
that were added by the commit method.
if (subject.isReadOnly()) {
cleanState();
throw new LoginException ("Subject is read-only");
}
subject.getPrincipals().remove(user);
// clean out state
cleanState();
succeeded = false;
commitSucceeded = false;
user = null;
if (logger.debugOn()) {
logger.debug("logout", "Subject is being logged out");
}
return true;
|
|