001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.preferences.server; 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; 016import java.beans.PropertyChangeEvent; 017import java.beans.PropertyChangeListener; 018 019import javax.swing.AbstractAction; 020import javax.swing.BorderFactory; 021import javax.swing.JCheckBox; 022import javax.swing.JLabel; 023import javax.swing.JPanel; 024 025import org.openstreetmap.josm.Main; 026import org.openstreetmap.josm.data.oauth.OAuthParameters; 027import org.openstreetmap.josm.data.oauth.OAuthToken; 028import org.openstreetmap.josm.gui.SideButton; 029import org.openstreetmap.josm.gui.oauth.AdvancedOAuthPropertiesPanel; 030import org.openstreetmap.josm.gui.oauth.OAuthAuthorizationWizard; 031import org.openstreetmap.josm.gui.oauth.TestAccessTokenTask; 032import org.openstreetmap.josm.gui.widgets.JMultilineLabel; 033import org.openstreetmap.josm.gui.widgets.JosmTextField; 034import org.openstreetmap.josm.io.OsmApi; 035import org.openstreetmap.josm.io.auth.CredentialsManager; 036import org.openstreetmap.josm.tools.ImageProvider; 037 038/** 039 * The preferences panel for the OAuth preferences. This just a summary panel 040 * showing the current Access Token Key and Access Token Secret, if the 041 * user already has an Access Token. 042 * 043 * For initial authorisation see {@link OAuthAuthorizationWizard}. 044 * 045 */ 046public class OAuthAuthenticationPreferencesPanel extends JPanel implements PropertyChangeListener { 047 private JPanel pnlAuthorisationMessage; 048 private NotYetAuthorisedPanel pnlNotYetAuthorised; 049 private AlreadyAuthorisedPanel pnlAlreadyAuthorised; 050 private AdvancedOAuthPropertiesPanel pnlAdvancedProperties; 051 private String apiUrl; 052 private JCheckBox cbShowAdvancedParameters; 053 private JCheckBox cbSaveToPreferences; 054 055 /** 056 * Builds the panel for entering the advanced OAuth parameters 057 * 058 * @return panel with advanced settings 059 */ 060 protected JPanel buildAdvancedPropertiesPanel() { 061 JPanel pnl = new JPanel(new GridBagLayout()); 062 GridBagConstraints gc = new GridBagConstraints(); 063 064 gc.anchor = GridBagConstraints.NORTHWEST; 065 gc.fill = GridBagConstraints.HORIZONTAL; 066 gc.weightx = 0.0; 067 gc.insets = new Insets(0, 0, 0, 3); 068 pnl.add(cbShowAdvancedParameters = new JCheckBox(), gc); 069 cbShowAdvancedParameters.setSelected(false); 070 cbShowAdvancedParameters.addItemListener( 071 new ItemListener() { 072 @Override 073 public void itemStateChanged(ItemEvent evt) { 074 pnlAdvancedProperties.setVisible(evt.getStateChange() == ItemEvent.SELECTED); 075 } 076 } 077 ); 078 079 gc.gridx = 1; 080 gc.weightx = 1.0; 081 JMultilineLabel lbl = new JMultilineLabel(tr("Display Advanced OAuth Parameters")); 082 lbl.setFont(lbl.getFont().deriveFont(Font.PLAIN)); 083 pnl.add(lbl, gc); 084 085 gc.gridy = 1; 086 gc.gridx = 1; 087 gc.insets = new Insets(3, 0, 3, 0); 088 gc.fill = GridBagConstraints.BOTH; 089 gc.weightx = 1.0; 090 gc.weighty = 1.0; 091 pnl.add(pnlAdvancedProperties = new AdvancedOAuthPropertiesPanel(), gc); 092 pnlAdvancedProperties.initFromPreferences(Main.pref); 093 pnlAdvancedProperties.setBorder( 094 BorderFactory.createCompoundBorder( 095 BorderFactory.createLineBorder(Color.GRAY, 1), 096 BorderFactory.createEmptyBorder(3, 3, 3, 3) 097 ) 098 ); 099 pnlAdvancedProperties.setVisible(false); 100 return pnl; 101 } 102 103 /** 104 * builds the UI 105 */ 106 protected final void build() { 107 setLayout(new GridBagLayout()); 108 setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 109 GridBagConstraints gc = new GridBagConstraints(); 110 111 // the panel for the OAuth parameters. pnlAuthorisationMessage is an 112 // empty panel. It is going to be filled later, depending on the 113 // current OAuth state in JOSM. 114 gc.fill = GridBagConstraints.BOTH; 115 gc.anchor = GridBagConstraints.NORTHWEST; 116 gc.weighty = 1.0; 117 gc.weightx = 1.0; 118 gc.insets = new Insets(10, 0, 0, 0); 119 add(pnlAuthorisationMessage = new JPanel(), gc); 120 pnlAuthorisationMessage.setLayout(new BorderLayout()); 121 122 // create these two panels, they are going to be used later in refreshView 123 // 124 pnlAlreadyAuthorised = new AlreadyAuthorisedPanel(); 125 pnlNotYetAuthorised = new NotYetAuthorisedPanel(); 126 } 127 128 protected void refreshView() { 129 pnlAuthorisationMessage.removeAll(); 130 if (OAuthAccessTokenHolder.getInstance().containsAccessToken()) { 131 pnlAuthorisationMessage.add(pnlAlreadyAuthorised, BorderLayout.CENTER); 132 pnlAlreadyAuthorised.refreshView(); 133 pnlAlreadyAuthorised.revalidate(); 134 } else { 135 pnlAuthorisationMessage.add(pnlNotYetAuthorised, BorderLayout.CENTER); 136 pnlNotYetAuthorised.revalidate(); 137 } 138 repaint(); 139 } 140 141 /** 142 * Create the panel 143 */ 144 public OAuthAuthenticationPreferencesPanel() { 145 build(); 146 refreshView(); 147 } 148 149 /** 150 * Sets the URL of the OSM API for which this panel is currently displaying OAuth properties. 151 * 152 * @param apiUrl the api URL 153 */ 154 public void setApiUrl(String apiUrl) { 155 this.apiUrl = apiUrl; 156 pnlAdvancedProperties.setApiUrl(apiUrl); 157 } 158 159 /** 160 * Initializes the panel from preferences 161 */ 162 public void initFromPreferences() { 163 setApiUrl(Main.pref.get("osm-server.url", OsmApi.DEFAULT_API_URL).trim()); 164 refreshView(); 165 } 166 167 /** 168 * Saves the current values to preferences 169 */ 170 public void saveToPreferences() { 171 OAuthAccessTokenHolder.getInstance().setSaveToPreferences(cbSaveToPreferences.isSelected()); 172 OAuthAccessTokenHolder.getInstance().save(Main.pref, CredentialsManager.getInstance()); 173 pnlAdvancedProperties.rememberPreferences(Main.pref); 174 } 175 176 /** 177 * The preferences panel displayed if there is currently no Access Token available. 178 * This means that the user didn't run through the OAuth authorisation procedure yet. 179 * 180 */ 181 private class NotYetAuthorisedPanel extends JPanel { 182 /** 183 * Constructs a new {@code NotYetAuthorisedPanel}. 184 */ 185 NotYetAuthorisedPanel() { 186 build(); 187 } 188 189 protected void build() { 190 setLayout(new GridBagLayout()); 191 GridBagConstraints gc = new GridBagConstraints(); 192 193 // A message explaining that the user isn't authorised yet 194 gc.anchor = GridBagConstraints.NORTHWEST; 195 gc.insets = new Insets(0, 0, 3, 0); 196 gc.fill = GridBagConstraints.HORIZONTAL; 197 gc.weightx = 1.0; 198 JMultilineLabel lbl; 199 add(lbl = new JMultilineLabel( 200 tr("You do not have an Access Token yet to access the OSM server using OAuth. Please authorize first.")), gc); 201 lbl.setFont(lbl.getFont().deriveFont(Font.PLAIN)); 202 203 // Action for authorising now 204 gc.gridy = 1; 205 gc.fill = GridBagConstraints.NONE; 206 gc.weightx = 0.0; 207 add(new SideButton(new AuthoriseNowAction()), gc); 208 209 // filler - grab remaining space 210 gc.gridy = 2; 211 gc.fill = GridBagConstraints.BOTH; 212 gc.weightx = 1.0; 213 gc.weighty = 1.0; 214 add(new JPanel(), gc); 215 } 216 } 217 218 /** 219 * The preferences panel displayed if there is currently an AccessToken available. 220 * 221 */ 222 private class AlreadyAuthorisedPanel extends JPanel { 223 private JosmTextField tfAccessTokenKey; 224 private JosmTextField tfAccessTokenSecret; 225 226 protected void build() { 227 setLayout(new GridBagLayout()); 228 GridBagConstraints gc = new GridBagConstraints(); 229 gc.anchor = GridBagConstraints.NORTHWEST; 230 gc.insets = new Insets(0, 0, 3, 3); 231 gc.fill = GridBagConstraints.HORIZONTAL; 232 gc.weightx = 1.0; 233 gc.gridwidth = 2; 234 JMultilineLabel lbl; 235 add(lbl = new JMultilineLabel(tr("You already have an Access Token to access the OSM server using OAuth.")), gc); 236 lbl.setFont(lbl.getFont().deriveFont(Font.PLAIN)); 237 238 // -- access token key 239 gc.gridy = 1; 240 gc.gridx = 0; 241 gc.gridwidth = 1; 242 gc.weightx = 0.0; 243 add(new JLabel(tr("Access Token Key:")), gc); 244 245 gc.gridx = 1; 246 gc.weightx = 1.0; 247 add(tfAccessTokenKey = new JosmTextField(), gc); 248 tfAccessTokenKey.setEditable(false); 249 250 // -- access token secret 251 gc.gridy = 2; 252 gc.gridx = 0; 253 gc.gridwidth = 1; 254 gc.weightx = 0.0; 255 add(new JLabel(tr("Access Token Secret:")), gc); 256 257 gc.gridx = 1; 258 gc.weightx = 1.0; 259 add(tfAccessTokenSecret = new JosmTextField(), gc); 260 tfAccessTokenSecret.setEditable(false); 261 262 // -- access token secret 263 gc.gridy = 3; 264 gc.gridx = 0; 265 gc.gridwidth = 2; 266 gc.weightx = 1.0; 267 add(cbSaveToPreferences = new JCheckBox(tr("Save to preferences")), gc); 268 cbSaveToPreferences.setSelected(OAuthAccessTokenHolder.getInstance().isSaveToPreferences()); 269 270 // -- action buttons 271 JPanel btns = new JPanel(new FlowLayout(FlowLayout.LEFT)); 272 btns.add(new SideButton(new RenewAuthorisationAction())); 273 btns.add(new SideButton(new TestAuthorisationAction())); 274 gc.gridy = 4; 275 gc.gridx = 0; 276 gc.gridwidth = 2; 277 gc.weightx = 1.0; 278 add(btns, gc); 279 280 // the panel with the advanced options 281 gc.gridy = 5; 282 gc.gridx = 0; 283 gc.gridwidth = 2; 284 gc.weightx = 1.0; 285 add(buildAdvancedPropertiesPanel(), gc); 286 287 // filler - grab the remaining space 288 gc.gridy = 6; 289 gc.fill = GridBagConstraints.BOTH; 290 gc.weightx = 1.0; 291 gc.weighty = 1.0; 292 add(new JPanel(), gc); 293 294 } 295 296 public final void refreshView() { 297 String v = OAuthAccessTokenHolder.getInstance().getAccessTokenKey(); 298 tfAccessTokenKey.setText(v == null ? "" : v); 299 v = OAuthAccessTokenHolder.getInstance().getAccessTokenSecret(); 300 tfAccessTokenSecret.setText(v == null ? "" : v); 301 cbSaveToPreferences.setSelected(OAuthAccessTokenHolder.getInstance().isSaveToPreferences()); 302 } 303 304 /** 305 * Constructs a new {@code AlreadyAuthorisedPanel}. 306 */ 307 AlreadyAuthorisedPanel() { 308 build(); 309 refreshView(); 310 } 311 } 312 313 /** 314 * Action to authorise the current user 315 */ 316 private class AuthoriseNowAction extends AbstractAction { 317 AuthoriseNowAction() { 318 putValue(NAME, tr("Authorize now")); 319 putValue(SHORT_DESCRIPTION, tr("Click to step through the OAuth authorization process")); 320 putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth-small")); 321 } 322 323 @Override 324 public void actionPerformed(ActionEvent arg0) { 325 OAuthAuthorizationWizard wizard = new OAuthAuthorizationWizard( 326 OAuthAuthenticationPreferencesPanel.this, 327 apiUrl 328 ); 329 wizard.setVisible(true); 330 if (wizard.isCanceled()) return; 331 OAuthAccessTokenHolder holder = OAuthAccessTokenHolder.getInstance(); 332 holder.setAccessToken(wizard.getAccessToken()); 333 holder.setSaveToPreferences(wizard.isSaveAccessTokenToPreferences()); 334 pnlAdvancedProperties.setAdvancedParameters(wizard.getOAuthParameters()); 335 refreshView(); 336 } 337 } 338 339 /** 340 * Launches the OAuthAuthorisationWizard to generate a new Access Token 341 */ 342 private class RenewAuthorisationAction extends AbstractAction { 343 /** 344 * Constructs a new {@code RenewAuthorisationAction}. 345 */ 346 RenewAuthorisationAction() { 347 putValue(NAME, tr("New Access Token")); 348 putValue(SHORT_DESCRIPTION, tr("Click to step through the OAuth authorization process and generate a new Access Token")); 349 putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth-small")); 350 351 } 352 353 @Override 354 public void actionPerformed(ActionEvent arg0) { 355 OAuthAuthorizationWizard wizard = new OAuthAuthorizationWizard( 356 OAuthAuthenticationPreferencesPanel.this, 357 apiUrl 358 ); 359 wizard.setVisible(true); 360 if (wizard.isCanceled()) return; 361 OAuthAccessTokenHolder holder = OAuthAccessTokenHolder.getInstance(); 362 holder.setAccessToken(wizard.getAccessToken()); 363 holder.setSaveToPreferences(wizard.isSaveAccessTokenToPreferences()); 364 pnlAdvancedProperties.setAdvancedParameters(wizard.getOAuthParameters()); 365 refreshView(); 366 } 367 } 368 369 /** 370 * Runs a test whether we can access the OSM server with the current Access Token 371 */ 372 private class TestAuthorisationAction extends AbstractAction { 373 /** 374 * Constructs a new {@code TestAuthorisationAction}. 375 */ 376 TestAuthorisationAction() { 377 putValue(NAME, tr("Test Access Token")); 378 putValue(SHORT_DESCRIPTION, tr("Click test access to the OSM server with the current access token")); 379 putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth-small")); 380 381 } 382 383 @Override 384 public void actionPerformed(ActionEvent evt) { 385 OAuthToken token = OAuthAccessTokenHolder.getInstance().getAccessToken(); 386 OAuthParameters parameters = OAuthParameters.createFromPreferences(Main.pref); 387 TestAccessTokenTask task = new TestAccessTokenTask( 388 OAuthAuthenticationPreferencesPanel.this, 389 apiUrl, 390 parameters, 391 token 392 ); 393 Main.worker.submit(task); 394 } 395 } 396 397 @Override 398 public void propertyChange(PropertyChangeEvent evt) { 399 if (!evt.getPropertyName().equals(OsmApiUrlInputPanel.API_URL_PROP)) 400 return; 401 setApiUrl((String) evt.getNewValue()); 402 } 403}