1 /* 2 * Copyright (c) 2014, 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 sun.security.util; 27 28 import javax.security.auth.callback.Callback; 29 import javax.security.auth.callback.CallbackHandler; 30 import javax.security.auth.callback.ConfirmationCallback; 31 import javax.security.auth.callback.NameCallback; 32 import javax.security.auth.callback.PasswordCallback; 33 import javax.security.auth.callback.TextOutputCallback; 34 import javax.security.auth.callback.UnsupportedCallbackException; 35 36 import java.io.BufferedReader; 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.io.InputStreamReader; 40 41 /** 42 * A {@code CallbackHandler} that prompts and reads from the command line 43 * for answers to authentication questions. 44 */ 45 public class ConsoleCallbackHandler implements CallbackHandler { 46 47 /** 48 * Creates a callback handler that prompts and reads from the 49 * command line for answers to authentication questions. 50 */ 51 public ConsoleCallbackHandler() { } 52 53 /** 54 * Handles the specified set of callbacks. 55 * 56 * @param callbacks the callbacks to handle 57 * @throws IOException if an input or output error occurs. 58 * @throws UnsupportedCallbackException if the callback is not an 59 * instance of NameCallback or PasswordCallback 60 */ 61 public void handle(Callback[] callbacks) 62 throws IOException, UnsupportedCallbackException 63 { 64 ConfirmationCallback confirmation = null; 65 66 for (int i = 0; i < callbacks.length; i++) { 67 if (callbacks[i] instanceof TextOutputCallback) { 68 TextOutputCallback tc = (TextOutputCallback) callbacks[i]; 69 70 String text; 71 switch (tc.getMessageType()) { 72 case TextOutputCallback.INFORMATION: 73 text = ""; 74 break; 75 case TextOutputCallback.WARNING: 76 text = "Warning: "; 77 break; 78 case TextOutputCallback.ERROR: 79 text = "Error: "; 80 break; 81 default: 82 throw new UnsupportedCallbackException( 83 callbacks[i], "Unrecognized message type"); 84 } 85 86 String message = tc.getMessage(); 87 if (message != null) { 88 text += message; 89 } 90 if (text != null) { 91 System.err.println(text); 92 } 93 94 } else if (callbacks[i] instanceof NameCallback) { 95 NameCallback nc = (NameCallback) callbacks[i]; 96 97 if (nc.getDefaultName() == null) { 98 System.err.print(nc.getPrompt()); 99 } else { 100 System.err.print(nc.getPrompt() + 101 " [" + nc.getDefaultName() + "] "); 102 } 103 System.err.flush(); 104 105 String result = readLine(); 106 if (result.equals("")) { 107 result = nc.getDefaultName(); 108 } 109 110 nc.setName(result); 111 112 } else if (callbacks[i] instanceof PasswordCallback) { 113 PasswordCallback pc = (PasswordCallback) callbacks[i]; 114 115 System.err.print(pc.getPrompt()); 116 System.err.flush(); 117 118 pc.setPassword(Password.readPassword(System.in, pc.isEchoOn())); 119 120 } else if (callbacks[i] instanceof ConfirmationCallback) { 121 confirmation = (ConfirmationCallback) callbacks[i]; 122 123 } else { 124 throw new UnsupportedCallbackException( 125 callbacks[i], "Unrecognized Callback"); 126 } 127 } 128 129 /* Do the confirmation callback last. */ 130 if (confirmation != null) { 131 doConfirmation(confirmation); 132 } 133 } 134 135 /* Reads a line of input */ 136 private String readLine() throws IOException { 137 String result = new BufferedReader 138 (new InputStreamReader(System.in)).readLine(); 139 if (result == null) { 140 throw new IOException("Cannot read from System.in"); 141 } 142 return result; 143 } 144 145 private void doConfirmation(ConfirmationCallback confirmation) 146 throws IOException, UnsupportedCallbackException 147 { 148 String prefix; 149 int messageType = confirmation.getMessageType(); 150 switch (messageType) { 151 case ConfirmationCallback.WARNING: 152 prefix = "Warning: "; 153 break; 154 case ConfirmationCallback.ERROR: 155 prefix = "Error: "; 156 break; 157 case ConfirmationCallback.INFORMATION: 158 prefix = ""; 159 break; 160 default: 161 throw new UnsupportedCallbackException( 162 confirmation, "Unrecognized message type: " + messageType); 163 } 164 165 class OptionInfo { 166 String name; 167 int value; 168 OptionInfo(String name, int value) { 169 this.name = name; 170 this.value = value; 171 } 172 } 173 174 OptionInfo[] options; 175 int optionType = confirmation.getOptionType(); 176 switch (optionType) { 177 case ConfirmationCallback.YES_NO_OPTION: 178 options = new OptionInfo[] { 179 new OptionInfo("Yes", ConfirmationCallback.YES), 180 new OptionInfo("No", ConfirmationCallback.NO) 181 }; 182 break; 183 case ConfirmationCallback.YES_NO_CANCEL_OPTION: 184 options = new OptionInfo[] { 185 new OptionInfo("Yes", ConfirmationCallback.YES), 186 new OptionInfo("No", ConfirmationCallback.NO), 187 new OptionInfo("Cancel", ConfirmationCallback.CANCEL) 188 }; 189 break; 190 case ConfirmationCallback.OK_CANCEL_OPTION: 191 options = new OptionInfo[] { 192 new OptionInfo("OK", ConfirmationCallback.OK), 193 new OptionInfo("Cancel", ConfirmationCallback.CANCEL) 194 }; 195 break; 196 case ConfirmationCallback.UNSPECIFIED_OPTION: 197 String[] optionStrings = confirmation.getOptions(); 198 options = new OptionInfo[optionStrings.length]; 199 for (int i = 0; i < options.length; i++) { 200 options[i] = new OptionInfo(optionStrings[i], i); 201 } 202 break; 203 default: 204 throw new UnsupportedCallbackException( 205 confirmation, "Unrecognized option type: " + optionType); 206 } 207 208 int defaultOption = confirmation.getDefaultOption(); 209 210 String prompt = confirmation.getPrompt(); 211 if (prompt == null) { 212 prompt = ""; 213 } 214 prompt = prefix + prompt; 215 if (!prompt.equals("")) { 216 System.err.println(prompt); 217 } 218 219 for (int i = 0; i < options.length; i++) { 220 if (optionType == ConfirmationCallback.UNSPECIFIED_OPTION) { 221 // defaultOption is an index into the options array 222 System.err.println( 223 i + ". " + options[i].name + 224 (i == defaultOption ? " [default]" : "")); 225 } else { 226 // defaultOption is an option value 227 System.err.println( 228 i + ". " + options[i].name + 229 (options[i].value == defaultOption ? " [default]" : "")); 230 } 231 } 232 System.err.print("Enter a number: "); 233 System.err.flush(); 234 int result; 235 try { 236 result = Integer.parseInt(readLine()); 237 if (result < 0 || result > (options.length - 1)) { 238 result = defaultOption; 239 } 240 result = options[result].value; 241 } catch (NumberFormatException e) { 242 result = defaultOption; 243 } 244 245 confirmation.setSelectedIndex(result); 246 } 247 }