1 /* 2 * Copyright (c) 2000, 2006, 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 */ 56 public 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<>(3); 103 104 /* Collection actions to perform if the user clicks OK */ 105 final List<Action> okActions = new ArrayList<>(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 }