Skip to content

Commit 2656698

Browse files
antususbszwarc
andauthored
feat: Using OkHttp in Java SDK (#1083)
Co-authored-by: Barbara Czyż <barbara.m.szwarc@gmail.com>
1 parent a15edbf commit 2656698

File tree

144 files changed

+4216
-5939
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

144 files changed

+4216
-5939
lines changed

.github/workflows/build-main.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ name: build-main
22
on:
33
pull_request:
44
types: [ opened, synchronize ]
5-
branches:
6-
- main
75
push:
86
branches:
97
- main
@@ -29,4 +27,4 @@ jobs:
2927
- name: Coverage
3028
env:
3129
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32-
run: ./gradlew jacocoTestReport coverallsJacoco
30+
run: ./gradlew jacocoTestReport coverallsJacoco

README.md

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ The Box Java SDK for interacting with the
1111
## Latest Release
1212
Latest release can be found [here](https://github.com/box/box-java-sdk/tree/v3.8.2).
1313

14+
## Upgrades
15+
You can read about how to migrate to the new version [here](https://github.com/box/box-java-sdk/tree/v3.8.0/doc/upgrades).
16+
1417
## Versions
1518
We use a modified version of [Semantic Versioning](https://semver.org/) for all changes. See [version strategy](VERSIONS.md) for details which is effective from 30 July 2022.
1619

@@ -56,6 +59,14 @@ If you are developing application for Android visit our [Android guide](doc/andr
5659
If you don't install this, you'll get an exception about key length or exception about parsing PKCS private key for Box Developer Edition. This is not a Box thing, this is a U.S. Government requirement concerning strong encryption.
5760
The listed jar is for Oracle JRE. There might be other similar JARs for different JRE versions like the one below for IBM JDK
5861
[Java Cryptography Extension for IBM JDK](https://www14.software.ibm.com/webapp/iwm/web/preLogin.do?source=jcesdk)
62+
6. [okhttp v4.10.0](https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp/4.10.0)
63+
Maven: `com.squareup.okhttp3:okhttp:4.10.0`
64+
7. [okio-jvm v3.2.0](https://mvnrepository.com/artifact/com.squareup.okio/okio-jvm/3.2.0)
65+
Maven: `com.squareup.okio:okio-jvm:3.2.0`
66+
8. [kotlin-stdlib v1.6.20](https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib/1.6.20)
67+
Maven: `org.jetbrains.kotlin:kotlin-stdlib:1.6.20`
68+
9. [kotlin-stdlib-common v1.6.20](https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib-common/1.6.20)
69+
Maven: `org.jetbrains.kotlin:kotlin-stdlib-common:1.6.20`
5970

6071
An app has to be authorized by the admin of the enterprise before these tests. It's always good to begin with the
6172
[Getting Started Section](https://developer.box.com/docs/setting-up-a-jwt-app) at Box's developer website.
@@ -161,16 +172,21 @@ To run the project, follow below steps
161172

162173
3. Provide the Id of the admin user (or any enterprise user) in `src/example/java/com/box/sdk/example/BoxDeveloperEditionAPIConnectionAsEnterpriseUser.java`.
163174

164-
```java
175+
```java
165176
public final class BoxDeveloperEditionAPIConnectionAsEnterpriseUser {
166177

167178
private static final String USER_ID = "";
168179
// ...
169180
Reader reader = new FileReader("src/example/config/config.json");
170181
BoxConfig boxConfig = BoxConfig.readFrom(reader);
171-
172-
BoxDeveloperEditionAPIConnection api =
173-
new BoxDeveloperEditionAPIConnection(USER_ID, DeveloperEditionEntityType.USER, boxConfig, accessTokenCache);
182+
IAccessTokenCache accessTokenCache = new InMemoryLRUAccessTokenCache(10);
183+
184+
BoxDeveloperEditionAPIConnection api = new BoxDeveloperEditionAPIConnection(
185+
USER_ID,
186+
DeveloperEditionEntityType.USER,
187+
boxConfig,
188+
accessTokenCache
189+
);
174190
}
175191
```
176192

build.gradle

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ dependencies {
6363
}
6464
because "v1.57 is compatible with org.bouncycastle:bc-fips:1.0.2.1 which is needed for FIPS compliance purposes"
6565
}
66+
implementation "com.squareup.okhttp3:okhttp:4.10.0"
6667
testsCommonImplementation "junit:junit:4.13.2"
6768
testsCommonImplementation "org.hamcrest:hamcrest-library:2.2"
6869
testsCommonImplementation "org.mockito:mockito-core:4.8.0"
@@ -117,16 +118,6 @@ task integrationTest(type: Test) {
117118
classpath = sourceSets.intTest.runtimeClasspath
118119
}
119120

120-
//TODO: enable JWT tests
121-
//task integrationTestJWT(type: Test) {
122-
// description "Runs the JWT based integration tests."
123-
// group "Verification"
124-
// testLogging.showStandardStreams = true
125-
// useJUnit {
126-
// includeCategories "com.box.sdk.IntegrationTestJWT"
127-
// }
128-
//}
129-
130121
jacoco {
131122
reportsDirectory = file("$buildDir/reports/jacoco")
132123
}

config/checkstyle/checkstyle.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@
142142
<!-- Miscellaneous other checks. -->
143143
<!-- See http://checkstyle.sf.net/config_misc.html -->
144144
<module name="ArrayTypeStyle"/>
145-
<module name="TodoComment"/>
145+
<!-- <module name="TodoComment"/>-->
146146
<module name="UpperEll"/>
147147

148148
<module name="ParameterAssignment"/>

doc/authentication.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,18 @@ object has been created you can use that to create an API connection.
6060
```java
6161
Reader reader = new FileReader("src/example/config/config.json");
6262
BoxConfig boxConfig = BoxConfig.readFrom(reader);
63-
64-
BoxDeveloperEditionAPIConnection api = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig);
63+
IAccessTokenCache tokenCache = new InMemoryLRUAccessTokenCache(100);
64+
BoxDeveloperEditionAPIConnection api = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig, tokenCache);
6565
```
6666

67-
It is also possible to get an API connection for an app user or managed user by doing something like this:
67+
It is also possible to get an API connection for an app user or managed user:
6868

6969
<!-- sample x_auth init_with_jwt_with_user_id -->
7070
```java
7171
Reader reader = new FileReader("src/example/config/config.json");
7272
BoxConfig boxConfig = BoxConfig.readFrom(reader);
7373

74-
InMemoryLRUAccessTokenCache accessTokenCache = new InMemoryLRUAccessTokenCache(100);
74+
IAccessTokenCache accessTokenCache = new InMemoryLRUAccessTokenCache(100);
7575
BoxDeveloperEditionAPIConnection api = BoxDeveloperEditionAPIConnection.getUserConnection(userId, boxConfig, accessTokenCache);
7676
```
7777

@@ -85,7 +85,7 @@ jwtPreferences.setPrivateKeyPassword("PRIVATE-KEY-PASSWORD");
8585
jwtPreferences.setPrivateKey("PRIVATE-KEY");
8686
jwtPreferences.setEncryptionAlgorithm(EncryptionAlgorithm.RSA_SHA_256);
8787

88-
InMemoryLRUAccessTokenCache accessTokenCache = new InMemoryLRUAccessTokenCache(100);
88+
IAccessTokenCache accessTokenCache = new InMemoryLRUAccessTokenCache(100);
8989
BoxDeveloperEditionAPIConnection api = BoxDeveloperEditionAPIConnection
9090
.getUserConnection("USER-ID", "CLIENT-ID","CLIENT-SECRET", jwtPreferences, accessTokenCache);
9191

@@ -108,8 +108,9 @@ jwtPreferences.setPrivateKey("PRIVATE-KEY");
108108
jwtPreferences.setEncryptionAlgorithm(EncryptionAlgorithm.RSA_SHA_256);
109109

110110
BoxConfig boxConfig = new BoxConfig("YOUR-CLIENT-ID", "YOUR-CLIENT-SECRET", "ENTERPRISE-ID", jwtPreferences);
111-
112-
BoxDeveloperEditionAPIConnection api = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig);
111+
IAccessTokenCache tokenCache = new InMemoryLRUAccessTokenCache(10);
112+
113+
BoxDeveloperEditionAPIConnection api = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig, tokenCache);
113114
```
114115

115116
### Standard 3-Legged Oauth 2.0

doc/collaborations.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ collaboration.delete();
8989
Get a Collaboration's Information
9090
---------------------------------
9191

92-
Calling [`getInfo()`][get-info] on a collaboration returns a snapshot of the
92+
Calling [`getInfo()`][get-info-fields] on a collaboration returns a snapshot of the
9393
collaboration's info.
9494

9595
<!-- sample get_collaborations_id -->
@@ -106,7 +106,6 @@ BoxCollaboration collaboration = new BoxCollaboration(api, "id");
106106
BoxCollaboration.Info info = collaboration.getInfo(BoxCollaboration.ALL_FIELDS);
107107
```
108108

109-
[get-info]: http://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxCollaboration.html#getInfo--
110109
[get-info-fields]: http://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxCollaboration.html#getInfo-java.lang.String...-
111110

112111
Get the Collaborations on a Folder

doc/configuration.md

Lines changed: 144 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Configuration
22

33
- [Proxy configuration](#proxy-configuration)
4+
- [Custom proxy authenticator](#custom-proxy-authenticator)
5+
- [Example: NTLM authenticator](#example-ntlm-authenticator)
46
- [Configure retries of calls and timeouts](#configure-retries-of-calls-and-timeouts)
57
- [Maximum retries](#maximum-retries)
68
- [Connection timeout](#connection-timeout)
@@ -11,24 +13,123 @@
1113
- [Base App URL](#base-app-url)
1214
- [Token URL](#token-url-deprecated)
1315
- [Revoke URL](#revoke-url-deprecated)
16+
- [SSL configuration](#ssl-configuration)
1417

1518
# Proxy configuration
1619

1720
To set up proxy
1821
use [BoxApiConnection.setProxy](https://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxAPIConnection.html#setProxy-java.net.Proxy-)
1922
to set proxy address
20-
and [BoxApiConnection.setProxyUsername](https://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxAPIConnection.html#setProxyUsername-java.lang.String-) /
21-
[BoxApiConnection.setProxyPassword](https://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxAPIConnection.html#setProxyPassword-java.lang.String-)
23+
and [BoxApiConnection.setProxyBasicAuthentication][set-basic-proxy-auth]
2224
to set username and password required by proxy:
2325

2426
```java
2527
BoxAPIConnection api=new BoxAPIConnection("access_token");
26-
Proxy proxy=new Proxy(Proxy.Type.HTTP,new InetSocketAddress("proxy_url",8888));
28+
Proxy proxy = new Proxy(Proxy.Type.HTTP,new InetSocketAddress("proxy_url",8888));
2729
// You can use any subclass of BoxAPIConnection
2830
api.setProxy(proxy);
29-
api.setProxyUsername("proxyUsername");
30-
api.setProxyPassword("proxyPassword");
31+
api.setProxyBasicAuthentication("proxyUsername", "proxyPassword");
3132
```
33+
Proxy username and password will be used only if provided `SocketAddress` is an instance of
34+
`InetSocketAddress`. If you would like to use a custom `SocketAddress` you can provide your own
35+
`okhttp3.Authenticator` using [BoxApiConnection.setProxyAuthenticator(Authenticator)][set-proxy-authenticator]
36+
37+
38+
## Custom proxy authenticator
39+
By using [BoxApiConnection.setProxyBasicAuthentication][set-basic-proxy-auth] you can enable default
40+
proxy authenticator that handles only Basic authentication. But you can provide your own authenticator by using
41+
[BoxApiConnection.setProxyAuthenticator(Authenticator)][set-proxy-authenticator].
42+
43+
To do that you will need to add a dependency to your project:
44+
```
45+
"com.squareup.okhttp3:okhttp:XXX"
46+
```
47+
Please match the version with what SDK is using by checking `build.gradle`
48+
and looking for entry `implementation "com.squareup.okhttp3:okhttp:"`.
49+
50+
Now you can add an authenticator. by calling
51+
52+
```java
53+
BoxAPIConnection api = new BoxAPIConnection("access_token");
54+
Proxy proxy = new Proxy(Proxy.Type.HTTP,new InetSocketAddress("proxy_url",8888));
55+
api.setProxy(proxy);
56+
api.setProxyAuthenticator((route, response) -> response
57+
.request()
58+
.newBuilder()
59+
.addHeader("Proxy-Authorization", "My custom authenticator")
60+
.build()
61+
);
62+
```
63+
64+
### Example: NTLM authenticator
65+
66+
For example, you can add NTLM authorization. This is example NTLM authenticator that
67+
is using parts from Apache Http Client 5.
68+
69+
```java
70+
import okhttp3.Authenticator;
71+
import okhttp3.Request;
72+
import okhttp3.Response;
73+
import okhttp3.Route;
74+
import org.apache.hc.client5.http.impl.auth.NTLMEngineException;
75+
76+
public class NTLMAuthenticator implements Authenticator {
77+
final NTLMEngineImpl engine = new NTLMEngineImpl();
78+
private final String domain;
79+
private final String username;
80+
private final String password;
81+
private final String ntlmMsg1;
82+
83+
public NTLMAuthenticator(String username, String password, String domain) {
84+
this.domain = domain;
85+
this.username = username;
86+
this.password = password;
87+
String localNtlmMsg1 = null;
88+
try {
89+
localNtlmMsg1 = engine.generateType1Msg(null, null);
90+
} catch (Exception e) {
91+
e.printStackTrace();
92+
}
93+
ntlmMsg1 = localNtlmMsg1;
94+
}
95+
96+
@Override
97+
public Request authenticate(Route route, Response response) {
98+
if(response.code() == 407 && "Proxy authorization required".equals(response.message())) {
99+
String ntlmChallenge = response.headers("Proxy-Authenticate")
100+
.stream()
101+
.filter(h -> h.startsWith("NTLM "))
102+
.findFirst().orElse("");
103+
if(ntlmChallenge.length() > 5) {
104+
try {
105+
String ntlmMsg3 = engine.generateType3Msg(username, password.toCharArray(), domain, "ok-http-example-ntlm", ntlmChallenge.substring(5));
106+
return response.request().newBuilder().header("proxy-Authorization", "NTLM " + ntlmMsg3).build();
107+
} catch (NTLMEngineException e) {
108+
throw new RuntimeException(e);
109+
}
110+
}
111+
return response.request().newBuilder().header("proxy-Authorization", "NTLM " + ntlmMsg1).build();
112+
}
113+
return response.request();
114+
}
115+
}
116+
```
117+
118+
The `NTLMEngineImpl` could be created by using Apache implementation that can be found
119+
[here](https://github.com/apache/httpcomponents-client/blob/master/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/NTLMEngineImpl.java).
120+
You can add a dependency to `org.apache.httpcomponents.client5:httpclient5:5.1.3`.
121+
Copy the `NTLMEngineImpl` class and add it to your source.
122+
123+
Now you can use custom NTML Authenticator in your `BoxAPIConnection`:
124+
```java
125+
BoxAPIConnection api = new BoxAPIConnection("access_token");
126+
Proxy proxy = new Proxy(Proxy.Type.HTTP,new InetSocketAddress("proxy_url",8888));
127+
api.setProxy(proxy);
128+
api.setProxyAuthenticator(new NTLMAuthenticator("some proxy username", "some proxy password", "proxy workgroup"));
129+
```
130+
131+
[set-basic-proxy-auth]: https://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxAPIConnection.html#setProxyBasicAuthentication-java.lang.String-java.lang.String-
132+
[set-proxy-authenticator]: https://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxAPIConnection.html#setProxyAuthenticator-okhttp3.Authenticator-
32133

33134
# Configure retries of calls and timeouts
34135
SDK can retry failed calls when:
@@ -151,3 +252,41 @@ api.setRevokeURL("https://example.com/revoke");
151252
```
152253

153254
If you use `setRevokeUrl` this URL will be used over the one coming from`setBaseUrl` when doing authentication.
255+
256+
# SSL configuration
257+
You can override default settings used to verify SSL certificates.
258+
This can be used to allow using self-signed certificates. For example:
259+
```java
260+
BoxAPIConnection api = new BoxAPIConnection(...);
261+
262+
// to allow self-signed certificates
263+
X509TrustManager trustManager = new X509TrustManager() {
264+
@Override
265+
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
266+
}
267+
268+
@Override
269+
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
270+
}
271+
272+
@Override
273+
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
274+
return new java.security.cert.X509Certificate[]{};
275+
}
276+
};
277+
278+
// to allow self-signed certificates created for localhost
279+
HostnameVerifier hostnameVerifier = (hostname, session) -> true;
280+
281+
api.configureSslCertificatesValidation(trustManager, hostnameVerifier);
282+
```
283+
284+
If you just need to provide trust manager use `BoxAPIConnection.DEFAULT_HOSTNAME_VERIFIER` as a hostname verifier.
285+
The same goes for hostname verifier. If you need just to provide it use
286+
`BoxAPIConnection.DEFAULT_TRUST_MANAGER` as a trust manager.
287+
Example:
288+
```java
289+
BoxAPIConnection api = new BoxAPIConnection(...);
290+
X509TrustManager trustManager = ...
291+
api.configureSslCertificatesValidation(trustManager, BoxAPIConnection.DEFAULT_HOSTNAME_VERIFIER);
292+
```

0 commit comments

Comments
 (0)