1 /*
2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.security.auth.callback;
27 
28 /* JAAS imports */
29 import javax.security.auth.callback.Callback;
30 import javax.security.auth.callback.CallbackHandler;
31 import javax.security.auth.callback.ConfirmationCallback;
32 import javax.security.auth.callback.NameCallback;
33 import javax.security.auth.callback.PasswordCallback;
34 import javax.security.auth.callback.TextOutputCallback;
35 import javax.security.auth.callback.UnsupportedCallbackException;
36 
37 /* Java imports */
38 import java.awt.Component;
39 import java.util.ArrayList;
40 import java.util.Iterator;
41 import java.util.List;
42 import javax.swing.Box;
43 import javax.swing.JLabel;
44 import javax.swing.JOptionPane;
45 import javax.swing.JPasswordField;
46 import 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  * @deprecated This class will be removed in a future release.
56  */
57 @jdk.Exported(false)
58 @Deprecated
59 public class DialogCallbackHandler implements CallbackHandler {
60 
61     /* -- Fields -- */
62 
63     /* The parent window, or null if using the default parent */
64     private Component parentComponent;
65     private static final int JPasswordFieldLen = 8 ;
66     private static final int JTextFieldLen = 8 ;
67 
68     /* -- Methods -- */
69 
70     /**
71      * Creates a callback dialog with the default parent window.
72      */
DialogCallbackHandler()73     public DialogCallbackHandler() { }
74 
75     /**
76      * Creates a callback dialog and specify the parent window.
77      *
78      * @param parentComponent the parent window -- specify <code>null</code>
79      * for the default parent
80      */
DialogCallbackHandler(Component parentComponent)81     public DialogCallbackHandler(Component parentComponent) {
82         this.parentComponent = parentComponent;
83     }
84 
85     /*
86      * An interface for recording actions to carry out if the user
87      * clicks OK for the dialog.
88      */
89     private static interface Action {
perform()90          void perform();
91     }
92 
93     /**
94      * Handles the specified set of callbacks.
95      *
96      * @param callbacks the callbacks to handle
97      * @throws UnsupportedCallbackException if the callback is not an
98      * instance  of NameCallback or PasswordCallback
99      */
100 
handle(Callback[] callbacks)101     public void handle(Callback[] callbacks)
102         throws UnsupportedCallbackException
103     {
104         /* Collect messages to display in the dialog */
105         final List<Object> messages = new ArrayList<>(3);
106 
107         /* Collection actions to perform if the user clicks OK */
108         final List<Action> okActions = new ArrayList<>(2);
109 
110         ConfirmationInfo confirmation = new ConfirmationInfo();
111 
112         for (int i = 0; i < callbacks.length; i++) {
113             if (callbacks[i] instanceof TextOutputCallback) {
114                 TextOutputCallback tc = (TextOutputCallback) callbacks[i];
115 
116                 switch (tc.getMessageType()) {
117                 case TextOutputCallback.INFORMATION:
118                     confirmation.messageType = JOptionPane.INFORMATION_MESSAGE;
119                     break;
120                 case TextOutputCallback.WARNING:
121                     confirmation.messageType = JOptionPane.WARNING_MESSAGE;
122                     break;
123                 case TextOutputCallback.ERROR:
124                     confirmation.messageType = JOptionPane.ERROR_MESSAGE;
125                     break;
126                 default:
127                     throw new UnsupportedCallbackException(
128                         callbacks[i], "Unrecognized message type");
129                 }
130 
131                 messages.add(tc.getMessage());
132 
133             } else if (callbacks[i] instanceof NameCallback) {
134                 final NameCallback nc = (NameCallback) callbacks[i];
135 
136                 JLabel prompt = new JLabel(nc.getPrompt());
137 
138                 final JTextField name = new JTextField(JTextFieldLen);
139                 String defaultName = nc.getDefaultName();
140                 if (defaultName != null) {
141                     name.setText(defaultName);
142                 }
143 
144                 /*
145                  * Put the prompt and name in a horizontal box,
146                  * and add that to the set of messages.
147                  */
148                 Box namePanel = Box.createHorizontalBox();
149                 namePanel.add(prompt);
150                 namePanel.add(name);
151                 messages.add(namePanel);
152 
153                 /* Store the name back into the callback if OK */
154                 okActions.add(new Action() {
155                     public void perform() {
156                         nc.setName(name.getText());
157                     }
158                 });
159 
160             } else if (callbacks[i] instanceof PasswordCallback) {
161                 final PasswordCallback pc = (PasswordCallback) callbacks[i];
162 
163                 JLabel prompt = new JLabel(pc.getPrompt());
164 
165                 final JPasswordField password =
166                                         new JPasswordField(JPasswordFieldLen);
167                 if (!pc.isEchoOn()) {
168                     password.setEchoChar('*');
169                 }
170 
171                 Box passwordPanel = Box.createHorizontalBox();
172                 passwordPanel.add(prompt);
173                 passwordPanel.add(password);
174                 messages.add(passwordPanel);
175 
176                 okActions.add(new Action() {
177                     public void perform() {
178                         pc.setPassword(password.getPassword());
179                     }
180                 });
181 
182             } else if (callbacks[i] instanceof ConfirmationCallback) {
183                 ConfirmationCallback cc = (ConfirmationCallback)callbacks[i];
184 
185                 confirmation.setCallback(cc);
186                 if (cc.getPrompt() != null) {
187                     messages.add(cc.getPrompt());
188                 }
189 
190             } else {
191                 throw new UnsupportedCallbackException(
192                     callbacks[i], "Unrecognized Callback");
193             }
194         }
195 
196         /* Display the dialog */
197         int result = JOptionPane.showOptionDialog(
198             parentComponent,
199             messages.toArray(),
200             "Confirmation",                     /* title */
201             confirmation.optionType,
202             confirmation.messageType,
203             null,                               /* icon */
204             confirmation.options,               /* options */
205             confirmation.initialValue);         /* initialValue */
206 
207         /* Perform the OK actions */
208         if (result == JOptionPane.OK_OPTION
209             || result == JOptionPane.YES_OPTION)
210         {
211             Iterator<Action> iterator = okActions.iterator();
212             while (iterator.hasNext()) {
213                 iterator.next().perform();
214             }
215         }
216         confirmation.handleResult(result);
217     }
218 
219     /*
220      * Provides assistance with translating between JAAS and Swing
221      * confirmation dialogs.
222      */
223     private static class ConfirmationInfo {
224 
225         private int[] translations;
226 
227         int optionType = JOptionPane.OK_CANCEL_OPTION;
228         Object[] options = null;
229         Object initialValue = null;
230 
231         int messageType = JOptionPane.QUESTION_MESSAGE;
232 
233         private ConfirmationCallback callback;
234 
235         /* Set the confirmation callback handler */
setCallback(ConfirmationCallback callback)236         void setCallback(ConfirmationCallback callback)
237             throws UnsupportedCallbackException
238         {
239             this.callback = callback;
240 
241             int confirmationOptionType = callback.getOptionType();
242             switch (confirmationOptionType) {
243             case ConfirmationCallback.YES_NO_OPTION:
244                 optionType = JOptionPane.YES_NO_OPTION;
245                 translations = new int[] {
246                     JOptionPane.YES_OPTION, ConfirmationCallback.YES,
247                     JOptionPane.NO_OPTION, ConfirmationCallback.NO,
248                     JOptionPane.CLOSED_OPTION, ConfirmationCallback.NO
249                 };
250                 break;
251             case ConfirmationCallback.YES_NO_CANCEL_OPTION:
252                 optionType = JOptionPane.YES_NO_CANCEL_OPTION;
253                 translations = new int[] {
254                     JOptionPane.YES_OPTION, ConfirmationCallback.YES,
255                     JOptionPane.NO_OPTION, ConfirmationCallback.NO,
256                     JOptionPane.CANCEL_OPTION, ConfirmationCallback.CANCEL,
257                     JOptionPane.CLOSED_OPTION, ConfirmationCallback.CANCEL
258                 };
259                 break;
260             case ConfirmationCallback.OK_CANCEL_OPTION:
261                 optionType = JOptionPane.OK_CANCEL_OPTION;
262                 translations = new int[] {
263                     JOptionPane.OK_OPTION, ConfirmationCallback.OK,
264                     JOptionPane.CANCEL_OPTION, ConfirmationCallback.CANCEL,
265                     JOptionPane.CLOSED_OPTION, ConfirmationCallback.CANCEL
266                 };
267                 break;
268             case ConfirmationCallback.UNSPECIFIED_OPTION:
269                 options = callback.getOptions();
270                 /*
271                  * There's no way to know if the default option means
272                  * to cancel the login, but there isn't a better way
273                  * to guess this.
274                  */
275                 translations = new int[] {
276                     JOptionPane.CLOSED_OPTION, callback.getDefaultOption()
277                 };
278                 break;
279             default:
280                 throw new UnsupportedCallbackException(
281                     callback,
282                     "Unrecognized option type: " + confirmationOptionType);
283             }
284 
285             int confirmationMessageType = callback.getMessageType();
286             switch (confirmationMessageType) {
287             case ConfirmationCallback.WARNING:
288                 messageType = JOptionPane.WARNING_MESSAGE;
289                 break;
290             case ConfirmationCallback.ERROR:
291                 messageType = JOptionPane.ERROR_MESSAGE;
292                 break;
293             case ConfirmationCallback.INFORMATION:
294                 messageType = JOptionPane.INFORMATION_MESSAGE;
295                 break;
296             default:
297                 throw new UnsupportedCallbackException(
298                     callback,
299                     "Unrecognized message type: " + confirmationMessageType);
300             }
301         }
302 
303 
304         /* Process the result returned by the Swing dialog */
handleResult(int result)305         void handleResult(int result) {
306             if (callback == null) {
307                 return;
308             }
309 
310             for (int i = 0; i < translations.length; i += 2) {
311                 if (translations[i] == result) {
312                     result = translations[i + 1];
313                     break;
314                 }
315             }
316             callback.setSelectedIndex(result);
317         }
318     }
319 }
320