001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.oauth; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.BorderLayout; 007import java.awt.Color; 008import java.awt.FlowLayout; 009import java.awt.Font; 010import java.awt.GridBagConstraints; 011import java.awt.GridBagLayout; 012import java.awt.Insets; 013import java.awt.event.ActionEvent; 014import java.awt.event.ItemEvent; 015import java.awt.event.ItemListener; 016 017import javax.swing.AbstractAction; 018import javax.swing.BorderFactory; 019import javax.swing.JCheckBox; 020import javax.swing.JLabel; 021import javax.swing.JPanel; 022import javax.swing.SwingUtilities; 023 024import org.openstreetmap.josm.Main; 025import org.openstreetmap.josm.data.oauth.OAuthToken; 026import org.openstreetmap.josm.gui.SideButton; 027import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder; 028import org.openstreetmap.josm.gui.widgets.HtmlPanel; 029import org.openstreetmap.josm.gui.widgets.JMultilineLabel; 030import org.openstreetmap.josm.tools.ImageProvider; 031import org.openstreetmap.josm.tools.OpenBrowser; 032import org.openstreetmap.josm.gui.widgets.JosmTextField; 033 034/** 035 * This is the UI for running a semic-automic authorisation procedure. 036 * 037 * In contrast to the fully-automatic procedure the user is dispatched to an 038 * external browser for login and authorisation. 039 * 040 * @since 2746 041 */ 042public class SemiAutomaticAuthorizationUI extends AbstractAuthorizationUI { 043 private AccessTokenInfoPanel pnlAccessTokenInfo; 044 private OAuthToken requestToken; 045 046 private RetrieveRequestTokenPanel pnlRetrieveRequestToken; 047 private RetrieveAccessTokenPanel pnlRetrieveAccessToken; 048 private ShowAccessTokenPanel pnlShowAccessToken; 049 050 /** 051 * build the UI 052 */ 053 protected void build() { 054 setLayout(new BorderLayout()); 055 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 056 pnlRetrieveRequestToken = new RetrieveRequestTokenPanel(); 057 pnlRetrieveAccessToken = new RetrieveAccessTokenPanel(); 058 pnlShowAccessToken = new ShowAccessTokenPanel(); 059 add(pnlRetrieveRequestToken, BorderLayout.CENTER); 060 } 061 062 /** 063 * Constructs a new {@code SemiAutomaticAuthorizationUI} for the given API URL. 064 * @param apiUrl The OSM API URL 065 * @since 5422 066 */ 067 public SemiAutomaticAuthorizationUI(String apiUrl) { 068 super(apiUrl); 069 build(); 070 } 071 072 @Override 073 public boolean isSaveAccessTokenToPreferences() { 074 return pnlAccessTokenInfo.isSaveToPreferences(); 075 } 076 077 protected void transitionToRetrieveAccessToken() { 078 OsmOAuthAuthorizationClient client = new OsmOAuthAuthorizationClient( 079 getAdvancedPropertiesPanel().getAdvancedParameters() 080 ); 081 String authoriseUrl = client.getAuthoriseUrl(requestToken); 082 OpenBrowser.displayUrl(authoriseUrl); 083 084 removeAll(); 085 pnlRetrieveAccessToken.setAuthoriseUrl(authoriseUrl); 086 add(pnlRetrieveAccessToken, BorderLayout.CENTER); 087 pnlRetrieveAccessToken.invalidate(); 088 validate(); 089 repaint(); 090 } 091 092 protected void transitionToRetrieveRequestToken() { 093 requestToken = null; 094 setAccessToken(null); 095 removeAll(); 096 add(pnlRetrieveRequestToken, BorderLayout.CENTER); 097 pnlRetrieveRequestToken.invalidate(); 098 validate(); 099 repaint(); 100 } 101 102 protected void transitionToShowAccessToken() { 103 removeAll(); 104 add(pnlShowAccessToken, BorderLayout.CENTER); 105 pnlShowAccessToken.invalidate(); 106 validate(); 107 repaint(); 108 pnlShowAccessToken.setAccessToken(getAccessToken()); 109 } 110 111 /** 112 * This is the panel displayed in the first step of the semi-automatic authorisation 113 * process. 114 */ 115 private class RetrieveRequestTokenPanel extends JPanel { 116 private JCheckBox cbShowAdvancedParameters; 117 118 protected JPanel buildAdvancedParametersPanel() { 119 JPanel pnl = new JPanel(new GridBagLayout()); 120 GridBagConstraints gc= new GridBagConstraints(); 121 122 gc.anchor = GridBagConstraints.NORTHWEST; 123 gc.fill = GridBagConstraints.HORIZONTAL; 124 gc.weightx = 0.0; 125 gc.insets = new Insets(0,0,0,3); 126 pnl.add(cbShowAdvancedParameters = new JCheckBox(), gc); 127 cbShowAdvancedParameters.setSelected(false); 128 cbShowAdvancedParameters.addItemListener( 129 new ItemListener() { 130 @Override 131 public void itemStateChanged(ItemEvent evt) { 132 getAdvancedPropertiesPanel().setVisible(evt.getStateChange() == ItemEvent.SELECTED); 133 } 134 } 135 ); 136 137 gc.gridx = 1; 138 gc.weightx = 1.0; 139 JMultilineLabel lbl = new JMultilineLabel(tr("Display Advanced OAuth Parameters")); 140 lbl.setFont(lbl.getFont().deriveFont(Font.PLAIN)); 141 pnl.add(lbl, gc); 142 143 gc.gridy = 1; 144 gc.gridx = 1; 145 gc.insets = new Insets(3,0,3,0); 146 gc.fill = GridBagConstraints.BOTH; 147 gc.weightx = 1.0; 148 gc.weighty = 1.0; 149 pnl.add(getAdvancedPropertiesPanel(), gc); 150 getAdvancedPropertiesPanel().setBorder( 151 BorderFactory.createCompoundBorder( 152 BorderFactory.createLineBorder(Color.GRAY, 1), 153 BorderFactory.createEmptyBorder(3,3,3,3) 154 ) 155 ); 156 getAdvancedPropertiesPanel().setVisible(false); 157 return pnl; 158 } 159 160 protected JPanel buildCommandPanel() { 161 JPanel pnl = new JPanel(new GridBagLayout()); 162 GridBagConstraints gc= new GridBagConstraints(); 163 164 gc.anchor = GridBagConstraints.NORTHWEST; 165 gc.fill = GridBagConstraints.BOTH; 166 gc.weightx = 1.0; 167 gc.weighty = 1.0; 168 gc.insets = new Insets(0,0,0,3); 169 170 171 HtmlPanel h = new HtmlPanel(); 172 h.setText(tr("<html>" 173 + "Please click on <strong>{0}</strong> to retrieve an OAuth Request Token from " 174 + "''{1}''.</html>", 175 tr("Retrieve Request Token"), 176 getAdvancedPropertiesPanel().getAdvancedParameters().getRequestTokenUrl() 177 )); 178 pnl.add(h, gc); 179 180 JPanel pnl1 = new JPanel(new FlowLayout(FlowLayout.LEFT)); 181 pnl1.add(new SideButton(new RetrieveRequestTokenAction())); 182 gc.fill = GridBagConstraints.HORIZONTAL; 183 gc.weightx = 1.0; 184 gc.gridy = 1; 185 pnl.add(pnl1, gc); 186 return pnl; 187 188 } 189 protected void build() { 190 setLayout(new BorderLayout(0,5)); 191 JLabel lbl = new JLabel(tr("<html>Step 1/3: Retrieve an OAuth Request Token</html>")); 192 lbl.setFont(lbl.getFont().deriveFont(16f)); 193 add(lbl, BorderLayout.NORTH); 194 add(buildAdvancedParametersPanel(), BorderLayout.CENTER); 195 add(buildCommandPanel(), BorderLayout.SOUTH); 196 } 197 198 public RetrieveRequestTokenPanel() { 199 build(); 200 } 201 } 202 203 204 /** 205 * This is the panel displayed in the second step of the semi-automatic authorization 206 * process. 207 */ 208 private class RetrieveAccessTokenPanel extends JPanel { 209 210 private JosmTextField tfAuthoriseUrl; 211 212 protected JPanel buildTitlePanel() { 213 JPanel pnl = new JPanel(new BorderLayout()); 214 JLabel lbl = new JLabel(tr("<html>Step 2/3: Authorize and retrieve an Access Token</html>")); 215 lbl.setFont(lbl.getFont().deriveFont(16f)); 216 pnl.add(lbl, BorderLayout.CENTER); 217 return pnl; 218 } 219 220 protected JPanel buildContentPanel() { 221 JPanel pnl = new JPanel(new GridBagLayout()); 222 GridBagConstraints gc = new GridBagConstraints(); 223 224 gc.anchor= GridBagConstraints.NORTHWEST; 225 gc.fill = GridBagConstraints.HORIZONTAL; 226 gc.weightx = 1.0; 227 gc.gridwidth = 2; 228 HtmlPanel html = new HtmlPanel(); 229 html.setText(tr("<html>" 230 + "JOSM successfully retrieved a Request Token. " 231 + "JOSM is now launching an authorization page in an external browser. " 232 + "Please login with your OSM username and password and follow the instructions " 233 + "to authorize the Request Token. Then switch back to this dialog and click on " 234 + "<strong>{0}</strong><br><br>" 235 + "If launching the external browser fails you can copy the following authorize URL " 236 + "and paste it into the address field of your browser.</html>", 237 tr("Request Access Token") 238 )); 239 pnl.add(html, gc); 240 241 gc.gridx = 0; 242 gc.gridy = 1; 243 gc.weightx = 0.0; 244 gc.gridwidth = 1; 245 pnl.add(new JLabel(tr("Authorize URL:")), gc); 246 247 gc.gridx = 1; 248 gc.weightx = 1.0; 249 pnl.add(tfAuthoriseUrl = new JosmTextField(), gc); 250 tfAuthoriseUrl.setEditable(false); 251 252 return pnl; 253 } 254 255 protected JPanel buildActionPanel() { 256 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT)); 257 258 pnl.add(new SideButton(new BackAction())); 259 pnl.add(new SideButton(new RetrieveAccessTokenAction())); 260 return pnl; 261 } 262 263 protected void build() { 264 setLayout(new BorderLayout()); 265 add(buildTitlePanel(), BorderLayout.NORTH); 266 add(buildContentPanel(), BorderLayout.CENTER); 267 add(buildActionPanel(), BorderLayout.SOUTH); 268 } 269 270 public RetrieveAccessTokenPanel() { 271 build(); 272 } 273 274 public void setAuthoriseUrl(String url) { 275 tfAuthoriseUrl.setText(url); 276 } 277 278 /** 279 * Action to go back to step 1 in the process 280 */ 281 class BackAction extends AbstractAction { 282 public BackAction() { 283 putValue(NAME, tr("Back")); 284 putValue(SHORT_DESCRIPTION, tr("Go back to step 1/3")); 285 putValue(SMALL_ICON, ImageProvider.get("dialogs", "previous")); 286 } 287 288 @Override 289 public void actionPerformed(ActionEvent arg0) { 290 transitionToRetrieveRequestToken(); 291 } 292 } 293 } 294 295 /** 296 * Displays the retrieved Access Token in step 3. 297 */ 298 class ShowAccessTokenPanel extends JPanel { 299 300 protected JPanel buildTitlePanel() { 301 JPanel pnl = new JPanel(new BorderLayout()); 302 JLabel lbl = new JLabel(tr("<html>Step 3/3: Successfully retrieved an Access Token</html>")); 303 lbl.setFont(lbl.getFont().deriveFont(16f)); 304 pnl.add(lbl, BorderLayout.CENTER); 305 return pnl; 306 } 307 308 protected JPanel buildContentPanel() { 309 JPanel pnl = new JPanel(new GridBagLayout()); 310 GridBagConstraints gc = new GridBagConstraints(); 311 312 gc.anchor= GridBagConstraints.NORTHWEST; 313 gc.fill = GridBagConstraints.HORIZONTAL; 314 gc.weightx = 1.0; 315 HtmlPanel html = new HtmlPanel(); 316 html.setText(tr("<html>" 317 + "JOSM has successfully retrieved an Access Token. " 318 + "You can now accept this token. JOSM will use it in the future for authentication " 319 + "and authorization to the OSM server.<br><br>" 320 + "The access token is: </html>" 321 )); 322 pnl.add(html, gc); 323 324 gc.gridx = 0; 325 gc.gridy = 1; 326 gc.weightx = 1.0; 327 gc.gridwidth = 1; 328 pnl.add(pnlAccessTokenInfo = new AccessTokenInfoPanel(), gc); 329 pnlAccessTokenInfo.setSaveToPreferences( 330 OAuthAccessTokenHolder.getInstance().isSaveToPreferences() 331 ); 332 return pnl; 333 } 334 335 protected JPanel buildActionPanel() { 336 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT)); 337 pnl.add(new SideButton(new RestartAction())); 338 pnl.add(new SideButton(new TestAccessTokenAction())); 339 return pnl; 340 } 341 342 protected void build() { 343 setLayout(new BorderLayout()); 344 add(buildTitlePanel(), BorderLayout.NORTH); 345 add(buildContentPanel(), BorderLayout.CENTER); 346 add(buildActionPanel(), BorderLayout.SOUTH); 347 } 348 349 public ShowAccessTokenPanel() { 350 build(); 351 } 352 353 /** 354 * Action to go back to step 1 in the process 355 */ 356 class RestartAction extends AbstractAction { 357 public RestartAction() { 358 putValue(NAME, tr("Restart")); 359 putValue(SHORT_DESCRIPTION, tr("Go back to step 1/3")); 360 putValue(SMALL_ICON, ImageProvider.get("dialogs", "previous")); 361 } 362 363 @Override 364 public void actionPerformed(ActionEvent arg0) { 365 transitionToRetrieveRequestToken(); 366 } 367 } 368 369 public void setAccessToken(OAuthToken accessToken) { 370 pnlAccessTokenInfo.setAccessToken(accessToken); 371 } 372 } 373 374 /** 375 * Action for retrieving a request token 376 */ 377 class RetrieveRequestTokenAction extends AbstractAction{ 378 379 public RetrieveRequestTokenAction() { 380 putValue(NAME, tr("Retrieve Request Token")); 381 putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth")); 382 putValue(SHORT_DESCRIPTION, tr("Click to retrieve a Request Token")); 383 } 384 385 @Override 386 public void actionPerformed(ActionEvent evt) { 387 final RetrieveRequestTokenTask task = new RetrieveRequestTokenTask( 388 SemiAutomaticAuthorizationUI.this, 389 getAdvancedPropertiesPanel().getAdvancedParameters() 390 ); 391 Main.worker.submit(task); 392 Runnable r = new Runnable() { 393 @Override 394 public void run() { 395 if (task.isCanceled()) return; 396 if (task.getRequestToken() == null) return; 397 requestToken = task.getRequestToken(); 398 SwingUtilities.invokeLater(new Runnable() { 399 @Override 400 public void run() { 401 transitionToRetrieveAccessToken(); 402 } 403 }); 404 } 405 }; 406 Main.worker.submit(r); 407 } 408 } 409 410 /** 411 * Action for retrieving an Access Token 412 */ 413 class RetrieveAccessTokenAction extends AbstractAction { 414 415 public RetrieveAccessTokenAction() { 416 putValue(NAME, tr("Retrieve Access Token")); 417 putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth")); 418 putValue(SHORT_DESCRIPTION, tr("Click to retrieve an Access Token")); 419 } 420 421 @Override 422 public void actionPerformed(ActionEvent evt) { 423 final RetrieveAccessTokenTask task = new RetrieveAccessTokenTask( 424 SemiAutomaticAuthorizationUI.this, 425 getAdvancedPropertiesPanel().getAdvancedParameters(), 426 requestToken 427 ); 428 Main.worker.submit(task); 429 Runnable r = new Runnable() { 430 @Override 431 public void run() { 432 if (task.isCanceled()) return; 433 if (task.getAccessToken() == null) return; 434 setAccessToken(task.getAccessToken()); 435 SwingUtilities.invokeLater(new Runnable() { 436 @Override 437 public void run() { 438 transitionToShowAccessToken(); 439 } 440 }); 441 } 442 }; 443 Main.worker.submit(r); 444 } 445 } 446 447 /** 448 * Action for testing an Access Token 449 */ 450 class TestAccessTokenAction extends AbstractAction { 451 452 public TestAccessTokenAction() { 453 putValue(NAME, tr("Test Access Token")); 454 putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth")); 455 putValue(SHORT_DESCRIPTION, tr("Click to test the Access Token")); 456 } 457 458 @Override 459 public void actionPerformed(ActionEvent evt) { 460 TestAccessTokenTask task = new TestAccessTokenTask( 461 SemiAutomaticAuthorizationUI.this, 462 getApiUrl(), 463 getAdvancedPropertiesPanel().getAdvancedParameters(), 464 getAccessToken() 465 ); 466 Main.worker.submit(task); 467 } 468 } 469}