8050460: JAAS login/logout tests with LoginContext
authorasmotrak
Thu, 20 Aug 2015 11:16:54 -0700
changeset 32233 d97dc26de1d7
parent 32232 8d58fc5a0349
child 32234 421773f441c6
8050460: JAAS login/logout tests with LoginContext Reviewed-by: xuelei Contributed-by: svetlana.nikandrova@oracle.com
jdk/test/javax/security/auth/login/LoginContext/LCTest.jaas.config
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.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" {
+};
--- /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<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;
+        }
+    }
+
+}