source: josm/trunk/src/org/openstreetmap/josm/gui/io/CredentialDialog.java@ 12821

Last change on this file since 12821 was 12821, checked in by Don-vip, 7 years ago

see #15229 - see #15182 - remove GUI references from I/O subsystem

  • Property svn:eol-style set to native
File size: 17.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Dimension;
8import java.awt.FlowLayout;
9import java.awt.Font;
10import java.awt.GridBagConstraints;
11import java.awt.GridBagLayout;
12import java.awt.Insets;
13import java.awt.event.ActionEvent;
14import java.awt.event.FocusAdapter;
15import java.awt.event.FocusEvent;
16import java.awt.event.KeyAdapter;
17import java.awt.event.KeyEvent;
18import java.awt.event.WindowAdapter;
19import java.awt.event.WindowEvent;
20import java.net.Authenticator.RequestorType;
21import java.util.Objects;
22
23import javax.swing.AbstractAction;
24import javax.swing.BorderFactory;
25import javax.swing.JCheckBox;
26import javax.swing.JDialog;
27import javax.swing.JLabel;
28import javax.swing.JPanel;
29import javax.swing.JTextField;
30
31import org.openstreetmap.josm.Main;
32import org.openstreetmap.josm.gui.SideButton;
33import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
34import org.openstreetmap.josm.gui.help.HelpUtil;
35import org.openstreetmap.josm.gui.util.GuiHelper;
36import org.openstreetmap.josm.gui.util.WindowGeometry;
37import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
38import org.openstreetmap.josm.gui.widgets.JosmPasswordField;
39import org.openstreetmap.josm.gui.widgets.JosmTextField;
40import org.openstreetmap.josm.io.DefaultProxySelector;
41import org.openstreetmap.josm.io.OsmApi;
42import org.openstreetmap.josm.io.auth.AbstractCredentialsAgent;
43import org.openstreetmap.josm.io.auth.CredentialsAgentResponse;
44import org.openstreetmap.josm.tools.ImageProvider;
45import org.openstreetmap.josm.tools.InputMapUtils;
46import org.openstreetmap.josm.tools.Logging;
47
48/**
49 * Dialog box to request username and password from the user.
50 *
51 * The credentials can be for the OSM API (basic authentication), a different
52 * host or an HTTP proxy.
53 */
54public class CredentialDialog extends JDialog {
55
56 public static CredentialDialog getOsmApiCredentialDialog(String username, String password, String host,
57 String saveUsernameAndPasswordCheckboxText) {
58 CredentialDialog dialog = new CredentialDialog(saveUsernameAndPasswordCheckboxText);
59 if (Objects.equals(OsmApi.getOsmApi().getHost(), host)) {
60 dialog.prepareForOsmApiCredentials(username, password);
61 } else {
62 dialog.prepareForOtherHostCredentials(username, password, host);
63 }
64 dialog.pack();
65 return dialog;
66 }
67
68 public static CredentialDialog getHttpProxyCredentialDialog(String username, String password, String host,
69 String saveUsernameAndPasswordCheckboxText) {
70 CredentialDialog dialog = new CredentialDialog(saveUsernameAndPasswordCheckboxText);
71 dialog.prepareForProxyCredentials(username, password);
72 dialog.pack();
73 return dialog;
74 }
75
76 /**
77 * Prompts the user (in the EDT) for credentials and fills the given response with what has been entered.
78 * @param requestorType type of the entity requesting authentication
79 * @param agent the credentials agent requesting credentials
80 * @param response authentication response to fill
81 * @param username the known username, if any. Likely to be empty
82 * @param password the known password, if any. Likely to be empty
83 * @param host the host against authentication will be performed
84 * @since 12821
85 */
86 public static void promptCredentials(RequestorType requestorType, AbstractCredentialsAgent agent, CredentialsAgentResponse response,
87 String username, String password, String host) {
88 GuiHelper.runInEDTAndWait(() -> {
89 CredentialDialog dialog;
90 if (requestorType.equals(RequestorType.PROXY))
91 dialog = getHttpProxyCredentialDialog(
92 username, password, host, agent.getSaveUsernameAndPasswordCheckboxText());
93 else
94 dialog = getOsmApiCredentialDialog(
95 username, password, host, agent.getSaveUsernameAndPasswordCheckboxText());
96 dialog.setVisible(true);
97 response.setCanceled(dialog.isCanceled());
98 if (dialog.isCanceled())
99 return;
100 response.setUsername(dialog.getUsername());
101 response.setPassword(dialog.getPassword());
102 response.setSaveCredentials(dialog.isSaveCredentials());
103 });
104 }
105
106 private boolean canceled;
107 protected CredentialPanel pnlCredentials;
108 private final String saveUsernameAndPasswordCheckboxText;
109
110 public boolean isCanceled() {
111 return canceled;
112 }
113
114 protected void setCanceled(boolean canceled) {
115 this.canceled = canceled;
116 }
117
118 @Override
119 public void setVisible(boolean visible) {
120 if (visible) {
121 WindowGeometry.centerInWindow(Main.parent, new Dimension(350, 300)).applySafe(this);
122 }
123 super.setVisible(visible);
124 }
125
126 protected JPanel createButtonPanel() {
127 JPanel pnl = new JPanel(new FlowLayout());
128 pnl.add(new SideButton(new OKAction()));
129 pnl.add(new SideButton(new CancelAction()));
130 pnl.add(new SideButton(new ContextSensitiveHelpAction(HelpUtil.ht("/Dialog/Password"))));
131 return pnl;
132 }
133
134 protected void build() {
135 getContentPane().setLayout(new BorderLayout());
136 getContentPane().add(createButtonPanel(), BorderLayout.SOUTH);
137
138 addWindowListener(new WindowEventHander());
139 InputMapUtils.addEscapeAction(getRootPane(), new CancelAction());
140
141 getRootPane().setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
142 }
143
144 public CredentialDialog(String saveUsernameAndPasswordCheckboxText) {
145 this.saveUsernameAndPasswordCheckboxText = saveUsernameAndPasswordCheckboxText;
146 setModalityType(ModalityType.DOCUMENT_MODAL);
147 try {
148 setAlwaysOnTop(true);
149 } catch (SecurityException e) {
150 Logging.log(Logging.LEVEL_WARN, tr("Failed to put Credential Dialog always on top. Caught security exception."), e);
151 }
152 build();
153 }
154
155 public void prepareForOsmApiCredentials(String username, String password) {
156 setTitle(tr("Enter credentials for OSM API"));
157 pnlCredentials = new OsmApiCredentialsPanel(this);
158 getContentPane().add(pnlCredentials, BorderLayout.CENTER);
159 pnlCredentials.init(username, password);
160 validate();
161 }
162
163 public void prepareForOtherHostCredentials(String username, String password, String host) {
164 setTitle(tr("Enter credentials for host"));
165 pnlCredentials = new OtherHostCredentialsPanel(this, host);
166 getContentPane().add(pnlCredentials, BorderLayout.CENTER);
167 pnlCredentials.init(username, password);
168 validate();
169 }
170
171 public void prepareForProxyCredentials(String username, String password) {
172 setTitle(tr("Enter credentials for HTTP proxy"));
173 pnlCredentials = new HttpProxyCredentialsPanel(this);
174 getContentPane().add(pnlCredentials, BorderLayout.CENTER);
175 pnlCredentials.init(username, password);
176 validate();
177 }
178
179 public String getUsername() {
180 if (pnlCredentials == null)
181 return null;
182 return pnlCredentials.getUserName();
183 }
184
185 public char[] getPassword() {
186 if (pnlCredentials == null)
187 return null;
188 return pnlCredentials.getPassword();
189 }
190
191 public boolean isSaveCredentials() {
192 if (pnlCredentials == null)
193 return false;
194 return pnlCredentials.isSaveCredentials();
195 }
196
197 protected static class CredentialPanel extends JPanel {
198 protected JosmTextField tfUserName;
199 protected JosmPasswordField tfPassword;
200 protected JCheckBox cbSaveCredentials;
201 protected final JMultilineLabel lblHeading = new JMultilineLabel("");
202 protected final JMultilineLabel lblWarning = new JMultilineLabel("");
203 protected CredentialDialog owner; // owner Dependency Injection to use Key listeners for username and password text fields
204
205 protected void build() {
206 tfUserName = new JosmTextField(20);
207 tfPassword = new JosmPasswordField(20);
208 tfUserName.addFocusListener(new SelectAllOnFocusHandler());
209 tfPassword.addFocusListener(new SelectAllOnFocusHandler());
210 tfUserName.addKeyListener(new TFKeyListener(owner, tfUserName, tfPassword));
211 tfPassword.addKeyListener(new TFKeyListener(owner, tfPassword, tfUserName));
212 cbSaveCredentials = new JCheckBox(owner != null ? owner.saveUsernameAndPasswordCheckboxText : "");
213
214 setLayout(new GridBagLayout());
215 GridBagConstraints gc = new GridBagConstraints();
216 gc.gridwidth = 2;
217 gc.gridheight = 1;
218 gc.fill = GridBagConstraints.HORIZONTAL;
219 gc.weightx = 1.0;
220 gc.weighty = 0.0;
221 gc.insets = new Insets(0, 0, 10, 0);
222 add(lblHeading, gc);
223
224 gc.gridx = 0;
225 gc.gridy = 1;
226 gc.gridwidth = 1;
227 gc.gridheight = 1;
228 gc.fill = GridBagConstraints.HORIZONTAL;
229 gc.weightx = 0.0;
230 gc.weighty = 0.0;
231 gc.insets = new Insets(0, 0, 10, 10);
232 add(new JLabel(tr("Username")), gc);
233 gc.gridx = 1;
234 gc.gridy = 1;
235 gc.weightx = 1.0;
236 add(tfUserName, gc);
237 gc.gridx = 0;
238 gc.gridy = 2;
239 gc.weightx = 0.0;
240 add(new JLabel(tr("Password")), gc);
241
242 gc.gridx = 1;
243 gc.gridy = 2;
244 gc.weightx = 0.0;
245 add(tfPassword, gc);
246
247 gc.gridx = 0;
248 gc.gridy = 3;
249 gc.gridwidth = 2;
250 gc.gridheight = 1;
251 gc.fill = GridBagConstraints.BOTH;
252 gc.weightx = 1.0;
253 gc.weighty = 0.0;
254 lblWarning.setFont(lblWarning.getFont().deriveFont(Font.ITALIC));
255 add(lblWarning, gc);
256
257 gc.gridx = 0;
258 gc.gridy = 4;
259 gc.weighty = 0.0;
260 add(cbSaveCredentials, gc);
261
262 // consume the remaining space
263 gc.gridx = 0;
264 gc.gridy = 5;
265 gc.weighty = 1.0;
266 add(new JPanel(), gc);
267
268 }
269
270 public CredentialPanel(CredentialDialog owner) {
271 this.owner = owner;
272 }
273
274 public void init(String username, String password) {
275 username = username == null ? "" : username;
276 password = password == null ? "" : password;
277 tfUserName.setText(username);
278 tfPassword.setText(password);
279 cbSaveCredentials.setSelected(!username.isEmpty() && !password.isEmpty());
280 }
281
282 public void startUserInput() {
283 tfUserName.requestFocusInWindow();
284 }
285
286 public String getUserName() {
287 return tfUserName.getText();
288 }
289
290 public char[] getPassword() {
291 return tfPassword.getPassword();
292 }
293
294 public boolean isSaveCredentials() {
295 return cbSaveCredentials.isSelected();
296 }
297
298 protected final void updateWarningLabel(String url) {
299 boolean https = url != null && url.startsWith("https");
300 if (https) {
301 lblWarning.setText(null);
302 } else {
303 lblWarning.setText(tr("Warning: The password is transferred unencrypted."));
304 }
305 lblWarning.setVisible(!https);
306 }
307 }
308
309 private static class OsmApiCredentialsPanel extends CredentialPanel {
310
311 @Override
312 protected void build() {
313 super.build();
314 tfUserName.setToolTipText(tr("Please enter the user name of your OSM account"));
315 tfPassword.setToolTipText(tr("Please enter the password of your OSM account"));
316 String apiUrl = OsmApi.getOsmApi().getBaseUrl();
317 lblHeading.setText(
318 "<html>" + tr("Authenticating at the OSM API ''{0}'' failed. Please enter a valid username and a valid password.",
319 apiUrl) + "</html>");
320 updateWarningLabel(apiUrl);
321 }
322
323 OsmApiCredentialsPanel(CredentialDialog owner) {
324 super(owner);
325 build();
326 }
327 }
328
329 private static class OtherHostCredentialsPanel extends CredentialPanel {
330
331 private final String host;
332
333 @Override
334 protected void build() {
335 super.build();
336 tfUserName.setToolTipText(tr("Please enter the user name of your account"));
337 tfPassword.setToolTipText(tr("Please enter the password of your account"));
338 lblHeading.setText(
339 "<html>" + tr("Authenticating at the host ''{0}'' failed. Please enter a valid username and a valid password.",
340 host) + "</html>");
341 updateWarningLabel(host);
342 }
343
344 OtherHostCredentialsPanel(CredentialDialog owner, String host) {
345 super(owner);
346 this.host = host;
347 build();
348 }
349 }
350
351 private static class HttpProxyCredentialsPanel extends CredentialPanel {
352 @Override
353 protected void build() {
354 super.build();
355 tfUserName.setToolTipText(tr("Please enter the user name for authenticating at your proxy server"));
356 tfPassword.setToolTipText(tr("Please enter the password for authenticating at your proxy server"));
357 lblHeading.setText(
358 "<html>" + tr("Authenticating at the HTTP proxy ''{0}'' failed. Please enter a valid username and a valid password.",
359 Main.pref.get(DefaultProxySelector.PROXY_HTTP_HOST) + ':' +
360 Main.pref.get(DefaultProxySelector.PROXY_HTTP_PORT)) + "</html>");
361 lblWarning.setText("<html>" +
362 tr("Warning: depending on the authentication method the proxy server uses the password may be transferred unencrypted.")
363 + "</html>");
364 }
365
366 HttpProxyCredentialsPanel(CredentialDialog owner) {
367 super(owner);
368 build();
369 }
370 }
371
372 private static class SelectAllOnFocusHandler extends FocusAdapter {
373 @Override
374 public void focusGained(FocusEvent e) {
375 if (e.getSource() instanceof JTextField) {
376 JTextField tf = (JTextField) e.getSource();
377 tf.selectAll();
378 }
379 }
380 }
381
382 /**
383 * Listener for username and password text fields key events.
384 * When user presses Enter:
385 * If current text field is empty (or just contains a sequence of spaces), nothing happens (or all spaces become selected).
386 * If current text field is not empty, but the next one is (or just contains a sequence of spaces), focuses the next text field.
387 * If both text fields contain characters, submits the form by calling owner's {@link OKAction}.
388 */
389 private static class TFKeyListener extends KeyAdapter {
390 protected CredentialDialog owner; // owner Dependency Injection to call OKAction
391 protected JTextField currentTF;
392 protected JTextField nextTF;
393
394 TFKeyListener(CredentialDialog owner, JTextField currentTF, JTextField nextTF) {
395 this.owner = owner;
396 this.currentTF = currentTF;
397 this.nextTF = nextTF;
398 }
399
400 @Override
401 public void keyPressed(KeyEvent e) {
402 if (e.getKeyChar() == KeyEvent.VK_ENTER) {
403 if (currentTF.getText().trim().isEmpty()) {
404 currentTF.selectAll();
405 return;
406 } else if (nextTF.getText().trim().isEmpty()) {
407 nextTF.requestFocusInWindow();
408 nextTF.selectAll();
409 return;
410 } else {
411 OKAction okAction = owner.new OKAction();
412 okAction.actionPerformed(null);
413 }
414 }
415 }
416 }
417
418 class OKAction extends AbstractAction {
419 OKAction() {
420 putValue(NAME, tr("Authenticate"));
421 putValue(SHORT_DESCRIPTION, tr("Authenticate with the supplied username and password"));
422 putValue(SMALL_ICON, ImageProvider.get("ok"));
423 }
424
425 @Override
426 public void actionPerformed(ActionEvent arg0) {
427 setCanceled(false);
428 setVisible(false);
429 }
430 }
431
432 class CancelAction extends AbstractAction {
433 CancelAction() {
434 putValue(NAME, tr("Cancel"));
435 putValue(SHORT_DESCRIPTION, tr("Cancel authentication"));
436 putValue(SMALL_ICON, ImageProvider.get("cancel"));
437 }
438
439 public void cancel() {
440 setCanceled(true);
441 setVisible(false);
442 }
443
444 @Override
445 public void actionPerformed(ActionEvent arg0) {
446 cancel();
447 }
448 }
449
450 class WindowEventHander extends WindowAdapter {
451
452 @Override
453 public void windowActivated(WindowEvent e) {
454 if (pnlCredentials != null) {
455 pnlCredentials.startUserInput();
456 }
457 }
458
459 @Override
460 public void windowClosing(WindowEvent e) {
461 new CancelAction().cancel();
462 }
463 }
464}
Note: See TracBrowser for help on using the repository browser.