blob: e5eeb6c1d7f1801240a1803b0447678b694302e3 [file] [log] [blame]
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import com.sun.security.auth.UnixPrincipal;
import javax.security.auth.Subject;
import javax.security.auth.callback.*;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import java.io.IOException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/*
* @test
* @bug 8050460
* @summary Test checks that proper methods associated with login/logout process
* of LoginContext are called for different configurations and circumstances.
* @modules jdk.security.auth
*
* @run main/othervm LCTest EmptyModuleConfig false
* @run main/othervm LCTest IncorrectName false
* @run main/othervm LCTest AbortRequisite false abort
* @run main/othervm LCTest AbortSufficient false abort
* @run main/othervm LCTest AbortRequired false abort
* @run main/othervm LCTest LogoutRequisite false logout
* @run main/othervm LCTest LogoutSufficient true logout
* @run main/othervm LCTest LogoutRequired false logout
* @run main/othervm LCTest LoginRequisite false login
* @run main/othervm LCTest LoginSufficient true login
* @run main/othervm LCTest LoginRequired false login
*/
public class LCTest {
private static final String USER_NAME = "testUser";
private static final String PASSWORD = "testPassword";
private static final List<String> loggedActions = new ArrayList<>();
static {
System.setProperty("java.security.auth.login.config",
System.getProperty("test.src")
+ System.getProperty("file.separator")
+ "LCTest.jaas.config");
}
public static void main(String[] args) {
if (args.length < 2) {
throw new RuntimeException("Incorrect test params");
}
String nameOfContext = args[0];
boolean isPositive = Boolean.parseBoolean(args[1]);
String actionName = null;
if (args.length == 3) {
actionName = args[2];
}
try {
LoginContext lc = new LoginContext(nameOfContext,
new MyCallbackHandler());
lc.login();
checkPrincipal(lc, true);
lc.logout();
checkPrincipal(lc, false);
if (!isPositive) {
throw new RuntimeException("Test failed. Exception expected.");
}
} catch (LoginException le) {
if (isPositive) {
throw new RuntimeException("Test failed. Unexpected " +
"exception", le);
}
System.out.println("Expected exception: "
+ le.getMessage());
}
checkActions(actionName);
System.out.println("Test passed.");
}
/*
* Log action from login modules
*/
private static void logAction(String actionName) {
loggedActions.add(actionName);
}
/*
* Check if logged actions are as expected. We always expected 3 actions
* if any.
*/
private static void checkActions(String actionName) {
if (actionName == null) {
if (loggedActions.size() != 0) {
throw new RuntimeException("No logged actions expected");
}
} else {
int loggedActionsFound = 0;
System.out.println("Logged actions : " + loggedActions);
for (String s : loggedActions) {
if (s.equals(actionName)) {
loggedActionsFound++;
}
}
if (loggedActionsFound != 3) {
throw new RuntimeException("Incorrect number of actions " +
actionName + " : " + loggedActionsFound);
}
}
}
/*
* Check context for principal of the test user.
*/
private static void checkPrincipal(LoginContext loginContext, boolean
principalShouldExist) {
if (!principalShouldExist) {
if (loginContext.getSubject().getPrincipals().size() != 0) {
throw new RuntimeException("Test failed. Principal was not " +
"cleared.");
}
} else {
for (Principal p : loginContext.getSubject().getPrincipals()) {
if (p instanceof UnixPrincipal &&
USER_NAME.equals(p.getName())) {
//Proper principal was found, return.
return;
}
}
throw new RuntimeException("Test failed. UnixPrincipal "
+ USER_NAME + " expected.");
}
}
private static class MyCallbackHandler implements CallbackHandler {
@Override
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof NameCallback) {
((NameCallback) callback).setName(USER_NAME);
} else if (callback instanceof PasswordCallback) {
((PasswordCallback) callback).setPassword(
PASSWORD.toCharArray());
} else {
throw new UnsupportedCallbackException(callback);
}
}
}
}
/* -------------------------------------------------------------------------
* Test login modules
* -------------------------------------------------------------------------
*/
/*
* Login module that should pass through all phases.
*/
public static class LoginModuleAllPass extends LoginModuleBase {
}
/*
* Login module that throws Exception in abort method.
*/
public static class LoginModuleWithAbortException extends LoginModuleBase {
@Override
public boolean abort() throws LoginException {
super.abort();
throw new LoginException("Abort failed!");
}
}
/*
* Login module that throws Exception in login method.
*/
public static class LoginModuleWithLoginException extends LoginModuleBase {
@Override
public boolean login() throws LoginException {
super.login();
throw new FailedLoginException("Login failed!");
}
}
/*
* Login module that throws Exception in logout method.
*/
public static class LoginModuleWithLogoutException extends LoginModuleBase {
@Override
public boolean logout() throws LoginException {
super.logout();
throw new FailedLoginException("Logout failed!");
}
}
/*
* Base class for login modules
*/
public static abstract class LoginModuleBase implements LoginModule {
// initial state
private Subject subject;
private CallbackHandler callbackHandler;
private Map sharedState;
private Map options;
private UnixPrincipal userPrincipal;
// username and password
private String username;
private String password;
// the authentication status
private boolean succeeded = false;
private boolean commitSucceeded = false;
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;
this.options = options;
System.out.println("Login module initialized.");
}
/*
* Authenticate the user by prompting for a username and password.
*/
@Override
public boolean login() throws LoginException {
LCTest.logAction("login");
if (callbackHandler == null) {
throw new LoginException("No CallbackHandler available");
}
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();
password = new String(((PasswordCallback) callbacks[1])
.getPassword());
if (username.equals(LCTest.USER_NAME) &&
password.equals(LCTest.PASSWORD)) {
succeeded = true;
return true;
}
throw new FailedLoginException("Incorrect username/password!");
} catch (IOException | UnsupportedCallbackException e) {
throw new LoginException("Login failed: " + e.getMessage());
}
}
@Override
public boolean commit() throws LoginException {
LCTest.logAction("commit");
if (succeeded == false) {
return false;
}
userPrincipal = new UnixPrincipal(username);
final Subject s = subject;
final UnixPrincipal up = userPrincipal;
java.security.AccessController.doPrivileged
((java.security.PrivilegedAction) () -> {
if (!s.getPrincipals().contains(up)) {
s.getPrincipals().add(up);
}
return null;
});
password = null;
commitSucceeded = true;
return true;
}
@Override
public boolean abort() throws LoginException {
LCTest.logAction("abort");
if (succeeded == false) {
return false;
}
clearState();
return true;
}
@Override
public boolean logout() throws LoginException {
LCTest.logAction("logout");
clearState();
return true;
}
private void clearState() {
if (commitSucceeded) {
final Subject s = subject;
final UnixPrincipal up = userPrincipal;
java.security.AccessController.doPrivileged
((java.security.PrivilegedAction) () -> {
s.getPrincipals().remove(up);
return null;
});
}
username = null;
password = null;
userPrincipal = null;
}
}
}