001: /*
002: * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.security.auth.callback;
027:
028: /* JAAS imports */
029: import javax.security.auth.callback.Callback;
030: import javax.security.auth.callback.CallbackHandler;
031: import javax.security.auth.callback.ConfirmationCallback;
032: import javax.security.auth.callback.NameCallback;
033: import javax.security.auth.callback.PasswordCallback;
034: import javax.security.auth.callback.TextOutputCallback;
035: import javax.security.auth.callback.UnsupportedCallbackException;
036:
037: /* Java imports */
038: import java.awt.Component;
039: import java.util.ArrayList;
040: import java.util.Iterator;
041: import java.util.List;
042: import javax.swing.Box;
043: import javax.swing.JLabel;
044: import javax.swing.JOptionPane;
045: import javax.swing.JPasswordField;
046: import javax.swing.JTextField;
047:
048: /**
049: * <p>
050: * Uses a Swing dialog window to query the user for answers to
051: * authentication questions.
052: * This can be used by a JAAS application to instantiate a
053: * CallbackHandler
054: * @see javax.security.auth.callback
055: */
056: public class DialogCallbackHandler implements CallbackHandler {
057:
058: /* -- Fields -- */
059:
060: /* The parent window, or null if using the default parent */
061: private Component parentComponent;
062: private static final int JPasswordFieldLen = 8;
063: private static final int JTextFieldLen = 8;
064:
065: /* -- Methods -- */
066:
067: /**
068: * Creates a callback dialog with the default parent window.
069: */
070: public DialogCallbackHandler() {
071: }
072:
073: /**
074: * Creates a callback dialog and specify the parent window.
075: *
076: * @param parentComponent the parent window -- specify <code>null</code>
077: * for the default parent
078: */
079: public DialogCallbackHandler(Component parentComponent) {
080: this .parentComponent = parentComponent;
081: }
082:
083: /*
084: * An interface for recording actions to carry out if the user
085: * clicks OK for the dialog.
086: */
087: private static interface Action {
088: void perform();
089: }
090:
091: /**
092: * Handles the specified set of callbacks.
093: *
094: * @param callbacks the callbacks to handle
095: * @throws UnsupportedCallbackException if the callback is not an
096: * instance of NameCallback or PasswordCallback
097: */
098:
099: public void handle(Callback[] callbacks)
100: throws UnsupportedCallbackException {
101: /* Collect messages to display in the dialog */
102: final List<Object> messages = new ArrayList<Object>(3);
103:
104: /* Collection actions to perform if the user clicks OK */
105: final List<Action> okActions = new ArrayList<Action>(2);
106:
107: ConfirmationInfo confirmation = new ConfirmationInfo();
108:
109: for (int i = 0; i < callbacks.length; i++) {
110: if (callbacks[i] instanceof TextOutputCallback) {
111: TextOutputCallback tc = (TextOutputCallback) callbacks[i];
112:
113: switch (tc.getMessageType()) {
114: case TextOutputCallback.INFORMATION:
115: confirmation.messageType = JOptionPane.INFORMATION_MESSAGE;
116: break;
117: case TextOutputCallback.WARNING:
118: confirmation.messageType = JOptionPane.WARNING_MESSAGE;
119: break;
120: case TextOutputCallback.ERROR:
121: confirmation.messageType = JOptionPane.ERROR_MESSAGE;
122: break;
123: default:
124: throw new UnsupportedCallbackException(
125: callbacks[i], "Unrecognized message type");
126: }
127:
128: messages.add(tc.getMessage());
129:
130: } else if (callbacks[i] instanceof NameCallback) {
131: final NameCallback nc = (NameCallback) callbacks[i];
132:
133: JLabel prompt = new JLabel(nc.getPrompt());
134:
135: final JTextField name = new JTextField(JTextFieldLen);
136: String defaultName = nc.getDefaultName();
137: if (defaultName != null) {
138: name.setText(defaultName);
139: }
140:
141: /*
142: * Put the prompt and name in a horizontal box,
143: * and add that to the set of messages.
144: */
145: Box namePanel = Box.createHorizontalBox();
146: namePanel.add(prompt);
147: namePanel.add(name);
148: messages.add(namePanel);
149:
150: /* Store the name back into the callback if OK */
151: okActions.add(new Action() {
152: public void perform() {
153: nc.setName(name.getText());
154: }
155: });
156:
157: } else if (callbacks[i] instanceof PasswordCallback) {
158: final PasswordCallback pc = (PasswordCallback) callbacks[i];
159:
160: JLabel prompt = new JLabel(pc.getPrompt());
161:
162: final JPasswordField password = new JPasswordField(
163: JPasswordFieldLen);
164: if (!pc.isEchoOn()) {
165: password.setEchoChar('*');
166: }
167:
168: Box passwordPanel = Box.createHorizontalBox();
169: passwordPanel.add(prompt);
170: passwordPanel.add(password);
171: messages.add(passwordPanel);
172:
173: okActions.add(new Action() {
174: public void perform() {
175: pc.setPassword(password.getPassword());
176: }
177: });
178:
179: } else if (callbacks[i] instanceof ConfirmationCallback) {
180: ConfirmationCallback cc = (ConfirmationCallback) callbacks[i];
181:
182: confirmation.setCallback(cc);
183: if (cc.getPrompt() != null) {
184: messages.add(cc.getPrompt());
185: }
186:
187: } else {
188: throw new UnsupportedCallbackException(callbacks[i],
189: "Unrecognized Callback");
190: }
191: }
192:
193: /* Display the dialog */
194: int result = JOptionPane.showOptionDialog(parentComponent,
195: messages.toArray(), "Confirmation", /* title */
196: confirmation.optionType, confirmation.messageType,
197: null, /* icon */
198: confirmation.options, /* options */
199: confirmation.initialValue); /* initialValue */
200:
201: /* Perform the OK actions */
202: if (result == JOptionPane.OK_OPTION
203: || result == JOptionPane.YES_OPTION) {
204: Iterator<Action> iterator = okActions.iterator();
205: while (iterator.hasNext()) {
206: iterator.next().perform();
207: }
208: }
209: confirmation.handleResult(result);
210: }
211:
212: /*
213: * Provides assistance with translating between JAAS and Swing
214: * confirmation dialogs.
215: */
216: private static class ConfirmationInfo {
217:
218: private int[] translations;
219:
220: int optionType = JOptionPane.OK_CANCEL_OPTION;
221: Object[] options = null;
222: Object initialValue = null;
223:
224: int messageType = JOptionPane.QUESTION_MESSAGE;
225:
226: private ConfirmationCallback callback;
227:
228: /* Set the confirmation callback handler */
229: void setCallback(ConfirmationCallback callback)
230: throws UnsupportedCallbackException {
231: this .callback = callback;
232:
233: int confirmationOptionType = callback.getOptionType();
234: switch (confirmationOptionType) {
235: case ConfirmationCallback.YES_NO_OPTION:
236: optionType = JOptionPane.YES_NO_OPTION;
237: translations = new int[] { JOptionPane.YES_OPTION,
238: ConfirmationCallback.YES,
239: JOptionPane.NO_OPTION, ConfirmationCallback.NO,
240: JOptionPane.CLOSED_OPTION,
241: ConfirmationCallback.NO };
242: break;
243: case ConfirmationCallback.YES_NO_CANCEL_OPTION:
244: optionType = JOptionPane.YES_NO_CANCEL_OPTION;
245: translations = new int[] { JOptionPane.YES_OPTION,
246: ConfirmationCallback.YES,
247: JOptionPane.NO_OPTION, ConfirmationCallback.NO,
248: JOptionPane.CANCEL_OPTION,
249: ConfirmationCallback.CANCEL,
250: JOptionPane.CLOSED_OPTION,
251: ConfirmationCallback.CANCEL };
252: break;
253: case ConfirmationCallback.OK_CANCEL_OPTION:
254: optionType = JOptionPane.OK_CANCEL_OPTION;
255: translations = new int[] { JOptionPane.OK_OPTION,
256: ConfirmationCallback.OK,
257: JOptionPane.CANCEL_OPTION,
258: ConfirmationCallback.CANCEL,
259: JOptionPane.CLOSED_OPTION,
260: ConfirmationCallback.CANCEL };
261: break;
262: case ConfirmationCallback.UNSPECIFIED_OPTION:
263: options = callback.getOptions();
264: /*
265: * There's no way to know if the default option means
266: * to cancel the login, but there isn't a better way
267: * to guess this.
268: */
269: translations = new int[] { JOptionPane.CLOSED_OPTION,
270: callback.getDefaultOption() };
271: break;
272: default:
273: throw new UnsupportedCallbackException(callback,
274: "Unrecognized option type: "
275: + confirmationOptionType);
276: }
277:
278: int confirmationMessageType = callback.getMessageType();
279: switch (confirmationMessageType) {
280: case ConfirmationCallback.WARNING:
281: messageType = JOptionPane.WARNING_MESSAGE;
282: break;
283: case ConfirmationCallback.ERROR:
284: messageType = JOptionPane.ERROR_MESSAGE;
285: break;
286: case ConfirmationCallback.INFORMATION:
287: messageType = JOptionPane.INFORMATION_MESSAGE;
288: break;
289: default:
290: throw new UnsupportedCallbackException(callback,
291: "Unrecognized message type: "
292: + confirmationMessageType);
293: }
294: }
295:
296: /* Process the result returned by the Swing dialog */
297: void handleResult(int result) {
298: if (callback == null) {
299: return;
300: }
301:
302: for (int i = 0; i < translations.length; i += 2) {
303: if (translations[i] == result) {
304: result = translations[i + 1];
305: break;
306: }
307: }
308: callback.setSelectedIndex(result);
309: }
310: }
311: }
|