Skip to content

Commit

Permalink
Merge pull request #35 from westnordost/oauth2
Browse files Browse the repository at this point in the history
replace oauth 1.0a with oauth 2.0
  • Loading branch information
westnordost authored Nov 24, 2023
2 parents 79cacb9 + 2361a80 commit 1618684
Show file tree
Hide file tree
Showing 13 changed files with 87 additions and 153 deletions.
51 changes: 19 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,47 +16,34 @@ Depending on which part of the API you use, you can only include what you need:

<table>
<tr><th>Class</th><th>Dependency</th><th>Description</th></tr>
<tr><td>CapabilitiesApi</td><td><pre>de.westnordost:osmapi-core:2.0</pre></td><td>Getting server capabilities</td></tr>
<tr><td>PermissionsApi</td><td><pre>de.westnordost:osmapi-core:2.0</pre></td><td>Getting user permissions</td></tr>
<tr><td>MapDataApi</td><td><pre>de.westnordost:osmapi-map:2.3</pre></td><td>Getting map data, querying single elements and their relations toward each other and uploading changes in changesets</td></tr>
<tr><td>MapDataHistoryApi</td><td><pre>de.westnordost:osmapi-map:2.3</pre></td><td>Getting the history and specific versions of elements</td></tr>
<tr><td>NotesApi</td><td><pre>de.westnordost:osmapi-notes:2.0</pre></td><td>Getting finding, creating, commenting on and solving notes</td></tr>
<tr><td>GpsTracesApi</td><td><pre>de.westnordost:osmapi-traces:2.0</pre></td><td>Getting, uploading, updating and deleting GPS traces and trackpoints</td></tr>
<tr><td>ChangesetsApi</td><td><pre>de.westnordost:osmapi-changesets:2.3</pre></td><td>Finding changesets, changeset discussion, subscription and data</td></tr>
<tr><td>UserApi</td><td><pre>de.westnordost:osmapi-user:2.0</pre></td><td>Getting user information</td></tr>
<tr><td>UserPreferencesApi</td><td><pre>de.westnordost:osmapi-user:2.0</pre></td><td>Managing user preferences</td></tr>
<tr><td>CapabilitiesApi</td><td><pre>de.westnordost:osmapi-core:3.0</pre></td><td>Getting server capabilities</td></tr>
<tr><td>PermissionsApi</td><td><pre>de.westnordost:osmapi-core:3.0</pre></td><td>Getting user permissions</td></tr>
<tr><td>MapDataApi</td><td><pre>de.westnordost:osmapi-map:3.0</pre></td><td>Getting map data, querying single elements and their relations toward each other and uploading changes in changesets</td></tr>
<tr><td>MapDataHistoryApi</td><td><pre>de.westnordost:osmapi-map:3.0</pre></td><td>Getting the history and specific versions of elements</td></tr>
<tr><td>NotesApi</td><td><pre>de.westnordost:osmapi-notes:3.0</pre></td><td>Getting finding, creating, commenting on and solving notes</td></tr>
<tr><td>GpsTracesApi</td><td><pre>de.westnordost:osmapi-traces:3.0</pre></td><td>Getting, uploading, updating and deleting GPS traces and trackpoints</td></tr>
<tr><td>ChangesetsApi</td><td><pre>de.westnordost:osmapi-changesets:3.0</pre></td><td>Finding changesets, changeset discussion, subscription and data</td></tr>
<tr><td>UserApi</td><td><pre>de.westnordost:osmapi-user:3.0</pre></td><td>Getting user information</td></tr>
<tr><td>UserPreferencesApi</td><td><pre>de.westnordost:osmapi-user:3.0</pre></td><td>Managing user preferences</td></tr>
</table>

