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