mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-06 06:00:26 +00:00
8048138: Tests for JAAS callbacks
Reviewed-by: weijun
This commit is contained in:
parent
ff227ec11f
commit
f3a11c507f
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* 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.IOException;
|
||||
import java.security.Principal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
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.ChoiceCallback;
|
||||
import javax.security.auth.callback.ConfirmationCallback;
|
||||
import javax.security.auth.callback.LanguageCallback;
|
||||
import javax.security.auth.callback.NameCallback;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.TextInputCallback;
|
||||
import javax.security.auth.callback.TextOutputCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import javax.security.auth.login.FailedLoginException;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
|
||||
public class CustomLoginModule implements LoginModule {
|
||||
|
||||
static final String HELLO = "Hello";
|
||||
|
||||
private Subject subject;
|
||||
private CallbackHandler callbackHandler;
|
||||
private boolean loginSucceeded = false;
|
||||
private String username;
|
||||
private char[] password;
|
||||
|
||||
/*
|
||||
* Initialize this LoginModule.
|
||||
*/
|
||||
@Override
|
||||
public void initialize(Subject subject, CallbackHandler callbackHandler,
|
||||
Map<String, ?> sharedState, Map<String, ?> options) {
|
||||
this.subject = subject;
|
||||
this.callbackHandler = callbackHandler;
|
||||
|
||||
// check if custom parameter is passed from comfiguration
|
||||
if (options == null) {
|
||||
throw new RuntimeException("options is null");
|
||||
}
|
||||
|
||||
// read username/password from configuration
|
||||
Object o = options.get("username");
|
||||
if (o == null) {
|
||||
throw new RuntimeException("Custom parameter not passed");
|
||||
}
|
||||
if (!(o instanceof String)) {
|
||||
throw new RuntimeException("Password is not a string");
|
||||
}
|
||||
username = (String) o;
|
||||
|
||||
o = options.get("password");
|
||||
if (o == null) {
|
||||
throw new RuntimeException("Custom parameter not passed");
|
||||
}
|
||||
if (!(o instanceof String)) {
|
||||
throw new RuntimeException("Password is not a string");
|
||||
}
|
||||
password = ((String) o).toCharArray();
|
||||
}
|
||||
|
||||
/*
|
||||
* Authenticate the user.
|
||||
*/
|
||||
@Override
|
||||
public boolean login() throws LoginException {
|
||||
// prompt for a user name and password
|
||||
if (callbackHandler == null) {
|
||||
throw new LoginException("No CallbackHandler available");
|
||||
}
|
||||
|
||||
// standard callbacks
|
||||
NameCallback name = new NameCallback("username: ", "default");
|
||||
PasswordCallback passwd = new PasswordCallback("password: ", false);
|
||||
|
||||
LanguageCallback language = new LanguageCallback();
|
||||
|
||||
TextOutputCallback error = new TextOutputCallback(
|
||||
TextOutputCallback.ERROR, "This is an error");
|
||||
TextOutputCallback warning = new TextOutputCallback(
|
||||
TextOutputCallback.WARNING, "This is a warning");
|
||||
TextOutputCallback info = new TextOutputCallback(
|
||||
TextOutputCallback.INFORMATION, "This is a FYI");
|
||||
|
||||
TextInputCallback text = new TextInputCallback("Please type " + HELLO,
|
||||
"Bye");
|
||||
|
||||
ChoiceCallback choice = new ChoiceCallback("Choice: ",
|
||||
new String[] { "pass", "fail" }, 1, true);
|
||||
|
||||
ConfirmationCallback confirmation = new ConfirmationCallback(
|
||||
"confirmation: ", ConfirmationCallback.INFORMATION,
|
||||
ConfirmationCallback.YES_NO_OPTION, ConfirmationCallback.NO);
|
||||
|
||||
CustomCallback custom = new CustomCallback();
|
||||
|
||||
Callback[] callbacks = new Callback[] {
|
||||
choice, info, warning, error, name, passwd, text, language,
|
||||
confirmation, custom
|
||||
};
|
||||
|
||||
boolean uce = false;
|
||||
try {
|
||||
callbackHandler.handle(callbacks);
|
||||
} catch (UnsupportedCallbackException e) {
|
||||
Callback callback = e.getCallback();
|
||||
if (custom.equals(callback)) {
|
||||
uce = true;
|
||||
System.out.println("CustomLoginModule: "
|
||||
+ "custom callback not supported as expected");
|
||||
} else {
|
||||
throw new LoginException("Unsupported callback: " + callback);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new LoginException(ioe.toString());
|
||||
}
|
||||
|
||||
if (!uce) {
|
||||
throw new RuntimeException("UnsupportedCallbackException "
|
||||
+ "not thrown");
|
||||
}
|
||||
|
||||
if (!HELLO.equals(text.getText())) {
|
||||
System.out.println("Text: " + text.getText());
|
||||
throw new FailedLoginException("No hello");
|
||||
}
|
||||
|
||||
if (!Locale.GERMANY.equals(language.getLocale())) {
|
||||
System.out.println("Selected locale: " + language.getLocale());
|
||||
throw new FailedLoginException("Achtung bitte");
|
||||
}
|
||||
|
||||
String readUsername = name.getName();
|
||||
char[] readPassword = passwd.getPassword();
|
||||
if (readPassword == null) {
|
||||
// treat a NULL password as an empty password
|
||||
readPassword = new char[0];
|
||||
}
|
||||
passwd.clearPassword();
|
||||
|
||||
// verify the username/password
|
||||
if (!username.equals(readUsername)
|
||||
|| !Arrays.equals(password, readPassword)) {
|
||||
loginSucceeded = false;
|
||||
throw new FailedLoginException("Username/password is not correct");
|
||||
}
|
||||
|
||||
// check chosen option
|
||||
int[] selected = choice.getSelectedIndexes();
|
||||
if (selected == null || selected.length == 0) {
|
||||
throw new FailedLoginException("Nothing selected");
|
||||
}
|
||||
|
||||
if (selected[0] != 0) {
|
||||
throw new FailedLoginException("Wrong choice: " + selected[0]);
|
||||
}
|
||||
|
||||
// check confirmation
|
||||
if (confirmation.getSelectedIndex() != ConfirmationCallback.YES) {
|
||||
throw new FailedLoginException("Not confirmed: "
|
||||
+ confirmation.getSelectedIndex());
|
||||
}
|
||||
|
||||
loginSucceeded = true;
|
||||
System.out.println("CustomLoginModule: authentication succeeded");
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called if the LoginContext's overall authentication
|
||||
* succeeded.
|
||||
*/
|
||||
@Override
|
||||
public boolean commit() throws LoginException {
|
||||
if (loginSucceeded) {
|
||||
// add a Principal to the Subject
|
||||
Principal principal = new TestPrincipal(username);
|
||||
if (!subject.getPrincipals().contains(principal)) {
|
||||
subject.getPrincipals().add(principal);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called if the LoginContext's overall authentication
|
||||
* failed.
|
||||
*/
|
||||
@Override
|
||||
public boolean abort() throws LoginException {
|
||||
loginSucceeded = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Logout the user.
|
||||
*/
|
||||
@Override
|
||||
public boolean logout() throws LoginException {
|
||||
loginSucceeded = false;
|
||||
boolean removed = subject.getPrincipals().remove(
|
||||
new TestPrincipal(username));
|
||||
if (!removed) {
|
||||
throw new LoginException("Coundn't remove a principal: "
|
||||
+ username);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static class TestPrincipal implements Principal {
|
||||
|
||||
private final String name;
|
||||
|
||||
public TestPrincipal(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return("TestPrincipal [name =" + name + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(o instanceof TestPrincipal)) {
|
||||
return false;
|
||||
}
|
||||
TestPrincipal other = (TestPrincipal) o;
|
||||
return name != null ? name.equals(other.name) : other.name == null;
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomCallback implements Callback {}
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.util.Map;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.login.LoginContext;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8048138
|
||||
* @summary Check if shared state is passed to login module
|
||||
* @run main/othervm SharedState
|
||||
*/
|
||||
public class SharedState {
|
||||
|
||||
static final String NAME = "name";
|
||||
static final String VALUE = "shared";
|
||||
|
||||
public static void main(String[] args) throws LoginException {
|
||||
System.setProperty("java.security.auth.login.config",
|
||||
System.getProperty("test.src")
|
||||
+ System.getProperty("file.separator")
|
||||
+ "shared.config");
|
||||
|
||||
new LoginContext("SharedState").login();
|
||||
}
|
||||
|
||||
public static abstract class Module implements LoginModule {
|
||||
|
||||
@Override
|
||||
public boolean login() throws LoginException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commit() throws LoginException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean abort() throws LoginException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean logout() throws LoginException {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FirstModule extends Module {
|
||||
|
||||
@Override
|
||||
public void initialize(Subject subject, CallbackHandler callbackHandler,
|
||||
Map<String,?> sharedState, Map<String,?> options) {
|
||||
((Map)sharedState).put(NAME, VALUE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class SecondModule extends Module {
|
||||
|
||||
@Override
|
||||
public void initialize(Subject subject, CallbackHandler callbackHandler,
|
||||
Map<String,?> sharedState, Map<String,?> options) {
|
||||
// check shared object
|
||||
Object shared = sharedState.get(NAME);
|
||||
if (!VALUE.equals(shared)) {
|
||||
throw new RuntimeException("Unexpected shared object: "
|
||||
+ shared);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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.security.Principal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.ChoiceCallback;
|
||||
import javax.security.auth.callback.ConfirmationCallback;
|
||||
import javax.security.auth.callback.LanguageCallback;
|
||||
import javax.security.auth.callback.NameCallback;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.TextInputCallback;
|
||||
import javax.security.auth.callback.TextOutputCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import javax.security.auth.login.LoginContext;
|
||||
import javax.security.auth.login.LoginException;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8048138
|
||||
* @summary Checks if JAAS login works fine with standard callbacks
|
||||
* @compile DefaultHandlerModule.java
|
||||
* @run main/othervm StandardCallbacks
|
||||
*/
|
||||
public class StandardCallbacks {
|
||||
|
||||
private static final String USERNAME = "username";
|
||||
private static final char[] PASSWORD = "password".toCharArray();
|
||||
|
||||
public static void main(String[] args) throws LoginException {
|
||||
System.setProperty("java.security.auth.login.config",
|
||||
System.getProperty("test.src")
|
||||
+ System.getProperty("file.separator")
|
||||
+ "custom.config");
|
||||
|
||||
CustomCallbackHandler handler = new CustomCallbackHandler(USERNAME);
|
||||
LoginContext context = new LoginContext("StandardCallbacks", handler);
|
||||
|
||||
handler.setPassword(PASSWORD);
|
||||
System.out.println("Try to login with correct password, "
|
||||
+ "successful authentication is expected");
|
||||
context.login();
|
||||
System.out.println("Authentication succeeded!");
|
||||
|
||||
Subject subject = context.getSubject();
|
||||
System.out.println("Authenticated user has the following principals ["
|
||||
+ subject.getPrincipals().size() + " ]:");
|
||||
boolean found = true;
|
||||
for (Principal principal : subject.getPrincipals()) {
|
||||
System.out.println("principal: " + principal);
|
||||
if (principal instanceof CustomLoginModule.TestPrincipal) {
|
||||
CustomLoginModule.TestPrincipal testPrincipal =
|
||||
(CustomLoginModule.TestPrincipal) principal;
|
||||
if (USERNAME.equals(testPrincipal.getName())) {
|
||||
System.out.println("Found test principal: "
|
||||
+ testPrincipal);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
throw new RuntimeException("TestPrincipal not found");
|
||||
}
|
||||
|
||||
// check if all expected text output callbacks have been called
|
||||
if (!handler.info) {
|
||||
throw new RuntimeException("TextOutputCallback.INFO not called");
|
||||
}
|
||||
|
||||
if (!handler.warning) {
|
||||
throw new RuntimeException("TextOutputCallback.WARNING not called");
|
||||
}
|
||||
|
||||
if (!handler.error) {
|
||||
throw new RuntimeException("TextOutputCallback.ERROR not called");
|
||||
}
|
||||
|
||||
System.out.println("Authenticated user has the following public "
|
||||
+ "credentials [" + subject.getPublicCredentials().size()
|
||||
+ "]:");
|
||||
subject.getPublicCredentials().stream().
|
||||
forEach((o) -> {
|
||||
System.out.println("public credential: " + o);
|
||||
});
|
||||
|
||||
context.logout();
|
||||
|
||||
System.out.println("Test passed");
|
||||
}
|
||||
|
||||
private static class CustomCallbackHandler implements CallbackHandler {
|
||||
|
||||
private final String username;
|
||||
private char[] password;
|
||||
private boolean info = false;
|
||||
private boolean warning = false;
|
||||
private boolean error = false;
|
||||
|
||||
CustomCallbackHandler(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
void setPassword(char[] password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Callback[] callbacks)
|
||||
throws UnsupportedCallbackException {
|
||||
for (Callback callback : callbacks) {
|
||||
if (callback instanceof TextOutputCallback) {
|
||||
TextOutputCallback toc = (TextOutputCallback) callback;
|
||||
switch (toc.getMessageType()) {
|
||||
case TextOutputCallback.INFORMATION:
|
||||
System.out.println("INFO: " + toc.getMessage());
|
||||
info = true;
|
||||
break;
|
||||
case TextOutputCallback.ERROR:
|
||||
System.out.println("ERROR: " + toc.getMessage());
|
||||
error = true;
|
||||
break;
|
||||
case TextOutputCallback.WARNING:
|
||||
System.out.println("WARNING: " + toc.getMessage());
|
||||
warning = true;
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedCallbackException(toc,
|
||||
"Unsupported message type: "
|
||||
+ toc.getMessageType());
|
||||
}
|
||||
} else if (callback instanceof TextInputCallback) {
|
||||
TextInputCallback tic = (TextInputCallback) callback;
|
||||
System.out.println(tic.getPrompt());
|
||||
tic.setText(CustomLoginModule.HELLO);
|
||||
} else if (callback instanceof LanguageCallback) {
|
||||
LanguageCallback lc = (LanguageCallback) callback;
|
||||
lc.setLocale(Locale.GERMANY);
|
||||
} else if (callback instanceof ConfirmationCallback) {
|
||||
ConfirmationCallback cc = (ConfirmationCallback) callback;
|
||||
System.out.println(cc.getPrompt());
|
||||
cc.setSelectedIndex(ConfirmationCallback.YES);
|
||||
} else if (callback instanceof ChoiceCallback) {
|
||||
ChoiceCallback cc = (ChoiceCallback) callback;
|
||||
System.out.println(cc.getPrompt()
|
||||
+ Arrays.toString(cc.getChoices()));
|
||||
cc.setSelectedIndex(0);
|
||||
} else if (callback instanceof NameCallback) {
|
||||
NameCallback nc = (NameCallback) callback;
|
||||
System.out.println(nc.getPrompt());
|
||||
nc.setName(username);
|
||||
} else if (callback instanceof PasswordCallback) {
|
||||
PasswordCallback pc = (PasswordCallback) callback;
|
||||
System.out.println(pc.getPrompt());
|
||||
pc.setPassword(password);
|
||||
} else {
|
||||
throw new UnsupportedCallbackException(callback,
|
||||
"Unknown callback");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
StandardCallbacks {
|
||||
DefaultHandlerModule required;
|
||||
CustomLoginModule required username="username" password="password";
|
||||
};
|
||||
@ -0,0 +1,4 @@
|
||||
SharedState {
|
||||
SharedState$FirstModule required;
|
||||
SharedState$SecondModule required;
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user