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.io.OsmApi; 033import org.openstreetmap.josm.io.auth.CredentialsManager; 034import org.openstreetmap.josm.tools.ImageProvider; 035import org.openstreetmap.josm.gui.widgets.JMultilineLabel; 036import org.openstreetmap.josm.gui.widgets.JosmTextField; 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 protected void build() { 183 setLayout(new GridBagLayout()); 184 GridBagConstraints gc = new GridBagConstraints(); 185 186 // A message explaining that the user isn't authorised yet 187 gc.anchor = GridBagConstraints.NORTHWEST; 188 gc.insets = new Insets(0,0,3,0); 189 gc.fill = GridBagConstraints.HORIZONTAL; 190 gc.weightx = 1.0; 191 JMultilineLabel lbl; 192 add(lbl = new JMultilineLabel(tr("You do not have an Access Token yet to access the OSM server using OAuth. Please authorize first.")), gc); 193 lbl.setFont(lbl.getFont().deriveFont(Font.PLAIN)); 194 195 // Action for authorising now 196 gc.gridy = 1; 197 gc.fill = GridBagConstraints.NONE; 198 gc.weightx = 0.0; 199 add(new SideButton(new AuthoriseNowAction()), gc); 200 201 // filler - grab remaining space 202 gc.gridy = 2; 203 gc.fill = GridBagConstraints.BOTH; 204 gc.weightx = 1.0; 205 gc.weighty = 1.0; 206 add(new JPanel(), gc); 207 } 208 209 public NotYetAuthorisedPanel() { 210 build(); 211 } 212 } 213 214 /** 215 * The preferences panel displayed if there is currently an AccessToken available. 216 * 217 */ 218 private class AlreadyAuthorisedPanel extends JPanel { 219 private JosmTextField tfAccessTokenKey; 220 private JosmTextField tfAccessTokenSecret; 221 222 protected void build() { 223 setLayout(new GridBagLayout()); 224 GridBagConstraints gc = new GridBagConstraints(); 225 gc.anchor = GridBagConstraints.NORTHWEST; 226 gc.insets = new Insets(0,0,3,3); 227 gc.fill = GridBagConstraints.HORIZONTAL; 228 gc.weightx = 1.0; 229 gc.gridwidth = 2; 230 JMultilineLabel lbl; 231 add(lbl = new JMultilineLabel(tr("You already have an Access Token to access the OSM server using OAuth.")), gc); 232 lbl.setFont(lbl.getFont().deriveFont(Font.PLAIN)); 233 234 // -- access token key 235 gc.gridy = 1; 236 gc.gridx = 0; 237 gc.gridwidth = 1; 238 gc.weightx = 0.0; 239 add(new JLabel(tr("Access Token Key:")), gc); 240 241 gc.gridx = 1; 242 gc.weightx = 1.0; 243 add(tfAccessTokenKey = new JosmTextField(), gc); 244 tfAccessTokenKey.setEditable(false); 245 246 // -- access token secret 247 gc.gridy = 2; 248 gc.gridx = 0; 249 gc.gridwidth = 1; 250 gc.weightx = 0.0; 251 add(new JLabel(tr("Access Token Secret:")), gc); 252 253 gc.gridx = 1; 254 gc.weightx = 1.0; 255 add(tfAccessTokenSecret = new JosmTextField(), gc); 256 tfAccessTokenSecret.setEditable(false); 257 258 // -- access token secret 259 gc.gridy = 3; 260 gc.gridx = 0; 261 gc.gridwidth = 2; 262 gc.weightx = 1.0; 263 add(cbSaveToPreferences = new JCheckBox(tr("Save to preferences")), gc); 264 cbSaveToPreferences.setSelected(OAuthAccessTokenHolder.getInstance().isSaveToPreferences()); 265 266 // -- action buttons 267 JPanel btns = new JPanel(new FlowLayout(FlowLayout.LEFT)); 268 btns.add(new SideButton(new RenewAuthorisationAction())); 269 btns.add(new SideButton(new TestAuthorisationAction())); 270 gc.gridy = 4; 271 gc.gridx = 0; 272 gc.gridwidth = 2; 273 gc.weightx = 1.0; 274 add(btns, gc); 275 276 // the panel with the advanced options 277 gc.gridy = 5; 278 gc.gridx = 0; 279 gc.gridwidth = 2; 280 gc.weightx = 1.0; 281 add(buildAdvancedPropertiesPanel(), gc); 282 283 // filler - grab the remaining space 284 gc.gridy = 6; 285 gc.fill = GridBagConstraints.BOTH; 286 gc.weightx = 1.0; 287 gc.weighty = 1.0; 288 add(new JPanel(), gc); 289 290 } 291 292 public final void refreshView() { 293 String v = OAuthAccessTokenHolder.getInstance().getAccessTokenKey(); 294 tfAccessTokenKey.setText(v == null? "" : v); 295 v = OAuthAccessTokenHolder.getInstance().getAccessTokenSecret(); 296 tfAccessTokenSecret.setText(v == null? "" : v); 297 cbSaveToPreferences.setSelected(OAuthAccessTokenHolder.getInstance().isSaveToPreferences()); 298 } 299 300 public AlreadyAuthorisedPanel() { 301 build(); 302 refreshView(); 303 } 304 } 305 306 /** 307 * Action to authorise the current user 308 */ 309 private class AuthoriseNowAction extends AbstractAction { 310 public AuthoriseNowAction() { 311 putValue(NAME, tr("Authorize now")); 312 putValue(SHORT_DESCRIPTION, tr("Click to step through the OAuth authorization process")); 313 putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth")); 314 315 } 316 @Override 317 public void actionPerformed(ActionEvent arg0) { 318 OAuthAuthorizationWizard wizard = new OAuthAuthorizationWizard( 319 OAuthAuthenticationPreferencesPanel.this, 320 apiUrl 321 ); 322 wizard.setVisible(true); 323 if (wizard.isCanceled()) return; 324 OAuthAccessTokenHolder holder = OAuthAccessTokenHolder.getInstance(); 325 holder.setAccessToken(wizard.getAccessToken()); 326 holder.setSaveToPreferences(wizard.isSaveAccessTokenToPreferences()); 327 pnlAdvancedProperties.setAdvancedParameters(wizard.getOAuthParameters()); 328 refreshView(); 329 } 330 } 331 332 /** 333 * Launches the OAuthAuthorisationWizard to generate a new Access Token 334 */ 335 private class RenewAuthorisationAction extends AbstractAction { 336 public RenewAuthorisationAction() { 337 putValue(NAME, tr("New Access Token")); 338 putValue(SHORT_DESCRIPTION, tr("Click to step through the OAuth authorization process and generate a new Access Token")); 339 putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth")); 340 341 } 342 @Override 343 public void actionPerformed(ActionEvent arg0) { 344 OAuthAuthorizationWizard wizard = new OAuthAuthorizationWizard( 345 OAuthAuthenticationPreferencesPanel.this, 346 apiUrl 347 ); 348 wizard.setVisible(true); 349 if (wizard.isCanceled()) return; 350 OAuthAccessTokenHolder holder = OAuthAccessTokenHolder.getInstance(); 351 holder.setAccessToken(wizard.getAccessToken()); 352 holder.setSaveToPreferences(wizard.isSaveAccessTokenToPreferences()); 353 pnlAdvancedProperties.setAdvancedParameters(wizard.getOAuthParameters()); 354 refreshView(); 355 } 356 } 357 358 /** 359 * Runs a test whether we can access the OSM server with the current Access Token 360 */ 361 private class TestAuthorisationAction extends AbstractAction { 362 public TestAuthorisationAction() { 363 putValue(NAME, tr("Test Access Token")); 364 putValue(SHORT_DESCRIPTION, tr("Click test access to the OSM server with the current access token")); 365 putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth")); 366 367 } 368 369 @Override 370 public void actionPerformed(ActionEvent evt) { 371 OAuthToken token = OAuthAccessTokenHolder.getInstance().getAccessToken(); 372 OAuthParameters parameters = OAuthParameters.createFromPreferences(Main.pref); 373 TestAccessTokenTask task = new TestAccessTokenTask( 374 OAuthAuthenticationPreferencesPanel.this, 375 apiUrl, 376 parameters, 377 token 378 ); 379 Main.worker.submit(task); 380 } 381 } 382 383 @Override 384 public void propertyChange(PropertyChangeEvent evt) { 385 if (! evt.getPropertyName().equals(OsmApiUrlInputPanel.API_URL_PROP)) 386 return; 387 setApiUrl((String)evt.getNewValue()); 388 } 389}