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