Skip to content

Commit 14ca6d4

Browse files
authored
Switch first request to login-whoAmI.api (#50)
1 parent 82cfa00 commit 14ca6d4

11 files changed

+152
-68
lines changed

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
# The LabKey Remote API Library for Java - Change Log
22

3-
## version 5.1.0-SNAPSHOT
3+
## version 5.2.0-SNAPSHOT
44
*Released*: TBD
5+
6+
## version 5.1.0
7+
*Released*: 3 March 2023
8+
* Delegate first request behavior to the configured `CredentialsProvider`. Connection-based providers invoke
9+
`login-ensureLogin.api` and connection-less providers invoke `login-whoAmI.api`.
10+
* Restore connection-based authentication for `BasicCredentialsProvider`
11+
* Add logging to `NetrcFileParser` to help with debugging authentication problems
512
* Optional parameters to `GetContainersCommand` to reduce the size of the response payload
13+
* Upgrade to most recent JSON-java, Gradle, and Gradle Plugins versions
614

715
## version 5.0.1
816
*Released*: 30 January 2023

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ repositories {
4848

4949
group "org.labkey.api"
5050

51-
version "5.1.0-SNAPSHOT"
51+
version "5.2.0-SNAPSHOT"
5252

5353
dependencies {
5454
api "org.json:json:${jsonObjectVersion}"

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ artifactory_contextUrl=https://labkey.jfrog.io/artifactory
77
sourceCompatibility=17
88
targetCompatibility=17
99

10-
gradlePluginsVersion=1.39.1
10+
gradlePluginsVersion=1.40.1
1111

1212
commonsCodecVersion=1.15
1313
commonsLoggingVersion=1.2
@@ -17,7 +17,7 @@ hamcrestVersion=1.3
1717
httpclient5Version=5.2.1
1818
httpcore5Version=5.2.1
1919

20-
jsonObjectVersion=20220924
20+
jsonObjectVersion=20230227
2121

2222
junitVersion=4.13.2
2323

src/org/labkey/remoteapi/ApiKeyCredentialsProvider.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616
package org.labkey.remoteapi;
1717

1818
import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
19+
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
1920
import org.apache.hc.client5.http.protocol.HttpClientContext;
21+
import org.labkey.remoteapi.security.WhoAmICommand;
2022

23+
import java.io.IOException;
2124
import java.net.URI;
2225

2326
public class ApiKeyCredentialsProvider implements CredentialsProvider
@@ -29,9 +32,21 @@ public ApiKeyCredentialsProvider(String apiKey)
2932
_apiKey = apiKey;
3033
}
3134

35+
@Override
36+
public void configureClientBuilder(URI baseURI, HttpClientBuilder builder)
37+
{
38+
}
39+
3240
@Override
3341
public void configureRequest(URI baseURI, HttpUriRequest request, HttpClientContext httpClientContext)
3442
{
3543
request.setHeader("apikey", _apiKey);
3644
}
45+
46+
@Override
47+
public void initializeConnection(Connection connection) throws IOException, CommandException
48+
{
49+
// No point in calling ensureLogin.api since the server doesn't "log in" the session when using an API key
50+
new WhoAmICommand().execute(connection, "/home");
51+
}
3752
}

src/org/labkey/remoteapi/BasicAuthCredentialsProvider.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,16 @@
1717

1818
import org.apache.hc.client5.http.auth.AuthScope;
1919
import org.apache.hc.client5.http.auth.Credentials;
20+
import org.apache.hc.client5.http.auth.StandardAuthScheme;
2021
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
2122
import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
2223
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
24+
import org.apache.hc.client5.http.impl.auth.BasicScheme;
25+
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
2326
import org.apache.hc.client5.http.protocol.HttpClientContext;
27+
import org.labkey.remoteapi.security.EnsureLoginCommand;
2428

29+
import java.io.IOException;
2530
import java.net.URI;
2631

2732
public class BasicAuthCredentialsProvider implements CredentialsProvider
@@ -36,12 +41,34 @@ public BasicAuthCredentialsProvider(String email, String password)
3641
}
3742

