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

Last change on this file since 12531 was 12452, checked in by bastiK, 7 years ago

see #14794 - javadoc for the josm.gui.io package

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