# HG changeset patch # User asmotrak # Date 1440094614 25200 # Node ID d97dc26de1d747af7dcc50ed6470959d7aeb7d07 # Parent 8d58fc5a0349d14906d9257a84031a6b0052cafd 8050460: JAAS login/logout tests with LoginContext Reviewed-by: xuelei Contributed-by: svetlana.nikandrova@oracle.com diff -r 8d58fc5a0349 -r d97dc26de1d7 jdk/test/javax/security/auth/login/LoginContext/LCTest.jaas.config --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/security/auth/login/LoginContext/LCTest.jaas.config Thu Aug 20 11:16:54 2015 -0700 @@ -0,0 +1,57 @@ + +"AbortRequired" { + LCTest$LoginModuleAllPass required; + LCTest$LoginModuleWithLoginException required; + LCTest$LoginModuleAllPass required; +}; + +"AbortRequisite" { + LCTest$LoginModuleWithLoginException required; + LCTest$LoginModuleWithAbortException requisite; + LCTest$LoginModuleAllPass required; +}; + +"AbortSufficient" { + LCTest$LoginModuleWithLoginException required; + LCTest$LoginModuleWithLoginException sufficient; + LCTest$LoginModuleAllPass required; +}; + +"LogoutRequisite" { + LCTest$LoginModuleAllPass required; + LCTest$LoginModuleWithLogoutException requisite; + LCTest$LoginModuleAllPass required; +}; + +"LogoutSufficient" { + LCTest$LoginModuleAllPass required; + LCTest$LoginModuleWithLoginException sufficient; + LCTest$LoginModuleAllPass required; +}; + +"LogoutRequired" { + LCTest$LoginModuleWithLogoutException required; + LCTest$LoginModuleWithAbortException required; + LCTest$LoginModuleAllPass required; +}; + +"LoginRequired" { + LCTest$LoginModuleWithLoginException required; + LCTest$LoginModuleWithAbortException required; + LCTest$LoginModuleAllPass required; +}; + +"LoginSufficient" { + LCTest$LoginModuleAllPass required; + LCTest$LoginModuleWithLoginException sufficient; + LCTest$LoginModuleAllPass required; +}; + +"LoginRequisite" { + LCTest$LoginModuleWithLoginException required; + LCTest$LoginModuleWithAbortException requisite; + LCTest$LoginModuleAllPass required; +}; + +"EmptyModuleConfig" { +}; diff -r 8d58fc5a0349 -r d97dc26de1d7 jdk/test/javax/security/auth/login/LoginContext/LCTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/security/auth/login/LoginContext/LCTest.java Thu Aug 20 11:16:54 2015 -0700 @@ -0,0 +1,337 @@ +/* + * 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 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 sharedState, Map 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; + } + } + +}