3843
@Override
39-
public void configureRequest(URI baseURI, HttpUriRequest request, HttpClientContext httpClientContext)
44+
public void configureClientBuilder(URI baseURI, HttpClientBuilder builder)
4045
{
4146
BasicCredentialsProvider provider = new BasicCredentialsProvider();
4247
AuthScope scope = new AuthScope(baseURI.getHost(), baseURI.getPort());
4348
Credentials credentials = new UsernamePasswordCredentials(_email, _password.toCharArray());
4449
provider.setCredentials(scope, credentials);
45-
httpClientContext.setCredentialsProvider(provider);
50+
builder.setDefaultCredentialsProvider(provider);
51+
52+
// HttpClient doesn't provide a simple way to dictate connection-based Basic authentication, so jump through
53+
// some hoops: set a custom AuthSchemeRegistry that returns a customized version of BasicScheme.
54+
builder.setDefaultAuthSchemeRegistry(name -> !StandardAuthScheme.BASIC.equals(name) ? null : context -> new BasicScheme()
55+
{
56+
@Override
57+
public boolean isConnectionBased()
58+
{
59+
return true;
60+
}
61+
});
62+
}
63+
64+
@Override
65+
public void configureRequest(URI baseURI, HttpUriRequest request, HttpClientContext httpClientContext)
66+
{
67+
}
68+
69+
@Override
70+
public void initializeConnection(Connection connection) throws IOException, CommandException
71+
{
72+
new EnsureLoginCommand().execute(connection, "/home");
4673
}
4774
}

src/org/labkey/remoteapi/Connection.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import org.apache.hc.core5.http.HttpHost;
3535
import org.apache.hc.core5.http.HttpRequest;
3636
import org.apache.hc.core5.ssl.SSLContextBuilder;
37-
import org.labkey.remoteapi.security.EnsureLoginCommand;
3837
import org.labkey.remoteapi.security.ImpersonateUserCommand;
3938
import org.labkey.remoteapi.security.LogoutCommand;
4039
import org.labkey.remoteapi.security.StopImpersonatingCommand;
@@ -225,7 +224,7 @@ public CloseableHttpClient getHttpClient()
225224
}
226225

227226
/**
228-
* Create the HttpClientBuilder based on this Connection's configuration options.
227+
* Create the HttpClientBuilder based on this Connection's configuration options and the CredentialsProvider's wishes.
229228
* @return The builder for an HttpClient
230229
*/
231230
private HttpClientBuilder clientBuilder()
@@ -242,6 +241,8 @@ private HttpClientBuilder clientBuilder()
242241
if (null != _userAgent)
243242
builder.setUserAgent(_userAgent);
244243

244+
_credentialsProvider.configureClientBuilder(getBaseURI(), builder);
245+
245246
return builder;
246247
}
247248

