/* * Copyright 2000-2006 Sun Microsystems, Inc. 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. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package com.sun.security.auth.callback; /* JAAS imports */ import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.ConfirmationCallback; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.TextOutputCallback; import javax.security.auth.callback.UnsupportedCallbackException; /* Java imports */ import java.awt.Component; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.Box; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPasswordField; import javax.swing.JTextField; /** * <p> * Uses a Swing dialog window to query the user for answers to * authentication questions. * This can be used by a JAAS application to instantiate a * CallbackHandler * @see javax.security.auth.callback */ public class DialogCallbackHandler implements CallbackHandler { /* -- Fields -- */ /* The parent window, or null if using the default parent */ private Component parentComponent; private static final int JPasswordFieldLen = 8 ; private static final int JTextFieldLen = 8 ; /* -- Methods -- */ /** * Creates a callback dialog with the default parent window. */ public DialogCallbackHandler() { } /** * Creates a callback dialog and specify the parent window. * * @param parentComponent the parent window -- specify <code>null</code> * for the default parent */ public DialogCallbackHandler(Component parentComponent) { this.parentComponent = parentComponent; } /* * An interface for recording actions to carry out if the user * clicks OK for the dialog. */ private static interface Action { void perform(); } /** * Handles the specified set of callbacks. * * @param callbacks the callbacks to handle * @throws UnsupportedCallbackException if the callback is not an * instance of NameCallback or PasswordCallback */ public void handle(Callback[] callbacks) throws UnsupportedCallbackException { /* Collect messages to display in the dialog */ final List<Object> messages = new ArrayList<Object>(3); /* Collection actions to perform if the user clicks OK */ final List<Action> okActions = new ArrayList<Action>(2); ConfirmationInfo confirmation = new ConfirmationInfo(); for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] instanceof TextOutputCallback) { TextOutputCallback tc = (TextOutputCallback) callbacks[i]; switch (tc.getMessageType()) { case TextOutputCallback.INFORMATION: confirmation.messageType = JOptionPane.INFORMATION_MESSAGE; break; case TextOutputCallback.WARNING: confirmation.messageType = JOptionPane.WARNING_MESSAGE; break; case TextOutputCallback.ERROR: confirmation.messageType = JOptionPane.ERROR_MESSAGE; break; default: throw new UnsupportedCallbackException( callbacks[i], "Unrecognized message type"); } messages.add(tc.getMessage()); } else if (callbacks[i] instanceof NameCallback) { final NameCallback nc = (NameCallback) callbacks[i]; JLabel prompt = new JLabel(nc.getPrompt()); final JTextField name = new JTextField(JTextFieldLen); String defaultName = nc.getDefaultName(); if (defaultName != null) { name.setText(defaultName); } /* * Put the prompt and name in a horizontal box, * and add that to the set of messages. */ Box namePanel = Box.createHorizontalBox(); namePanel.add(prompt); namePanel.add(name); messages.add(namePanel); /* Store the name back into the callback if OK */ okActions.add(new Action() { public void perform() { nc.setName(name.getText()); } }); } else if (callbacks[i] instanceof PasswordCallback) { final PasswordCallback pc = (PasswordCallback) callbacks[i]; JLabel prompt = new JLabel(pc.getPrompt()); final JPasswordField password = new JPasswordField(JPasswordFieldLen); if (!pc.isEchoOn()) { password.setEchoChar('*'); } Box passwordPanel = Box.createHorizontalBox(); passwordPanel.add(prompt); passwordPanel.add(password); messages.add(passwordPanel); okActions.add(new Action() { public void perform() { pc.setPassword(password.getPassword()); } }); } else if (callbacks[i] instanceof ConfirmationCallback) { ConfirmationCallback cc = (ConfirmationCallback)callbacks[i]; confirmation.setCallback(cc); if (cc.getPrompt() != null) { messages.add(cc.getPrompt()); } } else { throw new UnsupportedCallbackException( callbacks[i], "Unrecognized Callback"); } } /* Display the dialog */ int result = JOptionPane.showOptionDialog( parentComponent, messages.toArray(), "Confirmation", /* title */ confirmation.optionType, confirmation.messageType, null, /* icon */ confirmation.options, /* options */ confirmation.initialValue); /* initialValue */ /* Perform the OK actions */ if (result == JOptionPane.OK_OPTION || result == JOptionPane.YES_OPTION) { Iterator<Action> iterator = okActions.iterator(); while (iterator.hasNext()) { iterator.next().perform(); } } confirmation.handleResult(result); } /* * Provides assistance with translating between JAAS and Swing * confirmation dialogs. */ private static class ConfirmationInfo { private int[] translations; int optionType = JOptionPane.OK_CANCEL_OPTION; Object[] options = null; Object initialValue = null; int messageType = JOptionPane.QUESTION_MESSAGE; private ConfirmationCallback callback; /* Set the confirmation callback handler */ void setCallback(ConfirmationCallback callback) throws UnsupportedCallbackException { this.callback = callback; int confirmationOptionType = callback.getOptionType(); switch (confirmationOptionType) { case ConfirmationCallback.YES_NO_OPTION: optionType = JOptionPane.YES_NO_OPTION; translations = new int[] { JOptionPane.YES_OPTION, ConfirmationCallback.YES, JOptionPane.NO_OPTION, ConfirmationCallback.NO, JOptionPane.CLOSED_OPTION, ConfirmationCallback.NO }; break; case ConfirmationCallback.YES_NO_CANCEL_OPTION: optionType = JOptionPane.YES_NO_CANCEL_OPTION; translations = new int[] { JOptionPane.YES_OPTION, ConfirmationCallback.YES, JOptionPane.NO_OPTION, ConfirmationCallback.NO, JOptionPane.CANCEL_OPTION, ConfirmationCallback.CANCEL, JOptionPane.CLOSED_OPTION, ConfirmationCallback.CANCEL }; break; case ConfirmationCallback.OK_CANCEL_OPTION: optionType = JOptionPane.OK_CANCEL_OPTION; translations = new int[] { JOptionPane.OK_OPTION, ConfirmationCallback.OK, JOptionPane.CANCEL_OPTION, ConfirmationCallback.CANCEL, JOptionPane.CLOSED_OPTION, ConfirmationCallback.CANCEL }; break; case ConfirmationCallback.UNSPECIFIED_OPTION: options = callback.getOptions(); /* * There's no way to know if the default option means * to cancel the login, but there isn't a better way * to guess this. */ translations = new int[] { JOptionPane.CLOSED_OPTION, callback.getDefaultOption() }; break; default: throw new UnsupportedCallbackException( callback, "Unrecognized option type: " + confirmationOptionType); } int confirmationMessageType = callback.getMessageType(); switch (confirmationMessageType) { case ConfirmationCallback.WARNING: messageType = JOptionPane.WARNING_MESSAGE; break; case ConfirmationCallback.ERROR: messageType = JOptionPane.ERROR_MESSAGE; break; case ConfirmationCallback.INFORMATION: messageType = JOptionPane.INFORMATION_MESSAGE; break; default: throw new UnsupportedCallbackException( callback, "Unrecognized message type: " + confirmationMessageType); } } /* Process the result returned by the Swing dialog */ void handleResult(int result) { if (callback == null) { return; } for (int i = 0; i < translations.length; i += 2) { if (translations[i] == result) { result = translations[i + 1]; break; } } callback.setSelectedIndex(result); } } }