Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replace oauth 1.0a with oauth 2.0 #35

Merged
merged 4 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -213,7 +213,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 @@ -235,7 +235,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 @@ -249,7 +249,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 @@ -285,7 +285,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 @@ -382,7 +382,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