blob: 3b17870eebf82cd018d21bd4e09b7ebf421720cd [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.security.auth.callback;
27
28/* JAAS imports */
29import javax.security.auth.callback.Callback;
30import javax.security.auth.callback.CallbackHandler;
31import javax.security.auth.callback.ConfirmationCallback;
32import javax.security.auth.callback.NameCallback;
33import javax.security.auth.callback.PasswordCallback;
34import javax.security.auth.callback.TextOutputCallback;
35import javax.security.auth.callback.UnsupportedCallbackException;
36
37/* Java imports */
38import java.awt.Component;
39import java.util.ArrayList;
40import java.util.Iterator;
41import java.util.List;
42import javax.swing.Box;
43import javax.swing.JLabel;
44import javax.swing.JOptionPane;
45import javax.swing.JPasswordField;
46import javax.swing.JTextField;
47
48/**
49 * <p>
50 * Uses a Swing dialog window to query the user for answers to
51 * authentication questions.
52 * This can be used by a JAAS application to instantiate a
53 * CallbackHandler
54 * @see javax.security.auth.callback
55 */
56public class DialogCallbackHandler implements CallbackHandler {
57
58 /* -- Fields -- */
59
60 /* The parent window, or null if using the default parent */
61 private Component parentComponent;
62 private static final int JPasswordFieldLen = 8 ;
63 private static final int JTextFieldLen = 8 ;
64
65 /* -- Methods -- */
66
67 /**
68 * Creates a callback dialog with the default parent window.
69 */
70 public DialogCallbackHandler() { }
71
72 /**
73 * Creates a callback dialog and specify the parent window.
74 *
75 * @param parentComponent the parent window -- specify <code>null</code>
76 * for the default parent
77 */
78 public DialogCallbackHandler(Component parentComponent) {
79 this.parentComponent = parentComponent;
80 }
81
82 /*
83 * An interface for recording actions to carry out if the user
84 * clicks OK for the dialog.
85 */
86 private static interface Action {
87 void perform();
88 }
89
90 /**
91 * Handles the specified set of callbacks.
92 *
93 * @param callbacks the callbacks to handle
94 * @throws UnsupportedCallbackException if the callback is not an
95 * instance of NameCallback or PasswordCallback
96 */
97
98 public void handle(Callback[] callbacks)
99 throws UnsupportedCallbackException
100 {
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 =
163 new JPasswordField(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(
189 callbacks[i], "Unrecognized Callback");
190 }
191 }
192
193 /* Display the dialog */
194 int result = JOptionPane.showOptionDialog(
195 parentComponent,
196 messages.toArray(),
197 "Confirmation", /* title */
198 confirmation.optionType,
199 confirmation.messageType,
200 null, /* icon */
201 confirmation.options, /* options */
202 confirmation.initialValue); /* initialValue */
203
204 /* Perform the OK actions */
205 if (result == JOptionPane.OK_OPTION
206 || result == JOptionPane.YES_OPTION)
207 {
208 Iterator<Action> iterator = okActions.iterator();
209 while (iterator.hasNext()) {
210 iterator.next().perform();
211 }
212 }
213 confirmation.handleResult(result);
214 }
215
216 /*
217 * Provides assistance with translating between JAAS and Swing
218 * confirmation dialogs.
219 */
220 private static class ConfirmationInfo {
221
222 private int[] translations;
223
224 int optionType = JOptionPane.OK_CANCEL_OPTION;
225 Object[] options = null;
226 Object initialValue = null;
227
228 int messageType = JOptionPane.QUESTION_MESSAGE;
229
230 private ConfirmationCallback callback;
231
232 /* Set the confirmation callback handler */
233 void setCallback(ConfirmationCallback callback)
234 throws UnsupportedCallbackException
235 {
236 this.callback = callback;
237
238 int confirmationOptionType = callback.getOptionType();
239 switch (confirmationOptionType) {
240 case ConfirmationCallback.YES_NO_OPTION:
241 optionType = JOptionPane.YES_NO_OPTION;
242 translations = new int[] {
243 JOptionPane.YES_OPTION, ConfirmationCallback.YES,
244 JOptionPane.NO_OPTION, ConfirmationCallback.NO,
245 JOptionPane.CLOSED_OPTION, ConfirmationCallback.NO
246 };
247 break;
248 case ConfirmationCallback.YES_NO_CANCEL_OPTION:
249 optionType = JOptionPane.YES_NO_CANCEL_OPTION;
250 translations = new int[] {
251 JOptionPane.YES_OPTION, ConfirmationCallback.YES,
252 JOptionPane.NO_OPTION, ConfirmationCallback.NO,
253 JOptionPane.CANCEL_OPTION, ConfirmationCallback.CANCEL,
254 JOptionPane.CLOSED_OPTION, ConfirmationCallback.CANCEL
255 };
256 break;
257 case ConfirmationCallback.OK_CANCEL_OPTION:
258 optionType = JOptionPane.OK_CANCEL_OPTION;
259 translations = new int[] {
260 JOptionPane.OK_OPTION, ConfirmationCallback.OK,
261 JOptionPane.CANCEL_OPTION, ConfirmationCallback.CANCEL,
262 JOptionPane.CLOSED_OPTION, ConfirmationCallback.CANCEL
263 };
264 break;
265 case ConfirmationCallback.UNSPECIFIED_OPTION:
266 options = callback.getOptions();
267 /*
268 * There's no way to know if the default option means
269 * to cancel the login, but there isn't a better way
270 * to guess this.
271 */
272 translations = new int[] {
273 JOptionPane.CLOSED_OPTION, callback.getDefaultOption()
274 };
275 break;
276 default:
277 throw new UnsupportedCallbackException(
278 callback,
279 "Unrecognized option type: " + confirmationOptionType);
280 }
281
282 int confirmationMessageType = callback.getMessageType();
283 switch (confirmationMessageType) {
284 case ConfirmationCallback.WARNING:
285 messageType = JOptionPane.WARNING_MESSAGE;
286 break;
287 case ConfirmationCallback.ERROR:
288 messageType = JOptionPane.ERROR_MESSAGE;
289 break;
290 case ConfirmationCallback.INFORMATION:
291 messageType = JOptionPane.INFORMATION_MESSAGE;
292 break;
293 default:
294 throw new UnsupportedCallbackException(
295 callback,
296 "Unrecognized message type: " + confirmationMessageType);
297 }
298 }
299
300
301 /* Process the result returned by the Swing dialog */
302 void handleResult(int result) {
303 if (callback == null) {
304 return;
305 }
306
307 for (int i = 0; i < translations.length; i += 2) {
308 if (translations[i] == result) {
309 result = translations[i + 1];
310 break;
311 }
312 }
313 callback.setSelectedIndex(result);
314 }
315 }
316}