@@ -280,12 +281,11 @@ protected void beforeExecute(HttpRequest request)
280281
{
281282
if (_firstRequest)
282283
{
283-
// Make an initial request to ensure login (especially important when invoking @RequiresNoPermission actions),
284-
// get a JSESSIONID, and get a CSRF token
285284
try
286285
{
287286
_firstRequest = false;
288-
ensureAuthenticated();
287+
// First request on this connection: delegate to CredentialsProvider for initialization appropriate to the provider
288+
_credentialsProvider.initializeConnection(this);
289289
}
290290
catch (Exception ignored)
291291
{
@@ -297,7 +297,6 @@ protected void beforeExecute(HttpRequest request)
297297
request.setHeader(JSESSIONID, _sessionId);
298298
}
299299

300-
301300
protected void afterExecute()
302301
{
303302
// Always update our CSRF token as the session may be new since our last request
@@ -311,15 +310,9 @@ protected void afterExecute()
311310
}
312311
}
313312

314-
/**
315-
* Ensures that the credentials have been used to authenticate the users and returns a client that can be used for other requests
316-
*
317-
* @throws IOException if there is an IO problem executing the command to ensure login
318-
* @throws CommandException if the server returned a non-success status code.
319-
*/
313+
@Deprecated // Not used. Left for backwards compatibility with AccountsManager.updateSiteExpirationBanner(). TODO: Delete!
320314
public void ensureAuthenticated() throws IOException, CommandException
321315
{
322-
new EnsureLoginCommand().execute(this, "/home");
323316
}
324317

325318
/**

src/org/labkey/remoteapi/CredentialsProvider.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,20 @@
1717

1818
import org.apache.hc.client5.http.auth.AuthenticationException;
1919
import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
20+
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
2021
import org.apache.hc.client5.http.protocol.HttpClientContext;
2122

23+
import java.io.IOException;
2224
import java.net.URI;
2325

2426
public interface CredentialsProvider
2527
{
28+
void configureClientBuilder(URI baseURI, HttpClientBuilder builder);
2629
void configureRequest(URI baseURI, HttpUriRequest request, HttpClientContext httpClientContext) throws AuthenticationException;
30+
31+
/**
32+
* Initialize the connection before its first request. If connection-based, authenticate the user. In all cases,
33+
* retrieve the CSRF token and session ID to use with subsequent requests on this connection.
34+
*/
35+
void initializeConnection(Connection connection) throws IOException, CommandException;
2736
}

src/org/labkey/remoteapi/GuestCredentialsProvider.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616
package org.labkey.remoteapi;
1717

1818
import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
19+
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
1920
import org.apache.hc.client5.http.protocol.HttpClientContext;
21+
import org.labkey.remoteapi.security.WhoAmICommand;
2022

23+
import java.io.IOException;
2124
import java.net.URI;
2225

2326
/**
@@ -26,10 +29,22 @@
2629
*/
2730
public class GuestCredentialsProvider implements CredentialsProvider
2831
{
32+
@Override
33+
public void configureClientBuilder(URI baseURI, HttpClientBuilder builder)
34+
{
35+
}
36+
2937
@Override
3038
public void configureRequest(URI baseURI, HttpUriRequest request, HttpClientContext httpClientContext)
3139
{
3240
httpClientContext.setCredentialsProvider(null);
3341
request.removeHeaders("Authenticate");
42+
request.removeHeaders("Authorization");
43+
}
44+
45+
@Override
46+
public void initializeConnection(Connection connection) throws IOException, CommandException
47+
{
48+
new WhoAmICommand().execute(connection, "/home");
3449
}
3550
}

src/org/labkey/remoteapi/NetrcCredentialsProvider.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import org.apache.hc.client5.http.auth.AuthenticationException;
1919
import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
20+
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
2021
import org.apache.hc.client5.http.protocol.HttpClientContext;
2122

2223
import java.io.IOException;
@@ -58,9 +59,21 @@ public NetrcCredentialsProvider(String host) throws IOException
5859
_wrappedCredentialsProvider = new GuestCredentialsProvider();
5960
}
6061

62+
@Override
63+
public void configureClientBuilder(URI baseURI, HttpClientBuilder builder)
64+
{
65+
_wrappedCredentialsProvider.configureClientBuilder(baseURI, builder);
66+
}
67+
6168
@Override
6269
public void configureRequest(URI baseURI, HttpUriRequest request, HttpClientContext httpClientContext) throws AuthenticationException
6370
{
6471
_wrappedCredentialsProvider.configureRequest(baseURI, request, httpClientContext);
6572
}
73+
74+
@Override
75+
public void initializeConnection(Connection connection) throws IOException, CommandException
76+
{
77+
_wrappedCredentialsProvider.initializeConnection(connection);
78+
}
6679
}

0 commit comments

Comments
 (0)