To include everything, add [`de.westnordost:osmapi:4.3`](https://mvnrepository.com/artifact/de.westnordost/osmapi/4.0) as a Maven dependency or download the jar from there.
To include everything, add [`de.westnordost:osmapi:5.0`](https://mvnrepository.com/artifact/de.westnordost/osmapi/4.0) as a Maven dependency or download the jar from there.

### Android

On Android, you need to exclude kxml2 from the dependencies since it is already built-in, like so:

```gradle
dependencies {
implementation 'de.westnordost:osmapi:4.3'
}
On Android, you need to exclude kxml2 from the dependencies in your `gradle.kts` since it is already built-in, like so:

```kotlin
configurations {
// already included in Android
all*.exclude group: 'net.sf.kxml', module: 'kxml2'
// @NonNull etc annotations are also already included in Android
cleanedAnnotations
compile.exclude group: 'org.jetbrains', module:'annotations'
compile.exclude group: 'com.intellij', module:'annotations'
compile.exclude group: 'org.intellij', module:'annotations'
compile.exclude group: 'xmlpull', module:'xmlpull'
all {
// it's already included in Android
exclude(group = "net.sf.kxml", module = "kxml2")
exclude(group = "xmlpull", module = "xmlpull")
}
}
```

Also, starting with v4.0 (or v2.0 of the modularized version respectively), this library uses the classes from the Java 8 time API, like [`Instant`](https://developer.android.com/reference/java/time/Instant) etc. instead of `Date` which [leads to about 50% faster parsing times](https://github.com/streetcomplete/StreetComplete/discussions/2740) when receiving a result.

If your app supports Android API levels below 26, you have two options:

1. Either stick to using version 3.x (or v1.x of the modularized version respectively) of this library...
2. ...or enable [Java 8+ API desugaring support](https://developer.android.com/studio/write/java8-support#library-desugaring) for your app
This library uses classes from the Java 8 time API, like [`Instant`](https://developer.android.com/reference/java/time/Instant) etc., so if your app supports Android API levels below 26, you need to enable [Java 8+ API desugaring support](https://developer.android.com/studio/write/java8-support#library-desugaring).

## Basic Usage

Expand All @@ -70,7 +57,7 @@ If you plan to make calls that can only be made by a logged in user, such as upl
);
```

You can call osm.makeRequest(...) yourself to talk with the RESTful Api and write your own ApiRequestWriter and ApiResponseReader to write/read the request.
You can call `osm.makeRequest(...)` yourself to talk with the RESTful Api and write your own ApiRequestWriter and ApiResponseReader to write/read the request.
It is more convenient however to use the appropriate class to do that for you and return the data you are interested in. See the table above for which classes are available.

For example...
Expand Down
14 changes: 7 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ subprojects {
}

ext {
all_version = 4.3
core_version = 2.0
changesets_version = 2.3
user_version = 2.0
traces_version = 2.0
notes_version = 2.0
map_version = 2.3
all_version = 5.0
core_version = 3.0
changesets_version = 3.0
user_version = 3.0
traces_version = 3.0
notes_version = 3.0
map_version = 3.0
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public ChangesetInfo get(long id)
String query = CHANGESET + "/" + id + "?include_discussion=true";
try
{
boolean authenticate = osm.getOAuth() != null;
boolean authenticate = osm.getOAuthAccessToken() != null;
osm.makeRequest(query, authenticate, new ChangesetParser(handler));
}
catch(OsmNotFoundException e)
Expand All @@ -59,7 +59,7 @@ public void find(Handler<ChangesetInfo> handler, QueryChangesetsFilters filters)
String query = filters != null ? "?" + filters.toParamString() : "";
try
{
boolean authenticate = osm.getOAuth() != null;
boolean authenticate = osm.getOAuthAccessToken() != null;
osm.makeRequest(CHANGESET + "s" + query, authenticate, new ChangesetParser(handler));
}
catch(OsmNotFoundException e)
Expand Down Expand Up @@ -196,7 +196,7 @@ public void getData(long id, MapDataChangesHandler handler)
*/
public void getData(long id, MapDataChangesHandler handler, MapDataFactory factory)
{
boolean authenticate = osm.getOAuth() != null;
boolean authenticate = osm.getOAuthAccessToken() != null;
osm.makeRequest(CHANGESET + "/" + id + "/download", authenticate, new MapDataChangesParser(handler, factory));
}
}
1 change: 0 additions & 1 deletion libs/core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ version = core_version
description = 'Client for the OSM API 0.6 - Core functionality, getting server capabilities, getting user permissions'

dependencies {
compile 'oauth.signpost:signpost-core:2.1.1'
compile 'xmlpull:xmlpull:1.1.3.1'
runtime 'net.sf.kxml:kxml2:2.3.0'
}
64 changes: 16 additions & 48 deletions libs/core/src/main/java/de/westnordost/osmapi/OsmConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,7 @@
import java.net.URL;
import java.util.Locale;

import oauth.signpost.OAuthConsumer;
import oauth.signpost.basic.DefaultOAuthConsumer;
import oauth.signpost.exception.OAuthException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import de.westnordost.osmapi.common.errors.OsmApiReadResponseException;
import de.westnordost.osmapi.common.errors.OsmAuthorizationException;
import de.westnordost.osmapi.common.errors.OsmConnectionException;
import de.westnordost.osmapi.common.errors.RedirectedException;

Expand Down Expand Up @@ -54,36 +49,36 @@ public class OsmConnection
private int timeout;
private String apiUrl;
private String userAgent;

private OAuthConsumer oauth;
private String oauthAccessToken;

private final Object oauthLock = new Object();

/**
* Create a new OsmConnection with the given preferences
* @param apiUrl the URL to the API
* @param userAgent the user agent this application should identify as
* @param oauth oauth consumer to use to authenticate this app. If this is null, any attempt to
* make an API call that requires authorization will throw an OsmAuthorizationException
* @param oauthAccessToken OAuth 2.0 access token to use to authenticate this app. If this is null, any attempt
* to make an API call that requires authorization will throw an OsmAuthorizationException
* @param timeout for the server connection. Defaults to 45 seconds.
*/
public OsmConnection(String apiUrl, String userAgent, OAuthConsumer oauth, Integer timeout)
public OsmConnection(String apiUrl, String userAgent, String oauthAccessToken, Integer timeout)
{
this.apiUrl = apiUrl;
this.userAgent = userAgent;
this.oauth = oauth;
this.oauthAccessToken = oauthAccessToken;
this.timeout = timeout != null ? timeout : DEFAULT_TIMEOUT;
}

/**
* @see #OsmConnection(String, String, OAuthConsumer, Integer)
* @see #OsmConnection(String, String, String, Integer)
*/
public OsmConnection(String apiUrl, String userAgent, OAuthConsumer oauth)
public OsmConnection(String apiUrl, String userAgent, String oauth)
{
this(apiUrl, userAgent, oauth, null);
}

/**
* @see #OsmConnection(String, String, OAuthConsumer, Integer)
* @see #OsmConnection(String, String, String, Integer)
*/
public OsmConnection(String apiUrl, String userAgent)
{
Expand All @@ -95,11 +90,11 @@ public synchronized void setTimeout(int timeout)
this.timeout = timeout;
}

public void setOAuth(OAuthConsumer oauth)
public void setOAuthAccessToken(String oauthAccessToken)
{
synchronized(oauthLock)
{
this.oauth = oauth;
this.oauthAccessToken = oauthAccessToken;
}
}

Expand All @@ -123,11 +118,11 @@ public synchronized String getApiUrl()
return apiUrl;
}

public OAuthConsumer getOAuth()
public String getOAuthAccessToken()
{
synchronized(oauthLock)
{
return oauth;
return oauthAccessToken;
}
}

Expand Down Expand Up @@ -201,21 +196,14 @@ public <T> T makeRequest(String call, String method, boolean authenticate,
{
throw new OsmConnectionException(e);
}
catch(OAuthException e)
{
// because it was the user's fault that he did not supply an oauth consumer and the
// error is kinda related with the call he made
throw new OsmAuthorizationException(e);
}
finally
{
if(connection != null) connection.disconnect();
}
}

private HttpURLConnection sendRequest(
String call, String method, boolean authenticate, ApiRequestWriter writer)
throws IOException, OAuthException
private HttpURLConnection sendRequest(String call, String method, boolean authenticate, ApiRequestWriter writer)
throws IOException
{
HttpURLConnection connection = openConnection(call);
if(method != null)
Expand All @@ -231,7 +219,7 @@ private HttpURLConnection sendRequest(

if(authenticate)
{
createOAuthConsumer().sign(connection);
connection.setRequestProperty("Authorization", "Bearer " + oauthAccessToken);
}

if(writer != null)
Expand Down Expand Up @@ -290,26 +278,6 @@ private synchronized HttpURLConnection openConnection(String call) throws IOExce
return connection;
}

private OAuthConsumer createOAuthConsumer() throws OAuthExpectationFailedException
{
synchronized(oauthLock)
{
if(oauth == null)
{
throw new OAuthExpectationFailedException(
"This class has been initialized without a OAuthConsumer. Only API calls " +
"that do not require authentication can be made.");
}

// "clone" the original consumer every time because the consumer is documented to be not
// thread safe and maybe multiple threads are making calls to this class
OAuthConsumer consumer = new DefaultOAuthConsumer(
oauth.getConsumerKey(), oauth.getConsumerSecret());
consumer.setTokenWithSecret(oauth.getToken(), oauth.getTokenSecret());
return consumer;
}
}

private <T> T handleResponse(HttpURLConnection connection, ApiResponseReader<T> reader)
throws IOException
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class OsmConnectionTest
try
{
OsmConnection osm = ConnectionTestFactory.createConnection(null);
osm.makeAuthenticatedRequest("doesntMatter", "GET");
osm.makeAuthenticatedRequest("changeset/create", "PUT", null, null);
fail();
}
catch(OsmAuthorizationException ignore) {}
Expand Down
10 changes: 5 additions & 5 deletions libs/map/src/main/java/de/westnordost/osmapi/map/MapDataApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public void getMap(BoundingBox bounds, MapDataHandler handler)
}

String request = "map?bbox=" + bounds.getAsLeftBottomRightTopString();
boolean authenticate = osm.getOAuth() != null;
boolean authenticate = osm.getOAuthAccessToken() != null;

try
{
Expand All @@ -236,7 +236,7 @@ public void getMap(BoundingBox bounds, MapDataHandler handler)
* @throws OsmNotFoundException if the way with the given id does not exist */
public void getWayComplete(long id, MapDataHandler handler)
{
boolean authenticate = osm.getOAuth() != null;
boolean authenticate = osm.getOAuthAccessToken() != null;
osm.makeRequest(WAY + "/" + id + "/" + FULL, authenticate, new MapDataParser(handler, factory));
}

Expand All @@ -250,7 +250,7 @@ public void getWayComplete(long id, MapDataHandler handler)
* @throws OsmNotFoundException if the relation with the given id does not exist*/
public void getRelationComplete(long id, MapDataHandler handler)
{
boolean authenticate = osm.getOAuth() != null;
boolean authenticate = osm.getOAuthAccessToken() != null;
osm.makeRequest(RELATION + "/" + id + "/" + FULL, authenticate, new MapDataParser(handler, factory));
}

Expand Down Expand Up @@ -286,7 +286,7 @@ private <T extends Element> T getOneElement(String call, Class<T> tClass)
SingleOsmElementHandler<T> handler = new SingleOsmElementHandler<>(tClass);
try
{
boolean authenticate = osm.getOAuth() != null;
boolean authenticate = osm.getOAuthAccessToken() != null;
osm.makeRequest(call, authenticate, new MapDataParser(handler, factory));
}
catch(OsmNotFoundException e)
Expand Down Expand Up @@ -383,7 +383,7 @@ private static String toCommaList(Iterable<Long> vals)
private <T extends Element> List<T> getSomeElements(String call, Class<T> tClass)
{
ListOsmElementHandler<T> handler = new ListOsmElementHandler<>(tClass);
boolean authenticate = osm.getOAuth() != null;
boolean authenticate = osm.getOAuthAccessToken() != null;
osm.makeRequest(call, authenticate, new MapDataParser(handler, factory));
return handler.get();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public MapDataHistoryApi(OsmConnection osm)
public void getNodeHistory(long id, Handler<Node> handler)
{
MapDataHandler mapDataHandler = new WrapperOsmElementHandler<>(Node.class, handler);
boolean authenticate = osm.getOAuth() != null;
boolean authenticate = osm.getOAuthAccessToken() != null;
osm.makeRequest(NODE + "/" + id + "/" + HISTORY, authenticate,
new MapDataParser(mapDataHandler, factory));
}
Expand All @@ -65,7 +65,7 @@ public void getNodeHistory(long id, Handler<Node> handler)
public void getWayHistory(long id, Handler<Way> handler)
{
MapDataHandler mapDataHandler = new WrapperOsmElementHandler<>(Way.class, handler);
boolean authenticate = osm.getOAuth() != null;
boolean authenticate = osm.getOAuthAccessToken() != null;
osm.makeRequest(WAY + "/" + id + "/" + HISTORY, authenticate,
new MapDataParser(mapDataHandler, factory));
}
Expand All @@ -81,7 +81,7 @@ public void getWayHistory(long id, Handler<Way> handler)
public void getRelationHistory(long id, Handler<Relation> handler)
{
MapDataHandler mapDataHandler = new WrapperOsmElementHandler<>(Relation.class, handler);
boolean authenticate = osm.getOAuth() != null;
boolean authenticate = osm.getOAuthAccessToken() != null;
osm.makeRequest(RELATION + "/" + id + "/" + HISTORY, authenticate,
new MapDataParser(mapDataHandler, factory));
}
Expand Down Expand Up @@ -127,7 +127,7 @@ private <T extends Element> T getElementVersion(String call, Class<T> tClass)
SingleOsmElementHandler<T> handler = new SingleOsmElementHandler<>(tClass);
try
{
boolean authenticate = osm.getOAuth() != null;
boolean authenticate = osm.getOAuthAccessToken() != null;
osm.makeRequest(call, authenticate, new MapDataParser(handler, factory));
}
catch(OsmApiException e)
Expand Down
Loading

0 comments on commit 1618684

Please sign in to comment.