Changeset 18650 in josm for trunk/src/org/openstreetmap/josm/data/oauth
- Timestamp:
- 2023-02-08T18:31:58+01:00 (3 years ago)
- Location:
- trunk/src/org/openstreetmap/josm/data/oauth
- Files:
-
- 12 added
- 2 edited
-
IOAuthAuthorization.java (added)
-
IOAuthParameters.java (added)
-
IOAuthToken.java (added)
-
OAuth20Authorization.java (added)
-
OAuth20Exception.java (added)
-
OAuth20Parameters.java (added)
-
OAuth20Token.java (added)
-
OAuthAccessTokenHolder.java (modified) (5 diffs)
-
OAuthException.java (added)
-
OAuthParameters.java (modified) (7 diffs)
-
OAuthVersion.java (added)
-
osm (added)
-
osm/OsmScopes.java (added)
-
osm/package-info.java (added)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/oauth/OAuthAccessTokenHolder.java
r13173 r18650 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.net.URI; 7 import java.util.EnumMap; 8 import java.util.HashMap; 9 import java.util.Map; 10 import java.util.Objects; 11 import java.util.Optional; 12 6 13 import org.openstreetmap.josm.io.auth.CredentialsAgent; 7 14 import org.openstreetmap.josm.io.auth.CredentialsAgentException; 15 import org.openstreetmap.josm.io.auth.CredentialsManager; 8 16 import org.openstreetmap.josm.spi.preferences.Config; 9 17 import org.openstreetmap.josm.tools.CheckParameterUtil; … … 32 40 private String accessTokenSecret; 33 41 42 private final Map<String, Map<OAuthVersion, IOAuthToken>> tokenMap = new HashMap<>(); 43 34 44 /** 35 45 * Replies true if current access token should be saved to the preferences file. … … 101 111 return null; 102 112 return new OAuthToken(accessTokenKey, accessTokenSecret); 113 } 114 115 /** 116 * Replies the access token. 117 * @param api The api the token is for 118 * @param version The OAuth version the token is for 119 * @return the access token, can be {@code null} 120 * @since 18650 121 */ 122 public IOAuthToken getAccessToken(String api, OAuthVersion version) { 123 api = URI.create(api).getHost(); 124 if (this.tokenMap.containsKey(api)) { 125 Map<OAuthVersion, IOAuthToken> map = this.tokenMap.get(api); 126 return map.get(version); 127 } 128 try { 129 IOAuthToken token = CredentialsManager.getInstance().lookupOAuthAccessToken(api); 130 // We *do* want to set the API token to null, if it doesn't exist. Just to avoid unnecessary lookups. 131 this.setAccessToken(api, token); 132 return token; 133 } catch (CredentialsAgentException exception) { 134 Logging.trace(exception); 135 } 136 return null; 103 137 } 104 138 … … 126 160 this.accessTokenKey = token.getKey(); 127 161 this.accessTokenSecret = token.getSecret(); 162 } 163 } 164 165 /** 166 * Sets the access token hold by this holder. 167 * 168 * @param api The api the token is for 169 * @param token the access token. Can be null to clear the content in this holder. 170 * @since 18650 171 */ 172 public void setAccessToken(String api, IOAuthToken token) { 173 Objects.requireNonNull(api, "api url"); 174 // Sometimes the api might be sent as the host 175 api = Optional.ofNullable(URI.create(api).getHost()).orElse(api); 176 if (token == null) { 177 if (this.tokenMap.containsKey(api)) { 178 this.tokenMap.get(api).clear(); 179 } 180 } else { 181 this.tokenMap.computeIfAbsent(api, key -> new EnumMap<>(OAuthVersion.class)).put(token.getOAuthType(), token); 128 182 } 129 183 } … … 176 230 if (!saveToPreferences) { 177 231 cm.storeOAuthAccessToken(null); 232 for (String host : this.tokenMap.keySet()) { 233 cm.storeOAuthAccessToken(host, null); 234 } 178 235 } else { 179 cm.storeOAuthAccessToken(new OAuthToken(accessTokenKey, accessTokenSecret)); 236 if (this.accessTokenKey != null && this.accessTokenSecret != null) { 237 cm.storeOAuthAccessToken(new OAuthToken(accessTokenKey, accessTokenSecret)); 238 } 239 for (Map.Entry<String, Map<OAuthVersion, IOAuthToken>> entry : this.tokenMap.entrySet()) { 240 if (entry.getValue().isEmpty()) { 241 cm.storeOAuthAccessToken(entry.getKey(), null); 242 continue; 243 } 244 for (OAuthVersion version : OAuthVersion.values()) { 245 if (entry.getValue().containsKey(version)) { 246 cm.storeOAuthAccessToken(entry.getKey(), entry.getValue().get(version)); 247 } 248 } 249 } 180 250 } 181 251 } catch (CredentialsAgentException e) { -
trunk/src/org/openstreetmap/josm/data/oauth/OAuthParameters.java
r15009 r18650 2 2 package org.openstreetmap.josm.data.oauth; 3 3 4 import java.io.BufferedReader; 5 import java.io.IOException; 6 import java.net.URL; 4 7 import java.util.Objects; 5 8 9 import javax.json.Json; 10 import javax.json.JsonObject; 11 import javax.json.JsonReader; 12 import javax.json.JsonStructure; 13 import javax.json.JsonValue; 14 15 import org.openstreetmap.josm.io.OsmApi; 16 import org.openstreetmap.josm.io.auth.CredentialsAgentException; 17 import org.openstreetmap.josm.io.auth.CredentialsManager; 6 18 import org.openstreetmap.josm.spi.preferences.Config; 7 19 import org.openstreetmap.josm.spi.preferences.IUrls; 8 20 import org.openstreetmap.josm.tools.CheckParameterUtil; 21 import org.openstreetmap.josm.tools.HttpClient; 22 import org.openstreetmap.josm.tools.Logging; 9 23 import org.openstreetmap.josm.tools.Utils; 10 24 … … 16 30 * @since 2747 17 31 */ 18 public class OAuthParameters { 32 public class OAuthParameters implements IOAuthParameters { 19 33 20 34 /** … … 47 61 */ 48 62 public static OAuthParameters createDefault(String apiUrl) { 63 return (OAuthParameters) createDefault(apiUrl, OAuthVersion.OAuth10a); 64 } 65 66 /** 67 * Replies a set of default parameters for a consumer accessing an OSM server 68 * at the given API url. URL parameters are only set if the URL equals {@link IUrls#getDefaultOsmApiUrl} 69 * or references the domain "dev.openstreetmap.org", otherwise they may be <code>null</code>. 70 * 71 * @param apiUrl The API URL for which the OAuth default parameters are created. If null or empty, the default OSM API url is used. 72 * @param oAuthVersion The OAuth version to create default parameters for 73 * @return a set of default parameters for the given {@code apiUrl} 74 * @since 18650 75 */ 76 public static IOAuthParameters createDefault(String apiUrl, OAuthVersion oAuthVersion) { 77 if (!Utils.isValidUrl(apiUrl)) { 78 apiUrl = null; 79 } 80 81 switch (oAuthVersion) { 82 case OAuth10a: 83 return getDefaultOAuth10Parameters(apiUrl); 84 case OAuth20: 85 case OAuth21: // For now, OAuth 2.1 (draft) is just OAuth 2.0 with mandatory extensions, which we implement. 86 return getDefaultOAuth20Parameters(apiUrl); 87 default: 88 throw new IllegalArgumentException("Unknown OAuth version: " + oAuthVersion); 89 } 90 } 91 92 /** 93 * Get the default OAuth 2.0 parameters 94 * @param apiUrl The API url 95 * @return The default parameters 96 */ 97 private static OAuth20Parameters getDefaultOAuth20Parameters(String apiUrl) { 98 final String clientId; 99 final String clientSecret; 100 final String redirectUri; 101 final String baseUrl; 102 if (apiUrl != null && !Config.getUrls().getDefaultOsmApiUrl().equals(apiUrl)) { 103 clientId = ""; 104 clientSecret = ""; 105 baseUrl = apiUrl; 106 HttpClient client = null; 107 redirectUri = ""; 108 // Check if the server is RFC 8414 compliant 109 try { 110 client = HttpClient.create(new URL(apiUrl + (apiUrl.endsWith("/") ? "" : "/") + ".well-known/oauth-authorization-server")); 111 HttpClient.Response response = client.connect(); 112 if (response.getResponseCode() == 200) { 113 try (BufferedReader reader = response.getContentReader(); 114 JsonReader jsonReader = Json.createReader(reader)) { 115 JsonStructure structure = jsonReader.read(); 116 if (structure.getValueType() == JsonValue.ValueType.OBJECT) { 117 return parseAuthorizationServerMetadataResponse(clientId, clientSecret, apiUrl, 118 redirectUri, structure.asJsonObject()); 119 } 120 } 121 } 122 } catch (IOException | OAuthException e) { 123 Logging.trace(e); 124 } finally { 125 if (client != null) client.disconnect(); 126 } 127 } else { 128 clientId = "edPII614Lm0_0zEpc_QzEltA9BUll93-Y-ugRQUoHMI"; 129 // We don't actually use the client secret in our authorization flow. 130 clientSecret = null; 131 baseUrl = "https://www.openstreetmap.org/oauth2"; 132 redirectUri = "http://127.0.0.1:8111/oauth_authorization"; 133 apiUrl = OsmApi.getOsmApi().getBaseUrl(); 134 } 135 return new OAuth20Parameters(clientId, clientSecret, baseUrl, apiUrl, redirectUri); 136 } 137 138 /** 139 * Parse the response from <a href="https://www.rfc-editor.org/rfc/rfc8414.html">RFC 8414</a> 140 * (OAuth 2.0 Authorization Server Metadata) 141 * @return The parameters for the server metadata 142 */ 143 private static OAuth20Parameters parseAuthorizationServerMetadataResponse(String clientId, String clientSecret, 144 String apiUrl, String redirectUri, 145 JsonObject serverMetadata) 146 throws OAuthException { 147 final String authorizationEndpoint = serverMetadata.getString("authorization_endpoint", null); 148 final String tokenEndpoint = serverMetadata.getString("token_endpoint", null); 149 // This may also have additional documentation like what the endpoints allow (e.g. scopes, algorithms, etc.) 150 if (authorizationEndpoint == null || tokenEndpoint == null) { 151 throw new OAuth20Exception("Either token endpoint or authorization endpoints are missing"); 152 } 153 return new OAuth20Parameters(clientId, clientSecret, tokenEndpoint, authorizationEndpoint, apiUrl, redirectUri); 154 } 155 156 /** 157 * Get the default OAuth 1.0a parameters 158 * @param apiUrl The api url 159 * @return The default parameters 160 */ 161 private static OAuthParameters getDefaultOAuth10Parameters(String apiUrl) { 49 162 final String consumerKey; 50 163 final String consumerSecret; 51 164 final String serverUrl; 52 53 if (!Utils.isValidUrl(apiUrl)) {54 apiUrl = null;55 }56 165 57 166 if (apiUrl != null && !Config.getUrls().getDefaultOsmApiUrl().equals(apiUrl)) { … … 82 191 */ 83 192 public static OAuthParameters createFromApiUrl(String apiUrl) { 84 OAuthParameters parameters = createDefault(apiUrl); 85 return new OAuthParameters( 86 Config.getPref().get("oauth.settings.consumer-key", parameters.getConsumerKey()), 87 Config.getPref().get("oauth.settings.consumer-secret", parameters.getConsumerSecret()), 88 Config.getPref().get("oauth.settings.request-token-url", parameters.getRequestTokenUrl()), 89 Config.getPref().get("oauth.settings.access-token-url", parameters.getAccessTokenUrl()), 90 Config.getPref().get("oauth.settings.authorise-url", parameters.getAuthoriseUrl()), 91 Config.getPref().get("oauth.settings.osm-login-url", parameters.getOsmLoginUrl()), 92 Config.getPref().get("oauth.settings.osm-logout-url", parameters.getOsmLogoutUrl())); 193 return (OAuthParameters) createFromApiUrl(apiUrl, OAuthVersion.OAuth10a); 194 } 195 196 /** 197 * Replies a set of parameters as defined in the preferences. 198 * 199 * @param oAuthVersion The OAuth version to use. 200 * @param apiUrl the API URL. Must not be {@code null}. 201 * @return the parameters 202 * @since 18650 203 */ 204 public static IOAuthParameters createFromApiUrl(String apiUrl, OAuthVersion oAuthVersion) { 205 IOAuthParameters parameters = createDefault(apiUrl, oAuthVersion); 206 switch (oAuthVersion) { 207 case OAuth10a: 208 OAuthParameters oauth10aParameters = (OAuthParameters) parameters; 209 return new OAuthParameters( 210 Config.getPref().get("oauth.settings.consumer-key", oauth10aParameters.getConsumerKey()), 211 Config.getPref().get("oauth.settings.consumer-secret", oauth10aParameters.getConsumerSecret()), 212 Config.getPref().get("oauth.settings.request-token-url", oauth10aParameters.getRequestTokenUrl()), 213 Config.getPref().get("oauth.settings.access-token-url", oauth10aParameters.getAccessTokenUrl()), 214 Config.getPref().get("oauth.settings.authorise-url", oauth10aParameters.getAuthoriseUrl()), 215 Config.getPref().get("oauth.settings.osm-login-url", oauth10aParameters.getOsmLoginUrl()), 216 Config.getPref().get("oauth.settings.osm-logout-url", oauth10aParameters.getOsmLogoutUrl())); 217 case OAuth20: 218 case OAuth21: // Right now, OAuth 2.1 will work with our OAuth 2.0 implementation 219 OAuth20Parameters oAuth20Parameters = (OAuth20Parameters) parameters; 220 try { 221 IOAuthToken storedToken = CredentialsManager.getInstance().lookupOAuthAccessToken(apiUrl); 222 return storedToken != null ? storedToken.getParameters() : oAuth20Parameters; 223 } catch (CredentialsAgentException e) { 224 Logging.trace(e); 225 } 226 return oAuth20Parameters; 227 default: 228 throw new IllegalArgumentException("Unknown OAuth version: " + oAuthVersion); 229 } 93 230 } 94 231 … … 96 233 * Remembers the current values in the preferences. 97 234 */ 235 @Override 98 236 public void rememberPreferences() { 99 237 Config.getPref().put("oauth.settings.consumer-key", getConsumerKey()); … … 183 321 * @return The access token URL 184 322 */ 323 @Override 185 324 public String getAccessTokenUrl() { 186 325 return accessTokenUrl; 187 326 } 188 327 328 @Override 329 public String getAuthorizationUrl() { 330 return this.authoriseUrl; 331 } 332 333 @Override 334 public OAuthVersion getOAuthVersion() { 335 return OAuthVersion.OAuth10a; 336 } 337 338 @Override 339 public String getClientId() { 340 return this.consumerKey; 341 } 342 343 @Override 344 public String getClientSecret() { 345 return this.consumerSecret; 346 } 347 189 348 /** 190 349 * Gets the authorise URL. … … 192 351 */ 193 352 public String getAuthoriseUrl() { 194 return authoriseUrl;353 return this.getAuthorizationUrl(); 195 354 } 196 355
Note:
See TracChangeset
for help on using the changeset viewer.
