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

Last change on this file since 12854 was 12854, checked in by bastiK, 15 months ago

see #15229 - fix checkstyle warnings

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