source: josm/trunk/src/org/openstreetmap/josm/gui/preferences/server/AuthenticationPreferencesPanel.java

Last change on this file was 18991, checked in by taylor.smock, 2 months ago

Fix #22810: OSM OAuth 1.0a/Basic auth deprecation and removal

As of 2024-02-15, something changed in the OSM server configuration. This broke
our OAuth 1.0a implementation (see #23475). As such, we are removing OAuth 1.0a
from JOSM now instead of when the OSM server removes support in June 2024.

For third-party OpenStreetMap servers, the Basic Authentication method has been
kept. However, they should be made aware that it may be removed if a non-trivial
bug occurs with it. We highly recommend that the third-party servers update to
the current OpenStreetMap website implementation (if only for their own security).

Failing that, the third-party server can implement RFC8414. As of this commit,
we currently use the authorization_endpoint and token_endpoint fields.
To check and see if their third-party server implements RFC8414, they can go
to <server host>/.well-known/oauth-authorization-server.

Prominent third-party OpenStreetMap servers may give us a client id for their
specific server. That client id may be added to the hard-coded client id list
at maintainer discretion. At a minimum, the server must be publicly
available and have a significant user base.

  • Property svn:eol-style set to native
File size: 8.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.preferences.server;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.FlowLayout;
8import java.awt.GridBagConstraints;
9import java.awt.GridBagLayout;
10import java.awt.Insets;
11import java.awt.event.ItemEvent;
12import java.awt.event.ItemListener;
13import java.beans.PropertyChangeEvent;
14import java.beans.PropertyChangeListener;
15
16import javax.swing.ButtonGroup;
17import javax.swing.JPanel;
18import javax.swing.JRadioButton;
19
20import org.openstreetmap.josm.actions.ExpertToggleAction;
21import org.openstreetmap.josm.data.UserIdentityManager;
22import org.openstreetmap.josm.data.oauth.OAuthAccessTokenHolder;
23import org.openstreetmap.josm.data.oauth.OAuthVersion;
24import org.openstreetmap.josm.data.preferences.JosmUrls;
25import org.openstreetmap.josm.gui.help.HelpUtil;
26import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
27import org.openstreetmap.josm.io.OsmApi;
28import org.openstreetmap.josm.io.auth.CredentialsManager;
29import org.openstreetmap.josm.spi.preferences.Config;
30import org.openstreetmap.josm.tools.GBC;
31import org.openstreetmap.josm.tools.Logging;
32
33/**
34 * This is the preference panel for the authentication method and the authentication parameters.
35 * @since 2745
36 */
37public class AuthenticationPreferencesPanel extends VerticallyScrollablePanel implements PropertyChangeListener {
38
39 /** indicates whether we use basic authentication */
40 private final JRadioButton rbBasicAuthentication = new JRadioButton();
41 /** indicates whether we use OAuth 2.0 as authentication scheme */
42 private final JRadioButton rbOAuth20 = new JRadioButton();
43 /** the panel which contains the authentication parameters for the respective authentication scheme */
44 private final JPanel pnlAuthenticationParameters = new JPanel(new BorderLayout());
45 /** the panel for the basic authentication parameters */
46 private BasicAuthenticationPreferencesPanel pnlBasicAuthPreferences;
47 /** the panel for the OAuth 2.0 authentication parameters */
48 private OAuthAuthenticationPreferencesPanel pnlOAuth20Preferences;
49
50 /** Used to determine which API we are using for disabling/enabling Basic Auth/OAuth 1.0a */
51 private String apiUrl = OsmApi.getOsmApi().getServerUrl();
52 /** ExpertToggleAction uses weak references; we don't want this listener to be garbage collected */
53 private final ExpertToggleAction.ExpertModeChangeListener expertModeChangeListener = isExpert -> {
54 final String authMethod = OsmApi.getAuthMethod();
55 final boolean defaultApi = JosmUrls.getInstance().getDefaultOsmApiUrl().equals(apiUrl);
56 rbBasicAuthentication.setEnabled(rbBasicAuthentication.isSelected() || "basic".equals(authMethod) || isExpert || !defaultApi);
57 };
58
59 /**
60 * Constructs a new {@code AuthenticationPreferencesPanel}.
61 */
62 public AuthenticationPreferencesPanel() {
63 build();
64 initFromPreferences();
65 HelpUtil.setHelpContext(this, HelpUtil.ht("/Preferences/Connection#AuthenticationSettings"));
66 }
67
68 /**
69 * builds the UI
70 */
71 protected final void build() {
72 setLayout(new GridBagLayout());
73
74 AuthenticationMethodChangeListener authChangeListener = new AuthenticationMethodChangeListener();
75
76 JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
77 // -- radio button for basic authentication
78 buttonPanel.add(rbBasicAuthentication);
79 rbBasicAuthentication.setText(tr("Use Basic Authentication"));
80 rbBasicAuthentication.setToolTipText(tr("Select to use HTTP basic authentication with your OSM username and password"));
81 rbBasicAuthentication.addItemListener(authChangeListener);
82 //-- radio button for OAuth 2.0
83 buttonPanel.add(rbOAuth20);
84 rbOAuth20.setSelected(true); // This must before adding the listener; otherwise, saveToPreferences is called prior to initFromPreferences
85 rbOAuth20.setText(tr("Use OAuth {0}", "2.0"));
86 rbOAuth20.setToolTipText(tr("Select to use OAuth {0} as authentication mechanism", "2.0"));
87 rbOAuth20.addItemListener(authChangeListener);
88
89 add(buttonPanel, GBC.eol());
90 //-- radio button for OAuth
91 ButtonGroup bg = new ButtonGroup();
92 bg.add(rbBasicAuthentication);
93 bg.add(rbOAuth20);
94
95 //-- add the panel which will hold the authentication parameters
96 GridBagConstraints gc = new GridBagConstraints();
97 gc.anchor = GridBagConstraints.NORTHWEST;
98 gc.insets = new Insets(0, 0, 0, 3);
99 gc.gridx = 0;
100 gc.gridy = 1;
101 gc.gridwidth = 2;
102 gc.fill = GridBagConstraints.BOTH;
103 gc.weightx = 1.0;
104 gc.weighty = 1.0;
105 add(pnlAuthenticationParameters, gc);
106
107 //-- the two panels for authentication parameters
108 pnlBasicAuthPreferences = new BasicAuthenticationPreferencesPanel();
109 pnlOAuth20Preferences = new OAuthAuthenticationPreferencesPanel(OAuthVersion.OAuth20);
110
111 ExpertToggleAction.addExpertModeChangeListener(expertModeChangeListener, true);
112
113 pnlAuthenticationParameters.add(pnlOAuth20Preferences, BorderLayout.CENTER);
114 }
115
116 /**
117 * Initializes the panel from preferences
118 */
119 public final void initFromPreferences() {
120 final String authMethod = OsmApi.getAuthMethod();
121 if ("basic".equals(authMethod)) {
122 rbBasicAuthentication.setSelected(true);
123 } else if ("oauth20".equals(authMethod)) {
124 rbOAuth20.setSelected(true);
125 } else {
126 Logging.warn(
127 tr("Unsupported value in preference ''{0}'', got ''{1}''. Using authentication method ''OAuth 2.0 Authentication''.",
128 "osm-server.auth-method", authMethod));
129 rbOAuth20.setSelected(true);
130 }
131 pnlBasicAuthPreferences.initFromPreferences();
132 pnlOAuth20Preferences.initFromPreferences();
133 }
134
135 /**
136 * Saves the current values to the preferences
137 */
138 public final void saveToPreferences() {
139 // save the authentication method
140 String authMethod;
141 if (rbBasicAuthentication.isSelected()) {
142 authMethod = "basic";
143 } else if (rbOAuth20.isSelected()) {
144 authMethod = "oauth20";
145 } else {
146 throw new IllegalStateException("One of OAuth 2.0, OAuth 1.0a, or Basic authentication must be checked");
147 }
148 final boolean initUser = Config.getPref().put("osm-server.auth-method", authMethod);
149 if ("basic".equals(authMethod)) {
150 // save username and password and clear the OAuth token
151 pnlBasicAuthPreferences.saveToPreferences();
152 OAuthAccessTokenHolder.getInstance().clear();
153 OAuthAccessTokenHolder.getInstance().save(CredentialsManager.getInstance());
154 } else if ("oauth20".equals(authMethod)) {
155 // oauth20
156 // clear the password in the preferences
157 pnlBasicAuthPreferences.clearPassword();
158 pnlOAuth20Preferences.saveToPreferences();
159 }
160 if (initUser) {
161 if ("basic".equals(authMethod)) {
162 UserIdentityManager.getInstance().initFromPreferences();
163 } else if (OsmApi.isUsingOAuthAndOAuthSetUp(OsmApi.getOsmApi())) {
164 UserIdentityManager.getInstance().initFromOAuth();
165 } else {
166 UserIdentityManager.getInstance().setAnonymous();
167 }
168 }
169 ExpertToggleAction.removeExpertModeChangeListener(this.expertModeChangeListener);
170 }
171
172 /**
173 * Listens to changes in the authentication method
174 */
175 class AuthenticationMethodChangeListener implements ItemListener {
176 @Override
177 public void itemStateChanged(ItemEvent e) {
178 pnlAuthenticationParameters.removeAll();
179 if (rbBasicAuthentication.isSelected()) {
180 pnlAuthenticationParameters.add(pnlBasicAuthPreferences, BorderLayout.CENTER);
181 pnlBasicAuthPreferences.revalidate();
182 } else if (rbOAuth20.isSelected()) {
183 pnlAuthenticationParameters.add(pnlOAuth20Preferences, BorderLayout.CENTER);
184 pnlOAuth20Preferences.saveToPreferences();
185 pnlOAuth20Preferences.initFromPreferences();
186 pnlOAuth20Preferences.revalidate();
187 }
188 repaint();
189 }
190 }
191
192 @Override
193 public void propertyChange(PropertyChangeEvent evt) {
194 if (pnlOAuth20Preferences != null) {
195 pnlOAuth20Preferences.propertyChange(evt);
196 }
197 if (OsmApiUrlInputPanel.API_URL_PROP.equals(evt.getPropertyName())) {
198 this.apiUrl = (String) evt.getNewValue();
199 this.expertModeChangeListener.expertChanged(ExpertToggleAction.isExpert());
200 }
201 }
202}
Note: See TracBrowser for help on using the repository browser.