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