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