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    }