Skip to content

Commit 9bada60

Browse files
authored
Fix Connection initialization problems (#7)
* Fix pre-population of session ID and CSRF token in Connection * Identify target server with a `URI` instead of a `String`
1 parent bd124ce commit 9bada60

File tree

9 files changed

+130
-43
lines changed

9 files changed

+130
-43
lines changed

labkey-client-api/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# The LabKey Remote API Library for Java - Change Log
22

3+
## version 1.3.1 (TBD)
4+
*Released* : TBD
5+
6+
* Fix pre-population of session ID and CSRF token in Connection
7+
* Identify target server with a `URI` instead of a `String`
8+
39
## version 1.3.0
410
*Released* : 16 June 2020
511

labkey-client-api/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ buildDir = new File(project.rootProject.buildDir, "/remoteapi/labkey-api-java")
6060

6161
group "org.labkey.api"
6262

63-
version "1.4.0-SNAPSHOT"
63+
version "1.3.1-SNAPSHOT"
6464

6565
dependencies {
6666
api "org.apache.httpcomponents:httpmime:${httpmimeVersion}"

labkey-client-api/src/org/labkey/remoteapi/ApiKeyCredentialsProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import org.apache.http.client.methods.HttpUriRequest;
2020
import org.apache.http.client.protocol.HttpClientContext;
2121

22-
import java.net.URISyntaxException;
22+
import java.net.URI;
2323

2424
/**
2525
* Created by adam on 4/15/2016.
@@ -34,7 +34,7 @@ public ApiKeyCredentialsProvider(String apiKey)
3434
}
3535

3636
@Override
37-
public void configureRequest(String baseUrl, HttpUriRequest request, HttpClientContext httpClientContext) throws AuthenticationException, URISyntaxException
37+
public void configureRequest(URI baseURI, HttpUriRequest request, HttpClientContext httpClientContext) throws AuthenticationException
3838
{
3939
request.setHeader("apikey", _apiKey);
4040
}

labkey-client-api/src/org/labkey/remoteapi/BasicAuthCredentialsProvider.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import org.apache.http.impl.client.BasicCredentialsProvider;
2626

2727
import java.net.URI;
28-
import java.net.URISyntaxException;
2928

3029
/**
3130
* Created by adam on 4/15/2016.
@@ -42,9 +41,9 @@ public BasicAuthCredentialsProvider(String email, String password)
4241
}
4342

4443
@Override
45-
public void configureRequest(String baseUrl, HttpUriRequest request, HttpClientContext httpClientContext) throws AuthenticationException, URISyntaxException
44+
public void configureRequest(URI baseURI, HttpUriRequest request, HttpClientContext httpClientContext) throws AuthenticationException
4645
{
47-
AuthScope scope = new AuthScope(new URI(baseUrl).getHost(), AuthScope.ANY_PORT, AuthScope.ANY_REALM);
46+
AuthScope scope = new AuthScope(baseURI.getHost(), AuthScope.ANY_PORT, AuthScope.ANY_REALM);
4847
BasicCredentialsProvider provider = new BasicCredentialsProvider();
4948
Credentials credentials = new UsernamePasswordCredentials(_email, _password);
5049
provider.setCredentials(scope, credentials);

labkey-client-api/src/org/labkey/remoteapi/Command.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -489,9 +489,7 @@ protected HttpUriRequest createRequest(URI uri)
489489
*/
490490
protected URI getActionUrl(Connection connection, String folderPath) throws URISyntaxException
491491
{
492-
//start with the connection's base URL
493-
// Use a URI so that it correctly encodes each section of the overall URI
494-
URI uri = new URI(connection.getBaseUrl().replace('\\','/'));
492+
URI uri = connection.getBaseURI();
495493

496494
StringBuilder path = new StringBuilder(uri.getPath() == null || "".equals(uri.getPath()) ? "/" : uri.getPath());
497495

labkey-client-api/src/org/labkey/remoteapi/Connection.java

Lines changed: 100 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,19 @@
2020
import org.apache.http.auth.AuthenticationException;
2121
import org.apache.http.client.config.RequestConfig;
2222
import org.apache.http.client.methods.CloseableHttpResponse;
23-
import org.apache.http.client.methods.HttpPost;
2423
import org.apache.http.client.methods.HttpRequestBase;
2524
import org.apache.http.client.methods.HttpUriRequest;
2625
import org.apache.http.client.protocol.HttpClientContext;
2726
import org.apache.http.conn.HttpClientConnectionManager;
2827
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
29-
import org.apache.http.ssl.SSLContextBuilder;
3028
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
3129
import org.apache.http.cookie.Cookie;
3230
import org.apache.http.impl.client.BasicCookieStore;
3331
import org.apache.http.impl.client.CloseableHttpClient;
3432
import org.apache.http.impl.client.HttpClientBuilder;
3533
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
3634
import org.apache.http.impl.cookie.BasicClientCookie;
35+
import org.apache.http.ssl.SSLContextBuilder;
3736
import org.labkey.remoteapi.security.EnsureLoginCommand;
3837
import org.labkey.remoteapi.security.ImpersonateUserCommand;
3938
import org.labkey.remoteapi.security.LogoutCommand;
@@ -100,10 +99,13 @@
10099
*/
101100
public class Connection
102101
{
102+
public static final String X_LABKEY_CSRF = "X-LABKEY-CSRF";
103+
public static final String JSESSIONID = "JSESSIONID";
104+
103105
private static final int DEFAULT_TIMEOUT = 60000; // 60 seconds
104106
private static final HttpClientConnectionManager _connectionManager = new PoolingHttpClientConnectionManager();
105107

106-
private final String _baseUrl;
108+
private final URI _baseURI;
107109
private final CredentialsProvider _credentialsProvider;
108110
private final HttpClientContext _httpClientContext;
109111

@@ -113,6 +115,7 @@ public class Connection
113115
private String _proxyHost;
114116
private Integer _proxyPort;
115117
private String _csrf;
118+
private String _sessionId;
116119

117120
// The user email when impersonating a user
118121
private String _impersonateUser;
@@ -121,65 +124,100 @@ public class Connection
121124
/**
122125
* Constructs a new Connection object given a base URL and a credentials provider.
123126
* <p>
124-
* The baseUrl parameter should include the protocol, domain name, port,
127+
* The baseURI parameter should include the protocol, domain name, port,
125128
* and LabKey web application context path (if configured). For example
126129
* in a typical localhost configuration, the base URL would be:
127130
* </p>
128-
* <code>http://localhost:8080/labkey</code>
131+
* <code>new URI("http://localhost:8080/labkey")</code>
129132
* <p>
130133
* Note that https may also be used for the protocol. By default the
131134
* Connection is configured to deny self-signed SSL certificates.
132135
* If you want to accept self-signed certificates, use
133136
* <code>setAcceptSelfSignedCerts(false)</code> to enable this behavior.
134137
* </p>
135-
* The email name and password should correspond to a valid user email
136-
* and password on the target server.
137-
* @param baseUrl The base URL
138+
* @param baseURI Base URI for this Connection
138139
* @param credentialsProvider A credentials provider
139140
*/
140-
public Connection(String baseUrl, CredentialsProvider credentialsProvider)
141+
public Connection(URI baseURI, CredentialsProvider credentialsProvider)
141142
{
142-
_baseUrl = baseUrl;
143+
if (baseURI.getHost() == null || baseURI.getScheme() == null)
144+
{
145+
throw new IllegalArgumentException("Invalid server URL: " + baseURI.toString());
146+
}
147+
_baseURI = baseURI;
143148
_credentialsProvider = credentialsProvider;
144149
_httpClientContext = HttpClientContext.create();
145150
_httpClientContext.setCookieStore(new BasicCookieStore());
146151
setAcceptSelfSignedCerts(false);
147152
}
148153

154+
/**
155+
* Constructs a new Connection object given a base URL and a credentials provider.
156+
* <p>
157+
* The baseUrl parameter should include the protocol, domain name, port,
158+
* and LabKey web application context path (if configured). For example
159+
* in a typical localhost configuration, the base URL would be:
160+
* </p>
161+
* <code>http://localhost:8080/labkey</code>
162+
* <p>
163+
* @param baseUrl The base URL
164+
* @param credentialsProvider A credentials provider
165+
* @see #Connection(URI, CredentialsProvider)
166+
*/
167+
public Connection(String baseUrl, CredentialsProvider credentialsProvider)
168+
{
169+
this(toURI(baseUrl), credentialsProvider);
170+
}
171+
149172
/**
150173
* Constructs a new Connection object with a base URL that attempts authentication via .netrc/_netrc entry, if present.
151174
* If not present, connects as guest.
152175
* @param baseUrl The base URL
153176
* @throws URISyntaxException if the given url is not a valid URI
154177
* @throws IOException if there are problems reading the credentials
155-
* @see #Connection(String, CredentialsProvider)
178+
* @see NetrcCredentialsProvider
179+
* @see #Connection(URI, CredentialsProvider)
156180
*/
157181
public Connection(String baseUrl) throws URISyntaxException, IOException
158182
{
159-
this(baseUrl, new NetrcCredentialsProvider(new URI(baseUrl)));
183+
this(toURI(baseUrl), new NetrcCredentialsProvider(toURI(baseUrl)));
160184
}
161185

162186
/**
163187
* Constructs a new Connection object for a base URL that attempts basic authentication.
164188
* <p>
165189
* This is equivalent to calling <code>Connection(baseUrl, new BasicAuthCredentialsProvider(email, password))</code>.
190+
* The email and password should correspond to a valid user email
191+
* and password on the target server.
166192
* @param baseUrl The base URL
167193
* @param email The user email address to pass for authentication
168194
* @param password The user password to send for authentication
169-
* @see #Connection(String, CredentialsProvider)
195+
* @see BasicAuthCredentialsProvider#BasicAuthCredentialsProvider(String, String)
196+
* @see #Connection(URI, CredentialsProvider)
170197
*/
171198
public Connection(String baseUrl, String email, String password)
172199
{
173-
this(baseUrl, new BasicAuthCredentialsProvider(email, password));
200+
this(toURI(baseUrl), new BasicAuthCredentialsProvider(email, password));
174201
}
175202

176203
/**
177204
* Returns the base URL for this connection.
178205
* @return The base URL.
206+
* @deprecated Use {@link #getBaseURI()}
179207
*/
208+
@Deprecated
180209
public String getBaseUrl()
181210
{
182-
return _baseUrl;
211+
return _baseURI.toString();
212+
}
213+
214+
/**
215+
* Returns the base URI for this connection.
216+
* @return The target LabKey instance's base URI
217+
*/
218+
public URI getBaseURI()
219+
{
220+
return _baseURI;
183221
}
184222

185223
/**
@@ -228,11 +266,15 @@ protected HttpClientBuilder clientBuilder()
228266
return builder;
229267
}
230268

231-
232-
233-
protected void beforeExecute(HttpRequest request) throws IOException, CommandException
269+
/**
270+
* If necessary, prime the Connection for non-GET requests.
271+
* Executes a dummy command to trigger authentication and populate CSRF and session info.
272+
* @param request HttpRequest that is about to be executed
273+
*/
274+
protected void beforeExecute(HttpRequest request)
234275
{
235-
if (null == _csrf && request instanceof HttpPost)
276+
if (null == _csrf &&
277+
request instanceof HttpRequestBase && !"GET".equals(((HttpRequestBase) request).getMethod()))
236278
{
237279
// make a request to get a JSESSIONID
238280
try
@@ -244,7 +286,9 @@ protected void beforeExecute(HttpRequest request) throws IOException, CommandExc
244286
}
245287
}
246288
if (null != _csrf)
247-
request.setHeader("X-LABKEY-CSRF", _csrf);
289+
request.setHeader(X_LABKEY_CSRF, _csrf);
290+
if (null != _sessionId)
291+
request.setHeader(JSESSIONID, _sessionId);
248292
}
249293

250294

@@ -253,8 +297,11 @@ protected void afterExecute()
253297
// Always update our CSRF token as the session may be new since our last request
254298
for (Cookie c : _httpClientContext.getCookieStore().getCookies())
255299
{
256-
if ("JSESSIONID".equals(c.getName()))
300+
if (X_LABKEY_CSRF.equals(c.getName()))
257301
_csrf = c.getValue();
302+
303+
if (JSESSIONID.equals(c.getName()))
304+
_sessionId = c.getValue();
258305
}
259306
}
260307

@@ -354,10 +401,10 @@ public Connection stopImpersonate() throws IOException, CommandException
354401
return this;
355402
}
356403

357-
CloseableHttpResponse executeRequest(HttpUriRequest request, Integer timeout) throws IOException, URISyntaxException, AuthenticationException, CommandException
404+
CloseableHttpResponse executeRequest(HttpUriRequest request, Integer timeout) throws IOException, URISyntaxException, AuthenticationException
358405
{
359406
// Delegate authentication setup to CredentialsProvider
360-
_credentialsProvider.configureRequest(getBaseUrl(), request, _httpClientContext);
407+
_credentialsProvider.configureRequest(getBaseURI(), request, _httpClientContext);
361408

362409
CloseableHttpClient client = getHttpClient();
363410

@@ -462,7 +509,37 @@ public Connection addCookie(String name, String value, String domain, String pat
462509
cookie.setExpiryDate(expiry);
463510
cookie.setSecure(isSecure);
464511
_httpClientContext.getCookieStore().addCookie(cookie);
512+
513+
// Don't use session info from different server
514+
if (combineHostPath(_baseURI.getHost(), _baseURI.getPath()).equals(combineHostPath(domain, path)))
515+
{
516+
if (X_LABKEY_CSRF.equals(name))
517+
_csrf = value;
518+
if (JSESSIONID.equals(name))
519+
_sessionId = value;
520+
}
521+
465522
return this;
466523
}
467524

525+
private String combineHostPath(String host, String path)
526+
{
527+
String hostPath = (host == null ? "" : host.trim()) + (path == null ? "" : path.trim());
528+
return hostPath.replaceAll("//+", "/").replaceFirst("/$", "");
529+
}
530+
531+
/**
532+
* Utility method to construct a URI without modifying constructor signature
533+
*/
534+
private static URI toURI(String baseUrl)
535+
{
536+
try
537+
{
538+
return new URI(baseUrl);
539+
}
540+
catch (URISyntaxException e)
541+
{
542+
throw new IllegalArgumentException("Invalid target server URL: " + baseUrl);
543+
}
544+
}
468545
}

labkey-client-api/src/org/labkey/remoteapi/CredentialsProvider.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,22 @@
1919
import org.apache.http.client.methods.HttpUriRequest;
2020
import org.apache.http.client.protocol.HttpClientContext;
2121

22+
import java.net.URI;
2223
import java.net.URISyntaxException;
2324

2425
/**
2526
* Created by adam on 4/15/2016.
2627
*/
2728
public interface CredentialsProvider
2829
{
29-
void configureRequest(String baseUrl, HttpUriRequest request, HttpClientContext httpClientContext) throws AuthenticationException, URISyntaxException;
30+
/**
31+
* @deprecated Use {@link #configureRequest(URI, HttpUriRequest, HttpClientContext)}
32+
*/
33+
@Deprecated
34+
default void configureRequest(String baseUrl, HttpUriRequest request, HttpClientContext httpClientContext) throws AuthenticationException, URISyntaxException
35+
{
36+
configureRequest(new URI(baseUrl), request, httpClientContext);
37+
}
38+
39+
void configureRequest(URI baseURI, HttpUriRequest request, HttpClientContext httpClientContext) throws AuthenticationException;
3040
}

labkey-client-api/src/org/labkey/remoteapi/GuestCredentialsProvider.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@
1818
import org.apache.http.client.methods.HttpUriRequest;
1919
import org.apache.http.client.protocol.HttpClientContext;
2020

21-
/**
22-
* Created by adam on 4/15/2016.
23-
*/
21+
import java.net.URI;
2422

2523
/**
24+
* Created by adam on 4/15/2016.
25+
*
2626
* A credentials provider that provides no credentials. Connections using
2727
* this provider will be granted guest access only.
2828
*/
2929
public class GuestCredentialsProvider implements CredentialsProvider
3030
{
3131
@Override
32-
public void configureRequest(String baseUrl, HttpUriRequest request, HttpClientContext httpClientContext)
32+
public void configureRequest(URI baseURI, HttpUriRequest request, HttpClientContext httpClientContext)
3333
{
3434
httpClientContext.setCredentialsProvider(null);
3535
request.removeHeaders("Authenticate");

labkey-client-api/src/org/labkey/remoteapi/NetrcCredentialsProvider.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,11 @@
2121

2222
import java.io.IOException;
2323
import java.net.URI;
24-
import java.net.URISyntaxException;
2524

2625

2726
/**
2827
* Created by adam on 4/15/2016.
29-
*/
30-
31-
/**
28+
*
3229
* Attempts to find a .netrc or _netrc file and entry for the given host. If found, it will attempt basic auth using
3330
* the email and password in the entry. If file or entry are not found, it connects as guest.
3431
*/
@@ -65,8 +62,8 @@ public NetrcCredentialsProvider(String host) throws IOException
6562
}
6663

6764
@Override
68-
public void configureRequest(String baseUrl, HttpUriRequest request, HttpClientContext httpClientContext) throws AuthenticationException, URISyntaxException
65+
public void configureRequest(URI baseURI, HttpUriRequest request, HttpClientContext httpClientContext) throws AuthenticationException
6966
{
70-
_wrappedCredentialsProvider.configureRequest(baseUrl, request, httpClientContext);
67+
_wrappedCredentialsProvider.configureRequest(baseURI, request, httpClientContext);
7168
}
7269
}

0 commit comments

Comments
 (0)