--- /dev/null 2015-07-25 05:49:27.889711346 -0700 +++ new/test/java/security/jigsaw/JigsawCommon.java 2015-10-13 23:00:05.962957652 -0700 @@ -0,0 +1,252 @@ +/* + * 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 java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.lang.module.ModuleDescriptor; +import static java.lang.module.ModuleDescriptor.Builder; + +/** + * Jigsaw utility methods are part of this class. It exposes methods to + * generate module descriptor, compile jigsaw modules, creates modular and + * regular jar files and generate service descriptor inside + * META-INF folder etc. + */ +public class JigsawCommon { + + /** + * Enum represents all supported module types in JDK9. + */ + public enum MODULE_TYPE { + + STRICT, AUTO, UN_NAMED; + } + + public static final String SPACE = " "; + public static final String MODULE_INFO_DESCRIPTOR = "module-info.class"; + + /** + * Constructs a Java Command line string based on modular structure + * followed by modular client and service. + */ + public static String[] getJavaCommand(MODULE_TYPE clientModuleType, + MODULE_TYPE serviceModuletype, Path clientJarFilePath, + Path serviceJarFilePath, String clientModuleName, + String mainClass, Map vmArgs) throws IOException { + + final StringBuilder command = new StringBuilder(); + vmArgs.forEach((key, value) -> command.append(key + value + SPACE)); + if (serviceModuletype != MODULE_TYPE.UN_NAMED + && clientModuleType != MODULE_TYPE.UN_NAMED) { + command.append(SPACE + "-mp" + SPACE); + command.append(clientJarFilePath.getParent().toRealPath()); + command.append(SPACE + ("-m" + SPACE + clientModuleName + "/") + + mainClass); + } else if (serviceModuletype == MODULE_TYPE.UN_NAMED + && clientModuleType == MODULE_TYPE.UN_NAMED) { + command.append(SPACE + "-cp" + SPACE); + command.append(clientJarFilePath.toRealPath()); + command.append(File.pathSeparator); + command.append(serviceJarFilePath.toRealPath()); + command.append(SPACE + mainClass); + } else { + if (serviceModuletype != MODULE_TYPE.UN_NAMED) { + command.append(SPACE + "-mp" + SPACE); + command.append(serviceJarFilePath.getParent().toRealPath()); + } else { + command.append(SPACE + "-cp" + SPACE); + command.append(serviceJarFilePath.toRealPath()); + } + if (clientModuleType != MODULE_TYPE.UN_NAMED) { + command.append(SPACE + "-mp" + SPACE); + command.append(clientJarFilePath.getParent().toRealPath()); + command.append(SPACE + ("-m" + SPACE + clientModuleName + "/") + + mainClass); + } else { + command.append(SPACE + "-cp" + SPACE); + command.append(clientJarFilePath.toRealPath()); + command.append(SPACE + mainClass); + } + } + + return command.toString().trim().split("[\\s]+"); + } + + /** + * Compile the source path and generates a regular/modular jar as per need. + */ + public static boolean compileAndCreateJar(Path codePath, Path compilePath, + Path jarFile, ModuleDescriptor moduleDescriptor, + boolean addMetaInfServiceDescriptor, + String serviceImpl, Path metaServiceDescriptor) throws IOException { + + System.out.println(String.format( + "Compiling source code '%s'", codePath)); + boolean done = CompilerUtils.compile(codePath, compilePath); + System.out.println(String.format( + "Compilation completed successfully? %s", done)); + if (done) { + if (addMetaInfServiceDescriptor) { + createMetaInfServiceDescriptor(compilePath, serviceImpl, + metaServiceDescriptor); + } else { + System.out.println( + "META-INF service descriptor will not be created"); + } + + if (moduleDescriptor != null) { + System.out.println(String.format( + "Creating Modular jar file '%s'", jarFile)); + createModularjar(jarFile, compilePath, + moduleDescriptor, Paths.get(".")); + } else { + System.out.println(String.format( + "Creating regular jar file '%s'", jarFile)); + JarUtils.createJarFile(jarFile, compilePath); + } + } + + return done; + } + + /** + * Generates a modular jar with module descriptor. + */ + public static void createModularjar(Path jarfile, Path compilePath, + ModuleDescriptor moduleDescriptor, Path... file) + throws IOException { + + Path parent = jarfile.getParent(); + if (parent != null) { + Files.createDirectories(parent); + } + + List entries = new ArrayList<>(); + for (Path entry : file) { + Files.find(compilePath.resolve(entry), Integer.MAX_VALUE, + (p, attrs) -> attrs.isRegularFile()) + .map(e -> compilePath.relativize(e)) + .forEach(entries::add); + } + try (JarOutputStream jos = new JarOutputStream( + Files.newOutputStream(jarfile))) { + JarEntry je = new JarEntry(MODULE_INFO_DESCRIPTOR); + jos.putNextEntry(je); + jdk.internal.module.ModuleInfoWriter.write(moduleDescriptor, jos); + jos.closeEntry(); + + for (Path entry : entries) { + Path normalized = entry.normalize(); + String name = normalized + .subpath(0, normalized.getNameCount()) + .toString() + .replace(File.separatorChar, '/'); + + jos.putNextEntry(new JarEntry(name)); + Files.copy(compilePath.resolve(entry), jos); + } + } + } + + /** + * Generates a module descriptor for modular service component. + */ + public static ModuleDescriptor generateServiceModuleDescriptor( + MODULE_TYPE serviceModuletype, String moduleName, + String servicePkg, String serviceInterface, + String serviceImpl, List requiredModules) { + + final Builder builder; + if (serviceModuletype == MODULE_TYPE.STRICT) { + System.out.println("Generating ServiceModuleDescriptor object"); + builder = new Builder(moduleName) + .exports(servicePkg) + .provides(serviceInterface, serviceImpl); + } else { + System.out.println("ServiceModuleDescriptor object not required."); + return null; + } + requiredModules.stream().forEach( + reqMod -> builder.requires(reqMod)); + + return builder.build(); + } + + /** + * Generates a module descriptor for modular client component. + */ + public static ModuleDescriptor generateClientModuleDescriptor( + MODULE_TYPE clientModuleType, MODULE_TYPE serviceModuletype, + String moduleName, String clientPkg, String serviceInterface, + String serviceModuleName, List requiredModules) { + + final Builder builder; + if (clientModuleType == MODULE_TYPE.STRICT) { + System.out.println("Generating ClientModuleDescriptor object"); + builder = new Builder(moduleName) + .exports(clientPkg) + .uses(serviceInterface); + if (serviceModuletype == MODULE_TYPE.STRICT) { + builder.requires(serviceModuleName); + } + } else { + System.out.println("ClientModuleDescriptor object not required."); + return null; + } + requiredModules.stream().forEach( + reqMod -> builder.requires(reqMod)); + + return builder.build(); + } + + /** + * Generates service descriptor inside META-INF folder. + */ + public static void createMetaInfServiceDescriptor(Path serviceCompilePath, + String serviceImpl, Path metaServiceDescriptor) + throws UnsupportedEncodingException, IOException { + + System.out.println(String.format("Creating META-INF service descriptor" + + " for '%s' at path '%s'", serviceImpl, serviceCompilePath)); + Path serviceDescriptorFilePath + = serviceCompilePath.resolve(metaServiceDescriptor); + Path parent = serviceDescriptorFilePath.getParent(); + if (parent != null) { + Files.createDirectories(parent); + } + Files.write(serviceDescriptorFilePath, serviceImpl.getBytes("UTF-8")); + System.out.println(String.format( + "META-INF service descriptor created successfully")); + } + +} --- /dev/null 2015-07-25 05:49:27.889711346 -0700 +++ new/test/java/security/jigsaw/TEST.properties 2015-10-13 23:00:06.463951760 -0700 @@ -0,0 +1 @@ +modules java.base/jdk.internal.module --- /dev/null 2015-07-25 05:49:27.889711346 -0700 +++ new/test/java/security/jigsaw/jaas/JaasModularClientTest.java 2015-10-13 23:00:06.983945642 -0700 @@ -0,0 +1,218 @@ +/* + * 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 java.nio.file.Path; +import java.nio.file.Paths; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Arrays; +import java.lang.module.ModuleDescriptor; +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.OutputAnalyzer; + +/** + * @test + * @bug 8078813 + * @library /jdk/jigsaw/lib + * @library /lib/testlibrary + * @library /java/security/jigsaw + * @summary Test custom JAAS module with all possible modular option. The test + * includes different combination of JAAS client/login modules + * interaction with or without service description. The different + * module types used here are, + * STRICT - Modules have module descriptor(module-info.java) defining + * the module. + * AUTO - Are regular jar files but provided in MODULE_PATH instead + * of CLASS_PATH. + * UNNAMED - Are regular jar but provided through CLASS_PATH. + * @run main JaasModularClientTest + */ +public class JaasModularClientTest extends JigsawCommon { + + private static final String TEST_SRC = System.getProperty("test.src"); + private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); + private static final String SERVICE_MODULE_NAME = "jaasloginmodule"; + private static final Path SERVICE_SRC_DIR + = SRC_DIR.resolve(SERVICE_MODULE_NAME); + private static final String SERVICE_PKG = "login"; + private static final String SERVICE_JAR = SERVICE_PKG + ".jar"; + private static final String CLIENT_MODULE_NAME = "jaasclientmodule"; + private static final Path CLIENT_SRC_DIR + = SRC_DIR.resolve(CLIENT_MODULE_NAME); + private static final String CLIENT_PKG = "client"; + private static final String CLIENT_JAR = CLIENT_PKG + ".jar"; + private static final String JAR_DIR = "jar"; + private static final String JAR_MODULE = "jarmodule"; + private static final Path BUILD_DIR = Paths.get(".").resolve("build"); + private static final String MAIN_CLASS = CLIENT_PKG + ".JaasClient"; + private static final String LOGIN_SERVICE_INTERFACE + = "javax.security.auth.spi.LoginModule"; + private static final String SERVICE_IMPL = SERVICE_PKG + ".TestLoginModule"; + private static final List REQUIRED_MODULES + = Arrays.asList("java.base", "jdk.security.auth"); + private static final Path META_SERVICE_DESCRIPTOR + = Paths.get("META-INF").resolve("services") + .resolve(LOGIN_SERVICE_INTERFACE); + private static final boolean WITH_SERVICE_DESCRIPTOR = true; + private static final boolean WITHOUT_SERVICE_DESCRIPTOR = false; + private static final boolean PASS = true; + private static final String EXPECTED_FAILURE = "No LoginModule found"; + private static final String NO_FAILURE = null; + private static final Map VM_ARGS = new LinkedHashMap<>(); + + public static void main(String[] args) { + + boolean success = true; + + //PARAMETER ORDERS - + //client Module Type, Service Module Type, + //Service META Descriptor Required, Expected Result + success &= runTest(MODULE_TYPE.STRICT, MODULE_TYPE.STRICT, + WITH_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + success &= runTest(MODULE_TYPE.STRICT, MODULE_TYPE.STRICT, + WITHOUT_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + success &= runTest(MODULE_TYPE.STRICT, MODULE_TYPE.AUTO, + WITH_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + success &= runTest(MODULE_TYPE.STRICT, MODULE_TYPE.AUTO, + WITHOUT_SERVICE_DESCRIPTOR, PASS, EXPECTED_FAILURE); + success &= runTest(MODULE_TYPE.STRICT, MODULE_TYPE.UN_NAMED, + WITH_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + success &= runTest(MODULE_TYPE.STRICT, MODULE_TYPE.UN_NAMED, + WITHOUT_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + + success &= runTest(MODULE_TYPE.AUTO, MODULE_TYPE.STRICT, + WITH_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + success &= runTest(MODULE_TYPE.AUTO, MODULE_TYPE.STRICT, + WITHOUT_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + success &= runTest(MODULE_TYPE.AUTO, MODULE_TYPE.AUTO, + WITH_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + success &= runTest(MODULE_TYPE.AUTO, MODULE_TYPE.AUTO, + WITHOUT_SERVICE_DESCRIPTOR, PASS, EXPECTED_FAILURE); + success &= runTest(MODULE_TYPE.AUTO, MODULE_TYPE.UN_NAMED, + WITH_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + success &= runTest(MODULE_TYPE.AUTO, MODULE_TYPE.UN_NAMED, + WITHOUT_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + + success &= runTest(MODULE_TYPE.UN_NAMED, MODULE_TYPE.STRICT, + WITH_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + success &= runTest(MODULE_TYPE.UN_NAMED, MODULE_TYPE.STRICT, + WITHOUT_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + success &= runTest(MODULE_TYPE.UN_NAMED, MODULE_TYPE.AUTO, + WITH_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + success &= runTest(MODULE_TYPE.UN_NAMED, MODULE_TYPE.AUTO, + WITHOUT_SERVICE_DESCRIPTOR, PASS, EXPECTED_FAILURE); + success &= runTest(MODULE_TYPE.UN_NAMED, MODULE_TYPE.UN_NAMED, + WITH_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + success &= runTest(MODULE_TYPE.UN_NAMED, MODULE_TYPE.UN_NAMED, + WITHOUT_SERVICE_DESCRIPTOR, PASS, NO_FAILURE); + + if (!success) { + throw new RuntimeException("Atleast one test failed."); + } + } + + public static boolean runTest(MODULE_TYPE clientModuleType, + MODULE_TYPE serviceModuletype, boolean addMetaInfDescriptor, + boolean expectedResult, String expectedFailure) { + + boolean result = true; + try { + VM_ARGS.put("-Djava.security.auth.login.config=", + (CLIENT_SRC_DIR.resolve(CLIENT_PKG).resolve("jaas.conf") + .toRealPath().toString())); + + String baseFolderName = (clientModuleType + "_") + + (serviceModuletype + "_") + + ((addMetaInfDescriptor) ? "DESCRIPTOR" : "NO_DESCRIPTOR"); + Path baseFolderPath = BUILD_DIR.resolve(baseFolderName); + Path clientCompilePath = baseFolderPath.resolve(CLIENT_MODULE_NAME); + Path serviceCompilePath + = baseFolderPath.resolve(SERVICE_MODULE_NAME); + Path jarPath = baseFolderPath.resolve(JAR_DIR); + Path clientJarFilePath + = (clientModuleType != MODULE_TYPE.UN_NAMED) + ? jarPath.resolve(JAR_MODULE).resolve(CLIENT_JAR) + : jarPath.resolve(CLIENT_JAR); + Path serviceJarFilePath + = (serviceModuletype != MODULE_TYPE.UN_NAMED) + ? jarPath.resolve(JAR_MODULE).resolve(SERVICE_JAR) + : jarPath.resolve(SERVICE_JAR); + System.out.println(String.format( + "Started Test case type '%s'", baseFolderName)); + + ModuleDescriptor serviceModuleDescriptor + = generateServiceModuleDescriptor(serviceModuletype, + SERVICE_MODULE_NAME, SERVICE_PKG, + LOGIN_SERVICE_INTERFACE, SERVICE_IMPL, + REQUIRED_MODULES); + ModuleDescriptor clientModuleDescriptor + = generateClientModuleDescriptor( + clientModuleType, serviceModuletype, + CLIENT_MODULE_NAME, CLIENT_PKG, + LOGIN_SERVICE_INTERFACE, SERVICE_MODULE_NAME, + REQUIRED_MODULES); + System.out.println(String.format( + "Service Descriptor '%s'", serviceModuleDescriptor)); + System.out.println(String.format( + "Client Descriptor '%s'", clientModuleDescriptor)); + + compileAndCreateJar(SERVICE_SRC_DIR, serviceCompilePath, + serviceJarFilePath, serviceModuleDescriptor, + addMetaInfDescriptor, SERVICE_IMPL, + META_SERVICE_DESCRIPTOR); + compileAndCreateJar(CLIENT_SRC_DIR, clientCompilePath, + clientJarFilePath, clientModuleDescriptor, + false, null, null); + + System.out.println("Started executing java client with required" + + " custom service"); + String moduleName = (clientModuleType == MODULE_TYPE.STRICT) + ? CLIENT_MODULE_NAME + : ((clientModuleType == MODULE_TYPE.AUTO) + ? CLIENT_PKG : ""); + OutputAnalyzer output = ProcessTools.executeTestJava( + getJavaCommand(clientModuleType, serviceModuletype, + clientJarFilePath, serviceJarFilePath, moduleName, + MAIN_CLASS, VM_ARGS)) + .outputTo(System.out) + .errorTo(System.out); + + if (output.getExitValue() != 0) { + if (expectedFailure != null + && output.getOutput().contains(expectedFailure)) { + System.out.println("PASS: Test is expected to fail here."); + System.out.println("------------------------------------"); + } else { + throw new RuntimeException("Unexpected failure occured."); + } + } + } catch (Exception e) { + e.printStackTrace(System.out); + result = false; + } + + return (expectedResult == result); + } + +} --- /dev/null 2015-07-25 05:49:27.889711346 -0700 +++ new/test/java/security/jigsaw/jaas/src/jaasclientmodule/client/JaasClient.java 2015-10-13 23:00:07.511939429 -0700 @@ -0,0 +1,103 @@ +/* + * 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. + */ +package client; + +import java.io.IOException; +import java.security.Principal; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.LoginException; +import javax.security.auth.login.LoginContext; +import com.sun.security.auth.UnixPrincipal; + +/** + * JAAS client which will try to authenticate a user through a custom JAAS LOGIN + * Module. + */ +public class JaasClient { + + private static final String USER_NAME = "testUser"; + private static final String PASSWORD = "testPassword"; + private static final String LOGIN_CONTEXT = "ModularLoginConf"; + + public static void main(String[] args) { + try { + LoginContext lc = new LoginContext(LOGIN_CONTEXT, + new MyCallbackHandler()); + lc.login(); + checkPrincipal(lc, true); + lc.logout(); + checkPrincipal(lc, false); + } catch (LoginException le) { + throw new RuntimeException(le); + } + System.out.println("Test passed."); + + } + + /* + * 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."); + } + return; + } + 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); + } + } + } + } + +} --- /dev/null 2015-07-25 05:49:27.889711346 -0700 +++ new/test/java/security/jigsaw/jaas/src/jaasclientmodule/client/jaas.conf 2015-10-13 23:00:08.037933242 -0700 @@ -0,0 +1,3 @@ +ModularLoginConf { + login.TestLoginModule required; +}; \ No newline at end of file --- /dev/null 2015-07-25 05:49:27.889711346 -0700 +++ new/test/java/security/jigsaw/jaas/src/jaasloginmodule/login/TestLoginModule.java 2015-10-13 23:00:08.544927277 -0700 @@ -0,0 +1,140 @@ +/* + * 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. + */ +package login; + +import java.io.IOException; +import java.util.Map; +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; +import com.sun.security.auth.UnixPrincipal; + +/** + * Custom JAAS login module which will be loaded through Java LoginContext when + * it is bundled by Strict/Auto/Unnamed modules. + */ +public class TestLoginModule implements LoginModule { + + private static final String USER_NAME = "testUser"; + private static final String PASSWORD = "testPassword"; + private Subject subject; + private CallbackHandler callbackHandler; + private UnixPrincipal userPrincipal; + private String username; + private String password; + 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; + System.out.println(String.format( + "'%s' login module initialized", this.getClass())); + } + + /* + * Authenticate the user by prompting for a username and password. + */ + @Override + public boolean login() throws LoginException { + 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()); + System.out.println(String.format("'%s' login module found username" + + " as '%s' and password as '%s'", this.getClass(), + username, password)); + if (username.equals(USER_NAME) + && password.equals(PASSWORD)) { + System.out.println(String.format("'%s' login module " + + "authentication done successfully", this.getClass())); + succeeded = true; + return true; + } + throw new IllegalArgumentException("Incorrect username/password!"); + } catch (IOException | UnsupportedCallbackException e) { + throw new LoginException("Login failed: " + e.getMessage()); + } + } + + @Override + public boolean commit() throws LoginException { + if (succeeded == false) { + return false; + } + userPrincipal = new UnixPrincipal(username); + if (!subject.getPrincipals().contains(userPrincipal)) { + subject.getPrincipals().add(userPrincipal); + } + System.out.println(String.format("'%s' login module authentication " + + "committed", this.getClass())); + password = null; + commitSucceeded = true; + return true; + } + + @Override + public boolean abort() throws LoginException { + if (succeeded == false) { + return false; + } + System.out.println(String.format( + "'%s' login module aborted", this.getClass())); + clearState(); + return true; + } + + @Override + public boolean logout() throws LoginException { + clearState(); + System.out.println(String.format( + "'%s' login module logout completed", this.getClass())); + return true; + } + + private void clearState() { + if (commitSucceeded) { + subject.getPrincipals().remove(userPrincipal); + } + username = null; + password = null; + userPrincipal = null; + } +}