source: josm/trunk/src/org/openstreetmap/josm/gui/oauth/FullyAutomaticAuthorizationUI.java@ 8836

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

fix Checkstyle issues

  • Property svn:eol-style set to native
File size: 20.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.oauth;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Component;
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.io.IOException;
15import java.net.Authenticator.RequestorType;
16import java.net.PasswordAuthentication;
17
18import javax.swing.AbstractAction;
19import javax.swing.BorderFactory;
20import javax.swing.JLabel;
21import javax.swing.JOptionPane;
22import javax.swing.JPanel;
23import javax.swing.JTabbedPane;
24import javax.swing.event.DocumentEvent;
25import javax.swing.event.DocumentListener;
26import javax.swing.text.JTextComponent;
27import javax.swing.text.html.HTMLEditorKit;
28
29import org.openstreetmap.josm.Main;
30import org.openstreetmap.josm.data.Preferences;
31import org.openstreetmap.josm.data.oauth.OAuthToken;
32import org.openstreetmap.josm.gui.HelpAwareOptionPane;
33import org.openstreetmap.josm.gui.PleaseWaitRunnable;
34import org.openstreetmap.josm.gui.SideButton;
35import org.openstreetmap.josm.gui.help.HelpUtil;
36import org.openstreetmap.josm.gui.util.GuiHelper;
37import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
38import org.openstreetmap.josm.gui.widgets.HtmlPanel;
39import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
40import org.openstreetmap.josm.gui.widgets.JosmPasswordField;
41import org.openstreetmap.josm.gui.widgets.JosmTextField;
42import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
43import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
44import org.openstreetmap.josm.io.OsmApi;
45import org.openstreetmap.josm.io.OsmTransferException;
46import org.openstreetmap.josm.io.auth.CredentialsAgent;
47import org.openstreetmap.josm.io.auth.CredentialsAgentException;
48import org.openstreetmap.josm.io.auth.CredentialsManager;
49import org.openstreetmap.josm.tools.ImageProvider;
50import org.xml.sax.SAXException;
51
52/**
53 * This is an UI which supports a JOSM user to get an OAuth Access Token in a fully
54 * automatic process.
55 *
56 * @since 2746
57 */
58public class FullyAutomaticAuthorizationUI extends AbstractAuthorizationUI {
59
60 private JosmTextField tfUserName;
61 private JosmPasswordField tfPassword;
62 private transient UserNameValidator valUserName;
63 private transient PasswordValidator valPassword;
64 private AccessTokenInfoPanel pnlAccessTokenInfo;
65 private OsmPrivilegesPanel pnlOsmPrivileges;
66 private JPanel pnlPropertiesPanel;
67 private JPanel pnlActionButtonsPanel;
68 private JPanel pnlResult;
69
70 /**
71 * Builds the panel with the three privileges the user can grant JOSM
72 *
73 * @return constructed panel for the privileges
74 */
75 protected VerticallyScrollablePanel buildGrantsPanel() {
76 pnlOsmPrivileges = new OsmPrivilegesPanel();
77 return pnlOsmPrivileges;
78 }
79
80 /**
81 * Builds the panel for entering the username and password
82 *
83 * @return constructed panel for the creditentials
84 */
85 protected VerticallyScrollablePanel buildUserNamePasswordPanel() {
86 VerticallyScrollablePanel pnl = new VerticallyScrollablePanel(new GridBagLayout());
87 GridBagConstraints gc = new GridBagConstraints();
88 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
89
90 gc.anchor = GridBagConstraints.NORTHWEST;
91 gc.fill = GridBagConstraints.HORIZONTAL;
92 gc.weightx = 1.0;
93 gc.gridwidth = 2;
94 HtmlPanel pnlMessage = new HtmlPanel();
95 HTMLEditorKit kit = (HTMLEditorKit) pnlMessage.getEditorPane().getEditorKit();
96 kit.getStyleSheet().addRule(
97 ".warning-body {background-color:rgb(253,255,221);padding: 10pt; " +
98 "border-color:rgb(128,128,128);border-style: solid;border-width: 1px;}");
99 kit.getStyleSheet().addRule("ol {margin-left: 1cm}");
100 pnlMessage.setText("<html><body><p class=\"warning-body\">"
101 + tr("Please enter your OSM user name and password. The password will <strong>not</strong> be saved "
102 + "in clear text in the JOSM preferences and it will be submitted to the OSM server <strong>only once</strong>. "
103 + "Subsequent data upload requests don''t use your password any more.")
104 + "</p>"
105 + "</body></html>");
106 pnl.add(pnlMessage, gc);
107
108 // the user name input field
109 gc.gridy = 1;
110 gc.gridwidth = 1;
111 gc.anchor = GridBagConstraints.NORTHWEST;
112 gc.fill = GridBagConstraints.HORIZONTAL;
113 gc.weightx = 0.0;
114 gc.insets = new Insets(0, 0, 3, 3);
115 pnl.add(new JLabel(tr("Username: ")), gc);
116
117 gc.gridx = 1;
118 gc.weightx = 1.0;
119 pnl.add(tfUserName = new JosmTextField(), gc);
120 SelectAllOnFocusGainedDecorator.decorate(tfUserName);
121 valUserName = new UserNameValidator(tfUserName);
122 valUserName.validate();
123
124 // the password input field
125 gc.anchor = GridBagConstraints.NORTHWEST;
126 gc.fill = GridBagConstraints.HORIZONTAL;
127 gc.gridy = 2;
128 gc.gridx = 0;
129 gc.weightx = 0.0;
130 pnl.add(new JLabel(tr("Password: ")), gc);
131
132 gc.gridx = 1;
133 gc.weightx = 1.0;
134 pnl.add(tfPassword = new JosmPasswordField(), gc);
135 SelectAllOnFocusGainedDecorator.decorate(tfPassword);
136 valPassword = new PasswordValidator(tfPassword);
137 valPassword.validate();
138
139 gc.gridy = 3;
140 gc.gridx = 0;
141 gc.anchor = GridBagConstraints.NORTHWEST;
142 gc.fill = GridBagConstraints.HORIZONTAL;
143 gc.weightx = 1.0;
144 gc.gridwidth = 2;
145 pnlMessage = new HtmlPanel();
146 kit = (HTMLEditorKit) pnlMessage.getEditorPane().getEditorKit();
147 kit.getStyleSheet().addRule(
148 ".warning-body {background-color:rgb(253,255,221);padding: 10pt; " +
149 "border-color:rgb(128,128,128);border-style: solid;border-width: 1px;}");
150 kit.getStyleSheet().addRule("ol {margin-left: 1cm}");
151 pnlMessage.setText("<html><body>"
152 + "<p class=\"warning-body\">"
153 + tr("<strong>Warning:</strong> JOSM does login <strong>once</strong> using a secure connection.")
154 + "</p>"
155 + "</body></html>");
156 pnl.add(pnlMessage, gc);
157
158 // filler - grab remaining space
159 gc.gridy = 4;
160 gc.gridwidth = 2;
161 gc.fill = GridBagConstraints.BOTH;
162 gc.weightx = 1.0;
163 gc.weighty = 1.0;
164 pnl.add(new JPanel(), gc);
165
166 return pnl;
167 }
168
169 protected JPanel buildPropertiesPanel() {
170 JPanel pnl = new JPanel(new BorderLayout());
171
172 JTabbedPane tpProperties = new JTabbedPane();
173 tpProperties.add(buildUserNamePasswordPanel().getVerticalScrollPane());
174 tpProperties.add(buildGrantsPanel().getVerticalScrollPane());
175 tpProperties.add(getAdvancedPropertiesPanel().getVerticalScrollPane());
176 tpProperties.setTitleAt(0, tr("Basic"));
177 tpProperties.setTitleAt(1, tr("Granted rights"));
178 tpProperties.setTitleAt(2, tr("Advanced OAuth properties"));
179
180 pnl.add(tpProperties, BorderLayout.CENTER);
181 return pnl;
182 }
183
184 /**
185 * Initializes the panel with values from the preferences
186 * @param pref Preferences structure
187 */
188 @Override
189 public void initFromPreferences(Preferences pref) {
190 super.initFromPreferences(pref);
191 CredentialsAgent cm = CredentialsManager.getInstance();
192 try {
193 PasswordAuthentication pa = cm.lookup(RequestorType.SERVER, OsmApi.getOsmApi().getHost());
194 if (pa == null) {
195 tfUserName.setText("");
196 tfPassword.setText("");
197 } else {
198 tfUserName.setText(pa.getUserName() == null ? "" : pa.getUserName());
199 tfPassword.setText(pa.getPassword() == null ? "" : String.valueOf(pa.getPassword()));
200 }
201 } catch (CredentialsAgentException e) {
202 Main.error(e);
203 tfUserName.setText("");
204 tfPassword.setText("");
205 }
206 }
207
208 /**
209 * Builds the panel with the action button for starting the authorisation
210 *
211 * @return constructed button panel
212 */
213 protected JPanel buildActionButtonPanel() {
214 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
215
216 RunAuthorisationAction runAuthorisationAction = new RunAuthorisationAction();
217 tfPassword.getDocument().addDocumentListener(runAuthorisationAction);
218 tfUserName.getDocument().addDocumentListener(runAuthorisationAction);
219 pnl.add(new SideButton(runAuthorisationAction));
220 return pnl;
221 }
222
223 /**
224 * Builds the panel which displays the generated Access Token.
225 *
226 * @return constructed panel for the results
227 */
228 protected JPanel buildResultsPanel() {
229 JPanel pnl = new JPanel(new GridBagLayout());
230 GridBagConstraints gc = new GridBagConstraints();
231 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
232
233 // the message panel
234 gc.anchor = GridBagConstraints.NORTHWEST;
235 gc.fill = GridBagConstraints.HORIZONTAL;
236 gc.weightx = 1.0;
237 JMultilineLabel msg = new JMultilineLabel("");
238 msg.setFont(msg.getFont().deriveFont(Font.PLAIN));
239 String lbl = tr("Accept Access Token");
240 msg.setText(tr("<html>"
241 + "You have successfully retrieved an OAuth Access Token from the OSM website. "
242 + "Click on <strong>{0}</strong> to accept the token. JOSM will use it in "
243 + "subsequent requests to gain access to the OSM API."
244 + "</html>", lbl));
245 pnl.add(msg, gc);
246
247 // infos about the access token
248 gc.gridy = 1;
249 gc.insets = new Insets(5, 0, 0, 0);
250 pnl.add(pnlAccessTokenInfo = new AccessTokenInfoPanel(), gc);
251
252 // the actions
253 JPanel pnl1 = new JPanel(new FlowLayout(FlowLayout.LEFT));
254 pnl1.add(new SideButton(new BackAction()));
255 pnl1.add(new SideButton(new TestAccessTokenAction()));
256 gc.gridy = 2;
257 pnl.add(pnl1, gc);
258
259 // filler - grab the remaining space
260 gc.gridy = 3;
261 gc.fill = GridBagConstraints.BOTH;
262 gc.weightx = 1.0;
263 gc.weighty = 1.0;
264 pnl.add(new JPanel(), gc);
265
266 return pnl;
267 }
268
269 protected final void build() {
270 setLayout(new BorderLayout());
271 pnlPropertiesPanel = buildPropertiesPanel();
272 pnlActionButtonsPanel = buildActionButtonPanel();
273 pnlResult = buildResultsPanel();
274
275 prepareUIForEnteringRequest();
276 }
277
278 /**
279 * Prepares the UI for the first step in the automatic process: entering the authentication
280 * and authorisation parameters.
281 *
282 */
283 protected void prepareUIForEnteringRequest() {
284 removeAll();
285 add(pnlPropertiesPanel, BorderLayout.CENTER);
286 add(pnlActionButtonsPanel, BorderLayout.SOUTH);
287 pnlPropertiesPanel.revalidate();
288 pnlActionButtonsPanel.revalidate();
289 validate();
290 repaint();
291
292 setAccessToken(null);
293 }
294
295 /**
296 * Prepares the UI for the second step in the automatic process: displaying the access token
297 *
298 */
299 protected void prepareUIForResultDisplay() {
300 removeAll();
301 add(pnlResult, BorderLayout.CENTER);
302 validate();
303 repaint();
304 }
305
306 protected String getOsmUserName() {
307 return tfUserName.getText();
308 }
309
310 protected String getOsmPassword() {
311 return String.valueOf(tfPassword.getPassword());
312 }
313
314 /**
315 * Constructs a new {@code FullyAutomaticAuthorizationUI} for the given API URL.
316 * @param apiUrl The OSM API URL
317 * @since 5422
318 */
319 public FullyAutomaticAuthorizationUI(String apiUrl) {
320 super(apiUrl);
321 build();
322 }
323
324 @Override
325 public boolean isSaveAccessTokenToPreferences() {
326 return pnlAccessTokenInfo.isSaveToPreferences();
327 }
328
329 @Override
330 protected void setAccessToken(OAuthToken accessToken) {
331 super.setAccessToken(accessToken);
332 pnlAccessTokenInfo.setAccessToken(accessToken);
333 }
334
335 /**
336 * Starts the authorisation process
337 */
338 class RunAuthorisationAction extends AbstractAction implements DocumentListener{
339 RunAuthorisationAction() {
340 putValue(NAME, tr("Authorize now"));
341 putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth-small"));
342 putValue(SHORT_DESCRIPTION, tr("Click to redirect you to the authorization form on the JOSM web site"));
343 updateEnabledState();
344 }
345
346 @Override
347 public void actionPerformed(ActionEvent evt) {
348 Main.worker.submit(new FullyAutomaticAuthorisationTask(FullyAutomaticAuthorizationUI.this));
349 }
350
351 protected final void updateEnabledState() {
352 setEnabled(valPassword.isValid() && valUserName.isValid());
353 }
354
355 @Override
356 public void changedUpdate(DocumentEvent e) {
357 updateEnabledState();
358 }
359
360 @Override
361 public void insertUpdate(DocumentEvent e) {
362 updateEnabledState();
363 }
364
365 @Override
366 public void removeUpdate(DocumentEvent e) {
367 updateEnabledState();
368 }
369 }
370
371 /**
372 * Action to go back to step 1 in the process
373 */
374 class BackAction extends AbstractAction {
375 BackAction() {
376 putValue(NAME, tr("Back"));
377 putValue(SHORT_DESCRIPTION, tr("Run the automatic authorization steps again"));
378 putValue(SMALL_ICON, ImageProvider.get("dialogs", "previous"));
379 }
380
381 @Override
382 public void actionPerformed(ActionEvent arg0) {
383 prepareUIForEnteringRequest();
384 }
385 }
386
387 /**
388 * Action to test an access token.
389 */
390 class TestAccessTokenAction extends AbstractAction {
391 TestAccessTokenAction() {
392 putValue(NAME, tr("Test Access Token"));
393 putValue(SMALL_ICON, ImageProvider.get("logo"));
394 }
395
396 @Override
397 public void actionPerformed(ActionEvent arg0) {
398 Main.worker.submit(new TestAccessTokenTask(
399 FullyAutomaticAuthorizationUI.this,
400 getApiUrl(),
401 getAdvancedPropertiesPanel().getAdvancedParameters(),
402 getAccessToken()
403 ));
404 }
405 }
406
407 private static class UserNameValidator extends AbstractTextComponentValidator {
408 UserNameValidator(JTextComponent tc) {
409 super(tc);
410 }
411
412 @Override
413 public boolean isValid() {
414 return !getComponent().getText().trim().isEmpty();
415 }
416
417 @Override
418 public void validate() {
419 if (isValid()) {
420 feedbackValid(tr("Please enter your OSM user name"));
421 } else {
422 feedbackInvalid(tr("The user name cannot be empty. Please enter your OSM user name"));
423 }
424 }
425 }
426
427 private static class PasswordValidator extends AbstractTextComponentValidator {
428
429 PasswordValidator(JTextComponent tc) {
430 super(tc);
431 }
432
433 @Override
434 public boolean isValid() {
435 return !getComponent().getText().trim().isEmpty();
436 }
437
438 @Override
439 public void validate() {
440 if (isValid()) {
441 feedbackValid(tr("Please enter your OSM password"));
442 } else {
443 feedbackInvalid(tr("The password cannot be empty. Please enter your OSM password"));
444 }
445 }
446 }
447
448 class FullyAutomaticAuthorisationTask extends PleaseWaitRunnable {
449 private boolean canceled;
450 private OsmOAuthAuthorizationClient authClient;
451
452 FullyAutomaticAuthorisationTask(Component parent) {
453 super(parent, tr("Authorize JOSM to access the OSM API"), false /* don't ignore exceptions */);
454 }
455
456 @Override
457 protected void cancel() {
458 canceled = true;
459 }
460
461 @Override
462 protected void finish() {}
463
464 protected void alertAuthorisationFailed(OsmOAuthAuthorizationException e) {
465 HelpAwareOptionPane.showOptionDialog(
466 FullyAutomaticAuthorizationUI.this,
467 tr("<html>"
468 + "The automatic process for retrieving an OAuth Access Token<br>"
469 + "from the OSM server failed.<br><br>"
470 + "Please try again or choose another kind of authorization process,<br>"
471 + "i.e. semi-automatic or manual authorization."
472 +"</html>"),
473 tr("OAuth authorization failed"),
474 JOptionPane.ERROR_MESSAGE,
475 HelpUtil.ht("/Dialog/OAuthAuthorisationWizard#FullyAutomaticProcessFailed")
476 );
477 }
478
479 protected void alertInvalidLoginUrl() {
480 HelpAwareOptionPane.showOptionDialog(
481 FullyAutomaticAuthorizationUI.this,
482 tr("<html>"
483 + "The automatic process for retrieving an OAuth Access Token<br>"
484 + "from the OSM server failed because JOSM was not able to build<br>"
485 + "a valid login URL from the OAuth Authorize Endpoint URL ''{0}''.<br><br>"
486 + "Please check your advanced setting and try again."
487 + "</html>",
488 getAdvancedPropertiesPanel().getAdvancedParameters().getAuthoriseUrl()),
489 tr("OAuth authorization failed"),
490 JOptionPane.ERROR_MESSAGE,
491 HelpUtil.ht("/Dialog/OAuthAuthorisationWizard#FullyAutomaticProcessFailed")
492 );
493 }
494
495 protected void alertLoginFailed(OsmLoginFailedException e) {
496 String loginUrl = null;
497 try {
498 loginUrl = authClient.buildOsmLoginUrl();
499 } catch (OsmOAuthAuthorizationException e1) {
500 alertInvalidLoginUrl();
501 return;
502 }
503 HelpAwareOptionPane.showOptionDialog(
504 FullyAutomaticAuthorizationUI.this,
505 tr("<html>"
506 + "The automatic process for retrieving an OAuth Access Token<br>"
507 + "from the OSM server failed. JOSM failed to log into {0}<br>"
508 + "for user {1}.<br><br>"
509 + "Please check username and password and try again."
510 +"</html>",
511 loginUrl,
512 getOsmUserName()),
513 tr("OAuth authorization failed"),
514 JOptionPane.ERROR_MESSAGE,
515 HelpUtil.ht("/Dialog/OAuthAuthorisationWizard#FullyAutomaticProcessFailed")
516 );
517 }
518
519 protected void handleException(final OsmOAuthAuthorizationException e) {
520 Runnable r = new Runnable() {
521 @Override
522 public void run() {
523 if (e instanceof OsmLoginFailedException) {
524 alertLoginFailed((OsmLoginFailedException) e);
525 } else {
526 alertAuthorisationFailed(e);
527 }
528 }
529 };
530 Main.error(e);
531 GuiHelper.runInEDT(r);
532 }
533
534 @Override
535 protected void realRun() throws SAXException, IOException, OsmTransferException {
536 try {
537 getProgressMonitor().setTicksCount(3);
538 authClient = new OsmOAuthAuthorizationClient(
539 getAdvancedPropertiesPanel().getAdvancedParameters()
540 );
541 OAuthToken requestToken = authClient.getRequestToken(
542 getProgressMonitor().createSubTaskMonitor(1, false)
543 );
544 getProgressMonitor().worked(1);
545 if (canceled) return;
546 authClient.authorise(
547 requestToken,
548 getOsmUserName(),
549 getOsmPassword(),
550 pnlOsmPrivileges.getPrivileges(),
551 getProgressMonitor().createSubTaskMonitor(1, false)
552 );
553 getProgressMonitor().worked(1);
554 if (canceled) return;
555 final OAuthToken accessToken = authClient.getAccessToken(
556 getProgressMonitor().createSubTaskMonitor(1, false)
557 );
558 getProgressMonitor().worked(1);
559 if (canceled) return;
560 GuiHelper.runInEDT(new Runnable() {
561 @Override
562 public void run() {
563 prepareUIForResultDisplay();
564 setAccessToken(accessToken);
565 }
566 });
567 } catch (final OsmOAuthAuthorizationException e) {
568 handleException(e);
569 }
570 }
571 }
572}
Note: See TracBrowser for help on using the repository browser.