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

Last change on this file since 6084 was 6084, checked in by bastiK, 11 years ago

see #8902 - add missing @Override annotations (patch by shinigami)

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