1. /*
  2. * @(#)DialogCallbackHandler.java 1.9 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.security.auth.callback;
  8. /* JAAS imports */
  9. import javax.security.auth.callback.Callback;
  10. import javax.security.auth.callback.CallbackHandler;
  11. import javax.security.auth.callback.ConfirmationCallback;
  12. import javax.security.auth.callback.NameCallback;
  13. import javax.security.auth.callback.PasswordCallback;
  14. import javax.security.auth.callback.TextOutputCallback;
  15. import javax.security.auth.callback.UnsupportedCallbackException;
  16. /* Java imports */
  17. import java.awt.Component;
  18. import java.util.ArrayList;
  19. import java.util.Iterator;
  20. import java.util.List;
  21. import javax.swing.Box;
  22. import javax.swing.JLabel;
  23. import javax.swing.JOptionPane;
  24. import javax.swing.JPasswordField;
  25. import javax.swing.JTextField;
  26. /**
  27. * <p>
  28. * Uses a Swing dialog window to query the user for answers to
  29. * authentication questions.
  30. * This can be used by a JAAS application to instantiate a
  31. * CallbackHandler
  32. * @see javax.security.auth.callback
  33. */
  34. public class DialogCallbackHandler implements CallbackHandler {
  35. /* -- Fields -- */
  36. /* The parent window, or null if using the default parent */
  37. private Component parentComponent;
  38. private static final int JPasswordFieldLen = 8 ;
  39. /* -- Methods -- */
  40. /**
  41. * Creates a callback dialog with the default parent window.
  42. */
  43. public DialogCallbackHandler() { }
  44. /**
  45. * Creates a callback dialog and specify the parent window.
  46. *
  47. * @param parentComponent the parent window -- specify <code>null</code>
  48. * for the default parent
  49. */
  50. public DialogCallbackHandler(Component parentComponent) {
  51. this.parentComponent = parentComponent;
  52. }
  53. /*
  54. * An interface for recording actions to carry out if the user
  55. * clicks OK for the dialog.
  56. */
  57. private static interface Action {
  58. void perform();
  59. }
  60. /**
  61. * Handles the specified set of callbacks.
  62. *
  63. * @param callbacks the callbacks to handle
  64. * @throws UnsupportedCallbackException if the callback is not an
  65. * instance of NameCallback or PasswordCallback
  66. */
  67. public void handle(Callback[] callbacks)
  68. throws UnsupportedCallbackException
  69. {
  70. /* Collect messages to display in the dialog */
  71. final List messages = new ArrayList(3);
  72. /* Collection actions to perform if the user clicks OK */
  73. final List okActions = new ArrayList(2);
  74. ConfirmationInfo confirmation = new ConfirmationInfo();
  75. for (int i = 0; i < callbacks.length; i++) {
  76. if (callbacks[i] instanceof TextOutputCallback) {
  77. TextOutputCallback tc = (TextOutputCallback) callbacks[i];
  78. switch (tc.getMessageType()) {
  79. case TextOutputCallback.INFORMATION:
  80. confirmation.messageType = JOptionPane.INFORMATION_MESSAGE;
  81. break;
  82. case TextOutputCallback.WARNING:
  83. confirmation.messageType = JOptionPane.WARNING_MESSAGE;
  84. break;
  85. case TextOutputCallback.ERROR:
  86. confirmation.messageType = JOptionPane.ERROR_MESSAGE;
  87. break;
  88. default:
  89. throw new UnsupportedCallbackException(
  90. callbacks[i], "Unrecognized message type");
  91. }
  92. messages.add(tc.getMessage());
  93. } else if (callbacks[i] instanceof NameCallback) {
  94. final NameCallback nc = (NameCallback) callbacks[i];
  95. JLabel prompt = new JLabel(nc.getPrompt());
  96. final JTextField name = new JTextField();
  97. String defaultName = nc.getDefaultName();
  98. if (defaultName != null) {
  99. name.setText(defaultName);
  100. }
  101. /*
  102. * Put the prompt and name in a horizontal box,
  103. * and add that to the set of messages.
  104. */
  105. Box namePanel = Box.createHorizontalBox();
  106. namePanel.add(prompt);
  107. namePanel.add(name);
  108. messages.add(namePanel);
  109. /* Store the name back into the callback if OK */
  110. okActions.add(new Action() {
  111. public void perform() {
  112. nc.setName(name.getText());
  113. }
  114. });
  115. } else if (callbacks[i] instanceof PasswordCallback) {
  116. final PasswordCallback pc = (PasswordCallback) callbacks[i];
  117. JLabel prompt = new JLabel(pc.getPrompt());
  118. final JPasswordField password =
  119. new JPasswordField(JPasswordFieldLen);
  120. if (!pc.isEchoOn()) {
  121. password.setEchoChar('*');
  122. }
  123. Box passwordPanel = Box.createHorizontalBox();
  124. passwordPanel.add(prompt);
  125. passwordPanel.add(password);
  126. messages.add(passwordPanel);
  127. okActions.add(new Action() {
  128. public void perform() {
  129. pc.setPassword(password.getPassword());
  130. }
  131. });
  132. } else if (callbacks[i] instanceof ConfirmationCallback) {
  133. ConfirmationCallback cc = (ConfirmationCallback)callbacks[i];
  134. confirmation.setCallback(cc);
  135. if (cc.getPrompt() != null) {
  136. messages.add(cc.getPrompt());
  137. }
  138. } else {
  139. throw new UnsupportedCallbackException(
  140. callbacks[i], "Unrecognized Callback");
  141. }
  142. }
  143. /* Display the dialog */
  144. int result = JOptionPane.showOptionDialog(
  145. parentComponent,
  146. messages.toArray(),
  147. "Confirmation", /* title */
  148. confirmation.optionType,
  149. confirmation.messageType,
  150. null, /* icon */
  151. confirmation.options, /* options */
  152. confirmation.initialValue); /* initialValue */
  153. /* Perform the OK actions */
  154. if (result == JOptionPane.OK_OPTION
  155. || result == JOptionPane.YES_OPTION)
  156. {
  157. Iterator iterator = okActions.iterator();
  158. while (iterator.hasNext()) {
  159. ((Action) iterator.next()).perform();
  160. }
  161. }
  162. confirmation.handleResult(result);
  163. }
  164. /*
  165. * Provides assistance with translating between JAAS and Swing
  166. * confirmation dialogs.
  167. */
  168. private static class ConfirmationInfo {
  169. private int[] translations;
  170. int optionType = JOptionPane.OK_CANCEL_OPTION;
  171. Object[] options = null;
  172. Object initialValue = null;
  173. int messageType = JOptionPane.QUESTION_MESSAGE;
  174. private ConfirmationCallback callback;
  175. /* Set the confirmation callback handler */
  176. void setCallback(ConfirmationCallback callback)
  177. throws UnsupportedCallbackException
  178. {
  179. this.callback = callback;
  180. int confirmationOptionType = callback.getOptionType();
  181. switch (confirmationOptionType) {
  182. case ConfirmationCallback.YES_NO_OPTION:
  183. optionType = JOptionPane.YES_NO_OPTION;
  184. translations = new int[] {
  185. JOptionPane.YES_OPTION, ConfirmationCallback.YES,
  186. JOptionPane.NO_OPTION, ConfirmationCallback.NO,
  187. JOptionPane.CLOSED_OPTION, ConfirmationCallback.NO
  188. };
  189. break;
  190. case ConfirmationCallback.YES_NO_CANCEL_OPTION:
  191. optionType = JOptionPane.YES_NO_CANCEL_OPTION;
  192. translations = new int[] {
  193. JOptionPane.YES_OPTION, ConfirmationCallback.YES,
  194. JOptionPane.NO_OPTION, ConfirmationCallback.NO,
  195. JOptionPane.CANCEL_OPTION, ConfirmationCallback.CANCEL,
  196. JOptionPane.CLOSED_OPTION, ConfirmationCallback.CANCEL
  197. };
  198. break;
  199. case ConfirmationCallback.OK_CANCEL_OPTION:
  200. optionType = JOptionPane.OK_CANCEL_OPTION;
  201. translations = new int[] {
  202. JOptionPane.OK_OPTION, ConfirmationCallback.OK,
  203. JOptionPane.CANCEL_OPTION, ConfirmationCallback.CANCEL,
  204. JOptionPane.CLOSED_OPTION, ConfirmationCallback.CANCEL
  205. };
  206. break;
  207. case ConfirmationCallback.UNSPECIFIED_OPTION:
  208. options = callback.getOptions();
  209. /*
  210. * There's no way to know if the default option means
  211. * to cancel the login, but there isn't a better way
  212. * to guess this.
  213. */
  214. translations = new int[] {
  215. JOptionPane.CLOSED_OPTION, callback.getDefaultOption()
  216. };
  217. break;
  218. default:
  219. throw new UnsupportedCallbackException(
  220. callback,
  221. "Unrecognized option type: " + confirmationOptionType);
  222. }
  223. int confirmationMessageType = callback.getMessageType();
  224. switch (confirmationMessageType) {
  225. case ConfirmationCallback.WARNING:
  226. messageType = JOptionPane.WARNING_MESSAGE;
  227. break;
  228. case ConfirmationCallback.ERROR:
  229. messageType = JOptionPane.ERROR_MESSAGE;
  230. break;
  231. case ConfirmationCallback.INFORMATION:
  232. messageType = JOptionPane.INFORMATION_MESSAGE;
  233. break;
  234. default:
  235. throw new UnsupportedCallbackException(
  236. callback,
  237. "Unrecognized message type: " + confirmationMessageType);
  238. }
  239. }
  240. /* Process the result returned by the Swing dialog */
  241. void handleResult(int result) {
  242. if (callback == null) {
  243. return;
  244. }
  245. for (int i = 0; i < translations.length; i += 2) {
  246. if (translations[i] == result) {
  247. result = translations[i + 1];
  248. break;
  249. }
  250. }
  251. callback.setSelectedIndex(result);
  252. }
  253. }
  254. }