diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 3579b6b52..06244724e 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -20,10 +20,10 @@ jobs:
matrix:
docker-img:
- docker.io/arangodb/arangodb:3.7.15
- - docker.io/arangodb/arangodb:3.8.2
+ - docker.io/arangodb/arangodb:3.8.4
- docker.io/arangodb/arangodb-preview:3.9.0-alpha.1
- docker.io/arangodb/enterprise:3.7.15
- - docker.io/arangodb/enterprise:3.8.2
+ - docker.io/arangodb/enterprise:3.8.4
- docker.io/arangodb/enterprise-preview:3.9.0-alpha.1
topology:
- single
@@ -38,7 +38,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- - name: Set up JDK 1.8
+ - name: Set up JDK
uses: actions/setup-java@v2
with:
java-version: ${{matrix.java-version}}
@@ -74,3 +74,69 @@ jobs:
with:
name: logs.tgz
path: ./logs.tgz
+
+ test-jwt:
+ timeout-minutes: 20
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ docker-img:
+ - docker.io/arangodb/enterprise:3.8.4
+ topology:
+ - single
+ - cluster
+ - activefailover
+ db-ext-names:
+ - false
+ java-version:
+ - 17
+ user-language:
+ - en
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK
+ uses: actions/setup-java@v2
+ with:
+ java-version: ${{matrix.java-version}}
+ distribution: 'adopt'
+ - name: Start Database
+ run: ./docker/start_db.sh
+ env:
+ ARANGO_LICENSE_KEY: ${{ secrets.ARANGO_LICENSE_KEY }}
+ STARTER_MODE: ${{matrix.topology}}
+ DOCKER_IMAGE: ${{matrix.docker-img}}
+ DATABASE_EXTENDED_NAMES: ${{matrix.db-ext-names}}
+ - name: Cache local Maven repository
+ uses: actions/cache@v2
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-maven-
+ - name: Set JWT
+ run: |
+ ENDPOINT=$(./docker/find_active_endpoint.sh)
+ echo "Active endpoint: $ENDPOINT"
+ JWT=$(curl "http://$ENDPOINT/_db/_system/_open/auth" -X POST -d '{"username":"root","password":"test"}' | jq ".jwt" | xargs)
+ echo "Setting JWT: $JWT"
+ sed -i "/arangodb.password/c\arangodb.jwt=$JWT" src/test/resources/arangodb.properties
+ - name: Info
+ run: mvn -version
+ - name: Test
+ run: mvn --no-transfer-progress test -DargLine="-Duser.language=${{matrix.user-language}}"
+ - name: Collect docker logs on failure
+ if: ${{ cancelled() || failure() }}
+ uses: jwalton/gh-docker-logs@v1
+ with:
+ dest: './logs'
+ - name: Tar logs
+ if: ${{ cancelled() || failure() }}
+ run: tar cvzf ./logs.tgz ./logs
+ - name: Upload logs to GitHub
+ if: ${{ cancelled() || failure() }}
+ uses: actions/upload-artifact@master
+ with:
+ name: logs.tgz
+ path: ./logs.tgz
diff --git a/ChangeLog.md b/ChangeLog.md
index c8b30170d..48ecb27aa 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
## [Unreleased]
+- fixed swallowing connection exceptions (#420)
+- fixed `stopwords` analyzer (#414)
+- set max retries for active failover redirects (#412)
+- fixed deserializing `null` value as String (#411)
+
## [6.14.0] - 2021-10-01
- fixed issues with non-English locales (#407)
diff --git a/docker/find_active_endpoint.sh b/docker/find_active_endpoint.sh
new file mode 100755
index 000000000..505f4022e
--- /dev/null
+++ b/docker/find_active_endpoint.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+COORDINATORS=("172.17.0.1:8529" "172.17.0.1:8539" "172.17.0.1:8549")
+
+for a in ${COORDINATORS[*]} ; do
+ if curl -u root:test --silent --fail "http://$a"; then
+ echo "$a"
+ exit 0
+ fi
+done
+
+echo "Could not find any active endpoint!"
+exit 1
diff --git a/src/main/java/com/arangodb/ArangoDB.java b/src/main/java/com/arangodb/ArangoDB.java
index 25a0738f8..8cfa37e31 100644
--- a/src/main/java/com/arangodb/ArangoDB.java
+++ b/src/main/java/com/arangodb/ArangoDB.java
@@ -179,6 +179,17 @@ public Builder password(final String password) {
return this;
}
+ /**
+ * Sets the JWT for the user authentication.
+ *
+ * @param jwt token to use (default: {@code null})
+ * @return {@link ArangoDB.Builder}
+ */
+ public Builder jwt(final String jwt) {
+ setJwt(jwt);
+ return this;
+ }
+
/**
* If set to {@code true} SSL will be used when connecting to an ArangoDB server.
*
@@ -217,7 +228,7 @@ public Builder hostnameVerifier(final HostnameVerifier hostnameVerifier) {
*
* @param httpRequestRetryHandler HttpRequestRetryHandler to be used
* @return {@link ArangoDB.Builder}
- *
+ *
*
* NOTE:
* Some ArangoDB HTTP endpoints do not honor RFC-2616 HTTP 1.1 specification in respect to
@@ -647,12 +658,18 @@ public synchronized ArangoDB build() {
final Collection hostList = createHostList(max, connectionFactory);
final HostResolver hostResolver = createHostResolver(hostList, max, connectionFactory);
final HostHandler hostHandler = createHostHandler(hostResolver);
+ hostHandler.setJwt(jwt);
return new ArangoDBImpl(
new VstCommunicationSync.Builder(hostHandler).timeout(timeout).user(user).password(password)
- .useSsl(useSsl).sslContext(sslContext).chunksize(chunksize).maxConnections(maxConnections)
- .connectionTtl(connectionTtl),
- new HttpCommunication.Builder(hostHandler), util, protocol, hostResolver, new ArangoContext());
+ .jwt(jwt).useSsl(useSsl).sslContext(sslContext).chunksize(chunksize)
+ .maxConnections(maxConnections).connectionTtl(connectionTtl),
+ new HttpCommunication.Builder(hostHandler),
+ util,
+ protocol,
+ hostResolver,
+ hostHandler,
+ new ArangoContext());
}
}
@@ -664,6 +681,14 @@ public synchronized ArangoDB build() {
*/
void shutdown() throws ArangoDBException;
+ /**
+ * Updates the JWT used for requests authorization. It does not change already existing VST connections, since VST
+ * connections are authenticated during the initialization phase.
+ *
+ * @param jwt token to use
+ */
+ void updateJwt(String jwt);
+
/**
* Returns a {@code ArangoDatabase} instance for the {@code _system} database.
*
@@ -897,10 +922,8 @@ public synchronized ArangoDB build() {
/**
* Returns fatal, error, warning or info log messages from the server's global log.
*
- * @param options
- * Additional options, can be null
+ * @param options Additional options, can be null
* @return the log messages
- *
* @throws ArangoDBException
* @see API
* Documentation
@@ -912,10 +935,8 @@ public synchronized ArangoDB build() {
/**
* Returns the server logs
*
- * @param options
- * Additional options, can be null
+ * @param options Additional options, can be null
* @return the log messages
- *
* @see API
* Documentation
* @since ArangoDB 3.8
diff --git a/src/main/java/com/arangodb/async/ArangoDBAsync.java b/src/main/java/com/arangodb/async/ArangoDBAsync.java
index 8c88f04e3..762427810 100644
--- a/src/main/java/com/arangodb/async/ArangoDBAsync.java
+++ b/src/main/java/com/arangodb/async/ArangoDBAsync.java
@@ -26,14 +26,7 @@
import com.arangodb.async.internal.ArangoDBAsyncImpl;
import com.arangodb.async.internal.velocystream.VstCommunicationAsync;
import com.arangodb.async.internal.velocystream.VstConnectionFactoryAsync;
-import com.arangodb.entity.ArangoDBVersion;
-import com.arangodb.entity.LoadBalancingStrategy;
-import com.arangodb.entity.LogEntity;
-import com.arangodb.entity.LogEntriesEntity;
-import com.arangodb.entity.LogLevelEntity;
-import com.arangodb.entity.Permissions;
-import com.arangodb.entity.ServerRole;
-import com.arangodb.entity.UserEntity;
+import com.arangodb.entity.*;
import com.arangodb.internal.ArangoContext;
import com.arangodb.internal.ArangoDefaults;
import com.arangodb.internal.InternalArangoDBBuilder;
@@ -53,18 +46,7 @@
import com.arangodb.util.ArangoDeserializer;
import com.arangodb.util.ArangoSerialization;
import com.arangodb.util.ArangoSerializer;
-import com.arangodb.velocypack.VPack;
-import com.arangodb.velocypack.VPackAnnotationFieldFilter;
-import com.arangodb.velocypack.VPackAnnotationFieldNaming;
-import com.arangodb.velocypack.VPackDeserializer;
-import com.arangodb.velocypack.VPackInstanceCreator;
-import com.arangodb.velocypack.VPackJsonDeserializer;
-import com.arangodb.velocypack.VPackJsonSerializer;
-import com.arangodb.velocypack.VPackModule;
-import com.arangodb.velocypack.VPackParser;
-import com.arangodb.velocypack.VPackParserModule;
-import com.arangodb.velocypack.VPackSerializer;
-import com.arangodb.velocypack.ValueType;
+import com.arangodb.velocypack.*;
import com.arangodb.velocystream.Request;
import com.arangodb.velocystream.Response;
@@ -92,6 +74,14 @@ public interface ArangoDBAsync extends ArangoSerializationAccessor {
void shutdown() throws ArangoDBException;
+ /**
+ * Updates the JWT used for requests authorization. It does not change already existing VST connections, since VST
+ * connections are authenticated during the initialization phase.
+ *
+ * @param jwt token to use
+ */
+ void updateJwt(String jwt);
+
/**
* Returns a handler of the system database
*
@@ -281,10 +271,8 @@ public interface ArangoDBAsync extends ArangoSerializationAccessor {
/**
* Returns fatal, error, warning or info log messages from the server's global log.
*
- * @param options
- * Additional options, can be null
+ * @param options Additional options, can be null
* @return the log messages
- *
* @see API
* Documentation
* @deprecated use {@link #getLogEntries(LogOptions)} instead
@@ -295,10 +283,8 @@ public interface ArangoDBAsync extends ArangoSerializationAccessor {
/**
* Returns the server logs
*
- * @param options
- * Additional options, can be null
+ * @param options Additional options, can be null
* @return the log messages
- *
* @see API
* Documentation
* @since ArangoDB 3.8
@@ -383,6 +369,17 @@ public Builder password(final String password) {
return this;
}
+ /**
+ * Sets the JWT for the user authentication.
+ *
+ * @param jwt token to use (default: {@code null})
+ * @return {@link ArangoDBAsync.Builder}
+ */
+ public Builder jwt(final String jwt) {
+ setJwt(jwt);
+ return this;
+ }
+
/**
* If set to true
SSL will be used when connecting to an ArangoDB server.
*
@@ -812,18 +809,20 @@ public synchronized ArangoDBAsync build() {
syncBuilder(syncHostHandler),
asyncHostResolver,
syncHostResolver,
+ asyncHostHandler,
+ syncHostHandler,
new ArangoContext());
}
private VstCommunicationAsync.Builder asyncBuilder(final HostHandler hostHandler) {
return new VstCommunicationAsync.Builder(hostHandler).timeout(timeout).user(user).password(password)
- .useSsl(useSsl).sslContext(sslContext).chunksize(chunksize).maxConnections(maxConnections)
+ .jwt(jwt).useSsl(useSsl).sslContext(sslContext).chunksize(chunksize).maxConnections(maxConnections)
.connectionTtl(connectionTtl);
}
private VstCommunicationSync.Builder syncBuilder(final HostHandler hostHandler) {
return new VstCommunicationSync.Builder(hostHandler).timeout(timeout).user(user).password(password)
- .useSsl(useSsl).sslContext(sslContext).chunksize(chunksize).maxConnections(maxConnections)
+ .jwt(jwt).useSsl(useSsl).sslContext(sslContext).chunksize(chunksize).maxConnections(maxConnections)
.connectionTtl(connectionTtl);
}
diff --git a/src/main/java/com/arangodb/async/internal/ArangoDBAsyncImpl.java b/src/main/java/com/arangodb/async/internal/ArangoDBAsyncImpl.java
index 426546c5e..93c58df91 100644
--- a/src/main/java/com/arangodb/async/internal/ArangoDBAsyncImpl.java
+++ b/src/main/java/com/arangodb/async/internal/ArangoDBAsyncImpl.java
@@ -27,6 +27,7 @@
import com.arangodb.entity.*;
import com.arangodb.internal.*;
import com.arangodb.internal.net.CommunicationProtocol;
+import com.arangodb.internal.net.HostHandler;
import com.arangodb.internal.net.HostResolver;
import com.arangodb.internal.util.ArangoSerializationFactory;
import com.arangodb.internal.util.ArangoSerializationFactory.Serializer;
@@ -55,6 +56,8 @@ public class ArangoDBAsyncImpl extends InternalArangoDB imp
private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBAsyncImpl.class);
private final CommunicationProtocol cp;
+ private final HostHandler asyncHostHandler;
+ private final HostHandler syncHostHandler;
public ArangoDBAsyncImpl(
final VstCommunicationAsync.Builder asyncCommBuilder,
@@ -62,6 +65,8 @@ public ArangoDBAsyncImpl(
final VstCommunicationSync.Builder syncCommBuilder,
final HostResolver asyncHostResolver,
final HostResolver syncHostResolver,
+ final HostHandler asyncHostHandler,
+ final HostHandler syncHostHandler,
final ArangoContext context
) {
@@ -70,6 +75,8 @@ public ArangoDBAsyncImpl(
final VstCommunication cacheCom = syncCommBuilder.build(util.get(Serializer.INTERNAL));
cp = new VstProtocol(cacheCom);
+ this.asyncHostHandler = asyncHostHandler;
+ this.syncHostHandler = syncHostHandler;
ArangoExecutorSync arangoExecutorSync = new ArangoExecutorSync(cp, util, new DocumentCache());
asyncHostResolver.init(arangoExecutorSync, util.get(Serializer.INTERNAL));
@@ -95,6 +102,14 @@ public void shutdown() throws ArangoDBException {
}
}
+ @Override
+ public void updateJwt(String jwt) {
+ asyncHostHandler.setJwt(jwt);
+ syncHostHandler.setJwt(jwt);
+ cp.setJwt(jwt);
+ executor.setJwt(jwt);
+ }
+
@Override
public ArangoDatabaseAsync db() {
return db(ArangoRequestParam.SYSTEM);
diff --git a/src/main/java/com/arangodb/async/internal/ArangoExecutorAsync.java b/src/main/java/com/arangodb/async/internal/ArangoExecutorAsync.java
index 8340ebcc8..860c185de 100644
--- a/src/main/java/com/arangodb/async/internal/ArangoExecutorAsync.java
+++ b/src/main/java/com/arangodb/async/internal/ArangoExecutorAsync.java
@@ -80,4 +80,9 @@ public void disconnect() {
outgoingExecutor.shutdown();
}
}
+
+ public void setJwt(String jwt) {
+ communication.setJwt(jwt);
+ }
+
}
diff --git a/src/main/java/com/arangodb/async/internal/velocystream/VstCommunicationAsync.java b/src/main/java/com/arangodb/async/internal/velocystream/VstCommunicationAsync.java
index 3a1b3f1b7..f07eeba2a 100644
--- a/src/main/java/com/arangodb/async/internal/velocystream/VstCommunicationAsync.java
+++ b/src/main/java/com/arangodb/async/internal/velocystream/VstCommunicationAsync.java
@@ -28,6 +28,7 @@
import com.arangodb.internal.util.HostUtils;
import com.arangodb.internal.velocystream.VstCommunication;
import com.arangodb.internal.velocystream.internal.AuthenticationRequest;
+import com.arangodb.internal.velocystream.internal.JwtAuthenticationRequest;
import com.arangodb.internal.velocystream.internal.Message;
import com.arangodb.util.ArangoSerialization;
import com.arangodb.velocypack.exception.VPackException;
@@ -49,9 +50,9 @@ public class VstCommunicationAsync extends VstCommunication send(final Message message, final VstConnecti
@Override
protected void authenticate(final VstConnectionAsync connection) {
+ Request authRequest;
+ if (jwt != null) {
+ authRequest = new JwtAuthenticationRequest(jwt, ENCRYPTION_JWT);
+ } else {
+ authRequest = new AuthenticationRequest(user, password != null ? password : "", ENCRYPTION_PLAIN);
+ }
+
Response response;
try {
- response = execute(new AuthenticationRequest(user, password != null ? password : "", ENCRYPTION_PLAIN),
- connection).get();
+ response = execute(authRequest, connection).get();
} catch (final InterruptedException | ExecutionException e) {
- throw new ArangoDBException(e);
+ Throwable cause = e.getCause();
+ if (cause instanceof ArangoDBException) {
+ throw (ArangoDBException) cause;
+ } else {
+ throw new ArangoDBException(e.getCause());
+ }
}
checkError(response);
}
@@ -142,6 +154,7 @@ public static class Builder {
private Long connectionTtl;
private String user;
private String password;
+ private String jwt;
private Boolean useSsl;
private SSLContext sslContext;
private Integer chunksize;
@@ -167,6 +180,11 @@ public Builder password(final String password) {
return this;
}
+ public Builder jwt(final String jwt) {
+ this.jwt = jwt;
+ return this;
+ }
+
public Builder useSsl(final Boolean useSsl) {
this.useSsl = useSsl;
return this;
@@ -193,7 +211,7 @@ public Builder connectionTtl(final Long connectionTtl) {
}
public VstCommunicationAsync build(final ArangoSerialization util) {
- return new VstCommunicationAsync(hostHandler, timeout, user, password, useSsl, sslContext, util, chunksize,
+ return new VstCommunicationAsync(hostHandler, timeout, user, password, jwt, useSsl, sslContext, util, chunksize,
maxConnections, connectionTtl);
}
}
diff --git a/src/main/java/com/arangodb/internal/ArangoDBImpl.java b/src/main/java/com/arangodb/internal/ArangoDBImpl.java
index 60e337b6b..3ced2d2b7 100644
--- a/src/main/java/com/arangodb/internal/ArangoDBImpl.java
+++ b/src/main/java/com/arangodb/internal/ArangoDBImpl.java
@@ -29,6 +29,7 @@
import com.arangodb.internal.http.HttpProtocol;
import com.arangodb.internal.net.CommunicationProtocol;
import com.arangodb.internal.net.HostHandle;
+import com.arangodb.internal.net.HostHandler;
import com.arangodb.internal.net.HostResolver;
import com.arangodb.internal.util.ArangoSerializationFactory;
import com.arangodb.internal.util.ArangoSerializationFactory.Serializer;
@@ -59,10 +60,11 @@ public class ArangoDBImpl extends InternalArangoDB implement
private ArangoCursorInitializer cursorInitializer;
private final CommunicationProtocol cp;
+ private final HostHandler hostHandler;
public ArangoDBImpl(final VstCommunicationSync.Builder vstBuilder, final HttpCommunication.Builder httpBuilder,
final ArangoSerializationFactory util, final Protocol protocol, final HostResolver hostResolver,
- final ArangoContext context) {
+ final HostHandler hostHandler, final ArangoContext context) {
super(new ArangoExecutorSync(
createProtocol(vstBuilder, httpBuilder, util.get(Serializer.INTERNAL), protocol),
@@ -76,6 +78,7 @@ public ArangoDBImpl(final VstCommunicationSync.Builder vstBuilder, final HttpCom
new HttpCommunication.Builder(httpBuilder),
util.get(Serializer.INTERNAL),
protocol);
+ this.hostHandler = hostHandler;
hostResolver.init(this.executor(), util());
@@ -123,6 +126,13 @@ public void shutdown() throws ArangoDBException {
}
}
+ @Override
+ public void updateJwt(String jwt) {
+ hostHandler.setJwt(jwt);
+ cp.setJwt(jwt);
+ executor.setJwt(jwt);
+ }
+
@Override
public ArangoDatabase db() {
return db(ArangoRequestParam.SYSTEM);
diff --git a/src/main/java/com/arangodb/internal/ArangoExecutorSync.java b/src/main/java/com/arangodb/internal/ArangoExecutorSync.java
index 20143fc8c..31c18d797 100644
--- a/src/main/java/com/arangodb/internal/ArangoExecutorSync.java
+++ b/src/main/java/com/arangodb/internal/ArangoExecutorSync.java
@@ -90,4 +90,9 @@ public void disconnect() {
throw new ArangoDBException(e);
}
}
+
+ public void setJwt(String jwt) {
+ protocol.setJwt(jwt);
+ }
+
}
diff --git a/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java b/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java
index 3b238a751..3d8bb4907 100644
--- a/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java
+++ b/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java
@@ -66,6 +66,7 @@ public abstract class InternalArangoDBBuilder {
private static final String PROPERTY_KEY_TIMEOUT = "arangodb.timeout";
private static final String PROPERTY_KEY_USER = "arangodb.user";
private static final String PROPERTY_KEY_PASSWORD = "arangodb.password";
+ private static final String PROPERTY_KEY_JWT = "arangodb.jwt";
private static final String PROPERTY_KEY_USE_SSL = "arangodb.usessl";
private static final String PROPERTY_KEY_COOKIE_SPEC = "arangodb.httpCookieSpec";
private static final String PROPERTY_KEY_V_STREAM_CHUNK_CONTENT_SIZE = "arangodb.chunksize";
@@ -82,6 +83,7 @@ public abstract class InternalArangoDBBuilder {
protected Integer timeout;
protected String user;
protected String password;
+ protected String jwt;
protected Boolean useSsl;
protected String httpCookieSpec;
protected HttpRequestRetryHandler httpRequestRetryHandler;
@@ -140,6 +142,7 @@ protected void loadProperties(final Properties properties) {
timeout = loadTimeout(properties, timeout);
user = loadUser(properties, user);
password = loadPassword(properties, password);
+ jwt = loadJwt(properties, jwt);
useSsl = loadUseSsl(properties, useSsl);
httpCookieSpec = loadhttpCookieSpec(properties, httpCookieSpec);
chunksize = loadChunkSize(properties, chunksize);
@@ -167,6 +170,10 @@ protected void setPassword(final String password) {
this.password = password;
}
+ protected void setJwt(final String jwt) {
+ this.jwt = jwt;
+ }
+
protected void setUseSsl(final Boolean useSsl) {
this.useSsl = useSsl;
}
@@ -305,6 +312,10 @@ private static String loadPassword(final Properties properties, final String cur
return getProperty(properties, PROPERTY_KEY_PASSWORD, currentValue, null);
}
+ private static String loadJwt(final Properties properties, final String currentValue) {
+ return getProperty(properties, PROPERTY_KEY_JWT, currentValue, null);
+ }
+
private static Boolean loadUseSsl(final Properties properties, final Boolean currentValue) {
return Boolean.parseBoolean(
getProperty(properties, PROPERTY_KEY_USE_SSL, currentValue, ArangoDefaults.DEFAULT_USE_SSL));
diff --git a/src/main/java/com/arangodb/internal/http/CURLLogger.java b/src/main/java/com/arangodb/internal/http/CURLLogger.java
index 17acbd163..941769fe4 100644
--- a/src/main/java/com/arangodb/internal/http/CURLLogger.java
+++ b/src/main/java/com/arangodb/internal/http/CURLLogger.java
@@ -42,7 +42,8 @@ private CURLLogger() {
public static void log(
final String url,
final Request request,
- final Credentials credencials,
+ final Credentials credentials,
+ final String jwt,
final ArangoSerialization util) {
final RequestType requestType = request.getRequestType();
final boolean includeBody = (requestType == RequestType.POST || requestType == RequestType.PUT
@@ -59,9 +60,12 @@ public static void log(
buffer.append(" -H '").append(header.getKey()).append(":").append(header.getValue()).append("'");
}
}
- if (credencials != null) {
- buffer.append(" -u ").append(credencials.getUserPrincipal().getName()).append(":")
- .append(credencials.getPassword());
+ if (credentials != null) {
+ buffer.append(" -u ").append(credentials.getUserPrincipal().getName()).append(":")
+ .append(credentials.getPassword());
+ }
+ if (jwt != null) {
+ buffer.append(" -H ").append("'Authorization: Bearer ").append(jwt).append("'");
}
if (includeBody) {
buffer.append(" -d @-");
diff --git a/src/main/java/com/arangodb/internal/http/HttpConnection.java b/src/main/java/com/arangodb/internal/http/HttpConnection.java
index 5a221164c..50cbc504d 100644
--- a/src/main/java/com/arangodb/internal/http/HttpConnection.java
+++ b/src/main/java/com/arangodb/internal/http/HttpConnection.java
@@ -69,6 +69,8 @@
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
+import static org.apache.http.HttpHeaders.AUTHORIZATION;
+
/**
* @author Mark Vollmary
*/
@@ -163,6 +165,7 @@ public HttpConnection build() {
private final CloseableHttpClient client;
private final String user;
private final String password;
+ private volatile String jwt = null;
private final ArangoSerialization util;
private final Boolean useSsl;
private final Protocol contentType;
@@ -315,9 +318,19 @@ public Response execute(final Request request) throws ArangoDBException, IOExcep
httpRequest.setHeader("Accept", "application/x-velocypack");
}
addHeader(request, httpRequest);
- final Credentials credentials = addCredentials(httpRequest);
+ Credentials credentials = null;
+ if (jwt != null) {
+ httpRequest.addHeader(AUTHORIZATION, "Bearer " + jwt);
+ } else if (user != null) {
+ credentials = new UsernamePasswordCredentials(user, password != null ? password : "");
+ try {
+ httpRequest.addHeader(new BasicScheme().authenticate(credentials, httpRequest, null));
+ } catch (final AuthenticationException e) {
+ throw new ArangoDBException(e);
+ }
+ }
if (LOGGER.isDebugEnabled()) {
- CURLLogger.log(url, request, credentials, util);
+ CURLLogger.log(url, request, credentials, jwt, util);
}
Response response;
response = buildResponse(client.execute(httpRequest));
@@ -331,19 +344,6 @@ private static void addHeader(final Request request, final HttpRequestBase httpR
}
}
- public Credentials addCredentials(final HttpRequestBase httpRequest) {
- Credentials credentials = null;
- if (user != null) {
- credentials = new UsernamePasswordCredentials(user, password != null ? password : "");
- try {
- httpRequest.addHeader(new BasicScheme().authenticate(credentials, httpRequest, null));
- } catch (final AuthenticationException e) {
- throw new ArangoDBException(e);
- }
- }
- return credentials;
- }
-
public Response buildResponse(final CloseableHttpResponse httpResponse)
throws UnsupportedOperationException, IOException {
final Response response = new Response();
@@ -375,4 +375,9 @@ protected void checkError(final Response response) throws ArangoDBException {
ResponseUtils.checkError(util, response);
}
+ @Override
+ public void setJwt(String jwt) {
+ this.jwt = jwt;
+ }
+
}
diff --git a/src/main/java/com/arangodb/internal/http/HttpProtocol.java b/src/main/java/com/arangodb/internal/http/HttpProtocol.java
index 7997b245e..c3df748e9 100644
--- a/src/main/java/com/arangodb/internal/http/HttpProtocol.java
+++ b/src/main/java/com/arangodb/internal/http/HttpProtocol.java
@@ -45,6 +45,11 @@ public Response execute(final Request request, final HostHandle hostHandle) thro
return httpCommunitaction.execute(request, hostHandle);
}
+ @Override
+ public void setJwt(String jwt) {
+ // no-op: jwt is updated in the host handlers
+ }
+
@Override
public void close() throws IOException {
httpCommunitaction.close();
diff --git a/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java b/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java
index fbec7116b..31e9a38ea 100644
--- a/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java
+++ b/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java
@@ -33,4 +33,6 @@ public interface CommunicationProtocol extends Closeable {
Response execute(final Request request, HostHandle hostHandle) throws ArangoDBException;
+ void setJwt(String jwt);
+
}
diff --git a/src/main/java/com/arangodb/internal/net/Connection.java b/src/main/java/com/arangodb/internal/net/Connection.java
index a71a253ae..c2701361e 100644
--- a/src/main/java/com/arangodb/internal/net/Connection.java
+++ b/src/main/java/com/arangodb/internal/net/Connection.java
@@ -26,5 +26,5 @@
* @author Mark Vollmary
*/
public interface Connection extends Closeable {
-
+ void setJwt(String jwt);
}
diff --git a/src/main/java/com/arangodb/internal/net/ConnectionPool.java b/src/main/java/com/arangodb/internal/net/ConnectionPool.java
index 38341490e..d1ad313d0 100644
--- a/src/main/java/com/arangodb/internal/net/ConnectionPool.java
+++ b/src/main/java/com/arangodb/internal/net/ConnectionPool.java
@@ -31,4 +31,6 @@ public interface ConnectionPool extends Closeable {
Connection connection();
+ void setJwt(String jwt);
+
}
diff --git a/src/main/java/com/arangodb/internal/net/ConnectionPoolImpl.java b/src/main/java/com/arangodb/internal/net/ConnectionPoolImpl.java
index 43ab86d86..cf275e6db 100644
--- a/src/main/java/com/arangodb/internal/net/ConnectionPoolImpl.java
+++ b/src/main/java/com/arangodb/internal/net/ConnectionPoolImpl.java
@@ -41,6 +41,7 @@ public class ConnectionPoolImpl implements ConnectionPool {
private final List connections;
private int current;
private final ConnectionFactory factory;
+ private volatile String jwt = null;
public ConnectionPoolImpl(final HostDescription host, final Integer maxConnections,
final ConnectionFactory factory) {
@@ -54,7 +55,9 @@ public ConnectionPoolImpl(final HostDescription host, final Integer maxConnectio
@Override
public Connection createConnection(final HostDescription host) {
- return factory.create(host);
+ Connection c = factory.create(host);
+ c.setJwt(jwt);
+ return c;
}
@Override
@@ -78,6 +81,14 @@ public synchronized Connection connection() {
return connection;
}
+ @Override
+ public void setJwt(String jwt) {
+ this.jwt = jwt;
+ for (Connection connection : connections) {
+ connection.setJwt(jwt);
+ }
+ }
+
@Override
public synchronized void close() throws IOException {
for (final Connection connection : connections) {
diff --git a/src/main/java/com/arangodb/internal/net/DirtyReadHostHandler.java b/src/main/java/com/arangodb/internal/net/DirtyReadHostHandler.java
index 93039224d..d8e3f0033 100644
--- a/src/main/java/com/arangodb/internal/net/DirtyReadHostHandler.java
+++ b/src/main/java/com/arangodb/internal/net/DirtyReadHostHandler.java
@@ -81,4 +81,10 @@ public void closeCurrentOnError() {
determineHostHandler().closeCurrentOnError();
}
+ @Override
+ public void setJwt(String jwt) {
+ master.setJwt(jwt);
+ follower.setJwt(jwt);
+ }
+
}
diff --git a/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java b/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java
index d88ac21ec..a1427d426 100644
--- a/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java
+++ b/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java
@@ -104,4 +104,9 @@ public void closeCurrentOnError() {
current.closeOnError();
}
+ @Override
+ public void setJwt(String jwt) {
+ hosts.setJwt(jwt);
+ }
+
}
diff --git a/src/main/java/com/arangodb/internal/net/Host.java b/src/main/java/com/arangodb/internal/net/Host.java
index c3e7514ba..57f4d6e80 100644
--- a/src/main/java/com/arangodb/internal/net/Host.java
+++ b/src/main/java/com/arangodb/internal/net/Host.java
@@ -38,4 +38,7 @@ public interface Host {
void setMarkforDeletion(boolean markforDeletion);
boolean isMarkforDeletion();
+
+ void setJwt(String jwt);
+
}
diff --git a/src/main/java/com/arangodb/internal/net/HostHandler.java b/src/main/java/com/arangodb/internal/net/HostHandler.java
index b432ca81e..42c1e7570 100644
--- a/src/main/java/com/arangodb/internal/net/HostHandler.java
+++ b/src/main/java/com/arangodb/internal/net/HostHandler.java
@@ -40,4 +40,7 @@ public interface HostHandler {
void close() throws IOException;
void closeCurrentOnError();
+
+ void setJwt(String jwt);
+
}
diff --git a/src/main/java/com/arangodb/internal/net/HostImpl.java b/src/main/java/com/arangodb/internal/net/HostImpl.java
index a9839c911..d50b84ae1 100644
--- a/src/main/java/com/arangodb/internal/net/HostImpl.java
+++ b/src/main/java/com/arangodb/internal/net/HostImpl.java
@@ -73,6 +73,11 @@ public boolean isMarkforDeletion() {
return markforDeletion;
}
+ @Override
+ public void setJwt(String jwt) {
+ connectionPool.setJwt(jwt);
+ }
+
public void setMarkforDeletion(boolean markforDeletion) {
this.markforDeletion = markforDeletion;
}
diff --git a/src/main/java/com/arangodb/internal/net/HostSet.java b/src/main/java/com/arangodb/internal/net/HostSet.java
index f945a43cb..ddcad12bb 100644
--- a/src/main/java/com/arangodb/internal/net/HostSet.java
+++ b/src/main/java/com/arangodb/internal/net/HostSet.java
@@ -13,6 +13,7 @@ public class HostSet {
private static final Logger LOGGER = LoggerFactory.getLogger(HostSet.class);
private final ArrayList hosts = new ArrayList<>();
+ private volatile String jwt = null;
public HostSet() {
super();
@@ -32,21 +33,18 @@ public List getHostsList() {
}
public void addHost(Host newHost) {
-
if (hosts.contains(newHost)) {
LOGGER.debug("Host" + newHost + " already in Set");
-
for (Host host : hosts) {
if (host.equals(newHost)) {
host.setMarkforDeletion(false);
}
}
-
} else {
+ newHost.setJwt(jwt);
hosts.add(newHost);
LOGGER.debug("Added Host " + newHost + " - now " + hosts.size() + " Hosts in List");
}
-
}
public void close() {
@@ -77,7 +75,7 @@ public void clearAllMarkedForDeletion() {
LOGGER.debug("Clear all Hosts in Set with markForDeletion");
Iterator iterable = hosts.iterator();
- while (iterable.hasNext()){
+ while (iterable.hasNext()) {
Host host = iterable.next();
if (host.isMarkforDeletion()) {
try {
@@ -99,4 +97,12 @@ public void clear() {
close();
hosts.clear();
}
+
+ public void setJwt(String jwt) {
+ this.jwt = jwt;
+ for (Host h : hosts) {
+ h.setJwt(jwt);
+ }
+ }
+
}
diff --git a/src/main/java/com/arangodb/internal/net/RandomHostHandler.java b/src/main/java/com/arangodb/internal/net/RandomHostHandler.java
index a3ee4cac7..3ab1ce7da 100644
--- a/src/main/java/com/arangodb/internal/net/RandomHostHandler.java
+++ b/src/main/java/com/arangodb/internal/net/RandomHostHandler.java
@@ -85,4 +85,10 @@ public void closeCurrentOnError() {
current.closeOnError();
}
+ @Override
+ public void setJwt(String jwt) {
+ fallback.setJwt(jwt);
+ hosts.setJwt(jwt);
+ }
+
}
diff --git a/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java b/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java
index e4e653ff9..99f3c24eb 100644
--- a/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java
+++ b/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java
@@ -109,4 +109,9 @@ public void closeCurrentOnError() {
currentHost.closeOnError();
}
+ @Override
+ public void setJwt(String jwt) {
+ hosts.setJwt(jwt);
+ }
+
}
diff --git a/src/main/java/com/arangodb/internal/velocypack/VPackDriverModule.java b/src/main/java/com/arangodb/internal/velocypack/VPackDriverModule.java
index c0038cba3..723a696b2 100644
--- a/src/main/java/com/arangodb/internal/velocypack/VPackDriverModule.java
+++ b/src/main/java/com/arangodb/internal/velocypack/VPackDriverModule.java
@@ -27,6 +27,7 @@
import com.arangodb.entity.arangosearch.ConsolidationType;
import com.arangodb.entity.arangosearch.analyzer.SearchAnalyzer;
import com.arangodb.internal.velocystream.internal.AuthenticationRequest;
+import com.arangodb.internal.velocystream.internal.JwtAuthenticationRequest;
import com.arangodb.model.CollectionSchema;
import com.arangodb.model.TraversalOptions;
import com.arangodb.model.arangosearch.ArangoSearchPropertiesOptions;
@@ -55,6 +56,7 @@ public > void setup(final C context) {
});
context.registerSerializer(Request.class, VPackSerializers.REQUEST);
context.registerSerializer(AuthenticationRequest.class, VPackSerializers.AUTH_REQUEST);
+ context.registerSerializer(JwtAuthenticationRequest.class, VPackSerializers.JWT_AUTH_REQUEST);
context.registerSerializer(CollectionType.class, VPackSerializers.COLLECTION_TYPE);
context.registerSerializer(BaseDocument.class, VPackSerializers.BASE_DOCUMENT);
context.registerSerializer(BaseEdgeDocument.class, VPackSerializers.BASE_EDGE_DOCUMENT);
diff --git a/src/main/java/com/arangodb/internal/velocypack/VPackSerializers.java b/src/main/java/com/arangodb/internal/velocypack/VPackSerializers.java
index 9c7884e2f..ef3346db9 100644
--- a/src/main/java/com/arangodb/internal/velocypack/VPackSerializers.java
+++ b/src/main/java/com/arangodb/internal/velocypack/VPackSerializers.java
@@ -38,6 +38,7 @@
import com.arangodb.entity.arangosearch.StoreValuesType;
import com.arangodb.entity.arangosearch.StoredValue;
import com.arangodb.internal.velocystream.internal.AuthenticationRequest;
+import com.arangodb.internal.velocystream.internal.JwtAuthenticationRequest;
import com.arangodb.model.CollectionSchema;
import com.arangodb.model.TraversalOptions;
import com.arangodb.model.TraversalOptions.Order;
@@ -90,6 +91,15 @@ public class VPackSerializers {
builder.close();
};
+ public static final VPackSerializer JWT_AUTH_REQUEST = (builder, attribute, value, context) -> {
+ builder.add(attribute, ValueType.ARRAY);
+ builder.add(value.getVersion());
+ builder.add(value.getType());
+ builder.add(value.getEncryption());
+ builder.add(value.getToken());
+ builder.close();
+ };
+
public static final VPackSerializer COLLECTION_TYPE = (builder, attribute, value, context) -> builder.add(attribute, value.getType());
public static final VPackSerializer BASE_DOCUMENT = (builder, attribute, value, context) -> {
diff --git a/src/main/java/com/arangodb/internal/velocystream/VstCommunication.java b/src/main/java/com/arangodb/internal/velocystream/VstCommunication.java
index 92187e9ba..0cb2faea1 100644
--- a/src/main/java/com/arangodb/internal/velocystream/VstCommunication.java
+++ b/src/main/java/com/arangodb/internal/velocystream/VstCommunication.java
@@ -52,6 +52,7 @@
public abstract class VstCommunication implements Closeable {
protected static final String ENCRYPTION_PLAIN = "plain";
+ protected static final String ENCRYPTION_JWT = "jwt";
private static final Logger LOGGER = LoggerFactory.getLogger(VstCommunication.class);
protected static final AtomicLong mId = new AtomicLong(0L);
@@ -59,15 +60,17 @@ public abstract class VstCommunication implements Cl
protected final String user;
protected final String password;
+ protected volatile String jwt;
protected final Integer chunksize;
protected final HostHandler hostHandler;
- protected VstCommunication(final Integer timeout, final String user, final String password, final Boolean useSsl,
- final SSLContext sslContext, final ArangoSerialization util, final Integer chunksize,
- final HostHandler hostHandler) {
+ protected VstCommunication(final Integer timeout, final String user, final String password, final String jwt,
+ final Boolean useSsl, final SSLContext sslContext, final ArangoSerialization util,
+ final Integer chunksize, final HostHandler hostHandler) {
this.user = user;
this.password = password;
+ this.jwt = jwt;
this.util = util;
this.hostHandler = hostHandler;
this.chunksize = chunksize != null ? chunksize : ArangoDefaults.CHUNK_DEFAULT_CONTENT_SIZE;
@@ -89,7 +92,7 @@ protected synchronized C connect(final HostHandle hostHandle, final AccessType a
try {
connection.open();
hostHandler.success();
- if (user != null) {
+ if (jwt != null || user != null) {
tryAuthenticate(connection);
}
hostHandler.confirm();
@@ -191,4 +194,9 @@ protected Collection buildChunks(final Message message) {
return chunks;
}
+ public void setJwt(String jwt) {
+ this.jwt = jwt;
+ }
+
+
}
diff --git a/src/main/java/com/arangodb/internal/velocystream/VstCommunicationSync.java b/src/main/java/com/arangodb/internal/velocystream/VstCommunicationSync.java
index f433194bf..59a03a551 100644
--- a/src/main/java/com/arangodb/internal/velocystream/VstCommunicationSync.java
+++ b/src/main/java/com/arangodb/internal/velocystream/VstCommunicationSync.java
@@ -27,6 +27,7 @@
import com.arangodb.internal.net.HostHandler;
import com.arangodb.internal.util.HostUtils;
import com.arangodb.internal.velocystream.internal.AuthenticationRequest;
+import com.arangodb.internal.velocystream.internal.JwtAuthenticationRequest;
import com.arangodb.internal.velocystream.internal.Message;
import com.arangodb.internal.velocystream.internal.VstConnectionSync;
import com.arangodb.util.ArangoSerialization;
@@ -52,6 +53,7 @@ public static class Builder {
private Long connectionTtl;
private String user;
private String password;
+ private String jwt;
private Boolean useSsl;
private SSLContext sslContext;
private Integer chunksize;
@@ -64,8 +66,9 @@ public Builder(final HostHandler hostHandler) {
public Builder(final Builder builder) {
this(builder.hostHandler);
- timeout(builder.timeout).user(builder.user).password(builder.password).useSsl(builder.useSsl)
- .sslContext(builder.sslContext).chunksize(builder.chunksize).maxConnections(builder.maxConnections);
+ timeout(builder.timeout).user(builder.user).password(builder.password).jwt(builder.jwt)
+ .useSsl(builder.useSsl).sslContext(builder.sslContext).chunksize(builder.chunksize)
+ .maxConnections(builder.maxConnections);
}
public Builder timeout(final Integer timeout) {
@@ -83,6 +86,11 @@ public Builder password(final String password) {
return this;
}
+ public Builder jwt(final String jwt) {
+ this.jwt = jwt;
+ return this;
+ }
+
public Builder useSsl(final Boolean useSsl) {
this.useSsl = useSsl;
return this;
@@ -109,16 +117,17 @@ public Builder connectionTtl(final Long connectionTtl) {
}
public VstCommunication build(final ArangoSerialization util) {
- return new VstCommunicationSync(hostHandler, timeout, user, password, useSsl, sslContext, util, chunksize,
+ return new VstCommunicationSync(hostHandler, timeout, user, password, jwt, useSsl, sslContext, util, chunksize,
maxConnections, connectionTtl);
}
}
protected VstCommunicationSync(final HostHandler hostHandler, final Integer timeout, final String user,
- final String password, final Boolean useSsl, final SSLContext sslContext, final ArangoSerialization util,
+ final String password, final String jwt, final Boolean useSsl,
+ final SSLContext sslContext, final ArangoSerialization util,
final Integer chunksize, final Integer maxConnections, final Long ttl) {
- super(timeout, user, password, useSsl, sslContext, util, chunksize, hostHandler);
+ super(timeout, user, password, jwt, useSsl, sslContext, util, chunksize, hostHandler);
}
@Override
@@ -158,8 +167,13 @@ private Message send(final Message message, final VstConnectionSync connection)
@Override
protected void authenticate(final VstConnectionSync connection) {
- final Response response = execute(
- new AuthenticationRequest(user, password != null ? password : "", ENCRYPTION_PLAIN), connection);
+ Request authRequest;
+ if (jwt != null) {
+ authRequest = new JwtAuthenticationRequest(jwt, ENCRYPTION_JWT);
+ } else {
+ authRequest = new AuthenticationRequest(user, password != null ? password : "", ENCRYPTION_PLAIN);
+ }
+ final Response response = execute(authRequest, connection);
checkError(response);
}
diff --git a/src/main/java/com/arangodb/internal/velocystream/VstProtocol.java b/src/main/java/com/arangodb/internal/velocystream/VstProtocol.java
index 3a5d67352..25de5edba 100644
--- a/src/main/java/com/arangodb/internal/velocystream/VstProtocol.java
+++ b/src/main/java/com/arangodb/internal/velocystream/VstProtocol.java
@@ -46,6 +46,11 @@ public Response execute(final Request request, final HostHandle hostHandle) thro
return communication.execute(request, hostHandle);
}
+ @Override
+ public void setJwt(String jwt) {
+ communication.setJwt(jwt);
+ }
+
@Override
public void close() throws IOException {
communication.close();
diff --git a/src/main/java/com/arangodb/internal/velocystream/internal/JwtAuthenticationRequest.java b/src/main/java/com/arangodb/internal/velocystream/internal/JwtAuthenticationRequest.java
new file mode 100644
index 000000000..53ab1a58c
--- /dev/null
+++ b/src/main/java/com/arangodb/internal/velocystream/internal/JwtAuthenticationRequest.java
@@ -0,0 +1,25 @@
+package com.arangodb.internal.velocystream.internal;
+
+import com.arangodb.velocystream.Request;
+
+public class JwtAuthenticationRequest extends Request {
+
+ private final String token;
+ private final String encryption; // "jwt"
+
+ public JwtAuthenticationRequest(final String token, final String encryption) {
+ super(null, null, null);
+ this.token = token;
+ this.encryption = encryption;
+ setType(1000);
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public String getEncryption() {
+ return encryption;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/arangodb/internal/velocystream/internal/VstConnection.java b/src/main/java/com/arangodb/internal/velocystream/internal/VstConnection.java
index e1a656642..266820773 100644
--- a/src/main/java/com/arangodb/internal/velocystream/internal/VstConnection.java
+++ b/src/main/java/com/arangodb/internal/velocystream/internal/VstConnection.java
@@ -356,4 +356,8 @@ public String getConnectionName() {
return this.connectionName;
}
+ @Override
+ public void setJwt(String jwt) {
+ // no-op: VST connections send jwt token only at initialization time
+ }
}
diff --git a/src/test/java/com/arangodb/ArangoDBTest.java b/src/test/java/com/arangodb/ArangoDBTest.java
index 5b3db075d..985862226 100644
--- a/src/test/java/com/arangodb/ArangoDBTest.java
+++ b/src/test/java/com/arangodb/ArangoDBTest.java
@@ -388,7 +388,7 @@ public void updateUserDefaultCollectionAccess() {
@Test
public void authenticationFailPassword() {
- final ArangoDB arangoDB = new ArangoDB.Builder().password("no").build();
+ final ArangoDB arangoDB = new ArangoDB.Builder().password("no").jwt(null).build();
try {
arangoDB.getVersion();
fail();
@@ -399,7 +399,7 @@ public void authenticationFailPassword() {
@Test
public void authenticationFailUser() {
- final ArangoDB arangoDB = new ArangoDB.Builder().user("no").build();
+ final ArangoDB arangoDB = new ArangoDB.Builder().user("no").jwt(null).build();
try {
arangoDB.getVersion();
fail();
diff --git a/src/test/java/com/arangodb/JwtAuthTest.java b/src/test/java/com/arangodb/JwtAuthTest.java
new file mode 100644
index 000000000..9c2532fa7
--- /dev/null
+++ b/src/test/java/com/arangodb/JwtAuthTest.java
@@ -0,0 +1,122 @@
+package com.arangodb;
+
+import com.arangodb.util.ArangoSerialization;
+import com.arangodb.velocystream.Request;
+import com.arangodb.velocystream.RequestType;
+import com.arangodb.velocystream.Response;
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Michele Rastelli
+ */
+@RunWith(Parameterized.class)
+public class JwtAuthTest {
+
+ private static String jwt;
+ private final Protocol protocol;
+ private ArangoDB arangoDB;
+
+ @BeforeClass
+ public static void init() {
+ ArangoDB arangoDB = new ArangoDB.Builder().build();
+ jwt = getJwt(arangoDB);
+ arangoDB.shutdown();
+ }
+
+ @After
+ public void after() {
+ if (arangoDB != null)
+ arangoDB.shutdown();
+ }
+
+ @Parameterized.Parameters
+ public static List builders() {
+ return Arrays.asList(
+ Protocol.VST,
+ Protocol.HTTP_JSON,
+ Protocol.HTTP_VPACK
+ );
+ }
+
+ public JwtAuthTest(Protocol protocol) {
+ this.protocol = protocol;
+ }
+
+ @Test
+ public void notAuthenticated() {
+ arangoDB = getBuilder().build();
+ try {
+ arangoDB.getVersion();
+ fail();
+ } catch (ArangoDBException e) {
+ assertThat(e.getResponseCode(), is(401));
+ }
+ arangoDB.shutdown();
+ }
+
+ @Test
+ public void authenticated() {
+ arangoDB = getBuilder()
+ .jwt(jwt)
+ .build();
+ arangoDB.getVersion();
+ arangoDB.shutdown();
+ }
+
+ @Test
+ public void updateJwt() {
+ arangoDB = getBuilder()
+ .jwt(jwt)
+ .build();
+ arangoDB.getVersion();
+ if (protocol == Protocol.VST) {
+ arangoDB.shutdown();
+ }
+ arangoDB.updateJwt("bla");
+ try {
+ arangoDB.getVersion();
+ fail();
+ } catch (ArangoDBException e) {
+ assertThat(e.getResponseCode(), is(401));
+ }
+
+ arangoDB.updateJwt(jwt);
+ arangoDB.getVersion();
+ arangoDB.shutdown();
+ }
+
+ private ArangoDB.Builder getBuilder() {
+ return new ArangoDB.Builder()
+ .useProtocol(protocol)
+ .jwt(null) // unset credentials from properties file
+ .user(null) // unset credentials from properties file
+ .password(null); // unset credentials from properties file
+ }
+
+ private static String getJwt(ArangoDB arangoDB) {
+ ArangoSerialization serde = arangoDB.util();
+ Map reqBody = new HashMap<>();
+ reqBody.put("username", "root");
+ reqBody.put("password", "test");
+
+ Request req = new Request("_system", RequestType.POST, "/_open/auth");
+ req.setBody(serde.serialize(reqBody));
+
+ Response resp = arangoDB.execute(req);
+ Map respBody = serde.deserialize(resp.getBody(), Map.class);
+ return respBody.get("jwt");
+ }
+}
diff --git a/src/test/java/com/arangodb/async/ArangoDBTest.java b/src/test/java/com/arangodb/async/ArangoDBTest.java
index f6e33b7bc..c21715354 100644
--- a/src/test/java/com/arangodb/async/ArangoDBTest.java
+++ b/src/test/java/com/arangodb/async/ArangoDBTest.java
@@ -426,7 +426,7 @@ public void updateUserDefaultCollectionAccess() throws InterruptedException, Exe
@Test
public void authenticationFailPassword() throws InterruptedException {
- final ArangoDBAsync arangoDB = new ArangoDBAsync.Builder().password("no").build();
+ final ArangoDBAsync arangoDB = new ArangoDBAsync.Builder().password("no").jwt(null).build();
try {
arangoDB.getVersion().get();
fail();
@@ -437,7 +437,7 @@ public void authenticationFailPassword() throws InterruptedException {
@Test
public void authenticationFailUser() throws InterruptedException {
- final ArangoDBAsync arangoDB = new ArangoDBAsync.Builder().user("no").build();
+ final ArangoDBAsync arangoDB = new ArangoDBAsync.Builder().user("no").jwt(null).build();
try {
arangoDB.getVersion().get();
fail();
diff --git a/src/test/java/com/arangodb/async/JwtAuthTest.java b/src/test/java/com/arangodb/async/JwtAuthTest.java
new file mode 100644
index 000000000..25f6d1a06
--- /dev/null
+++ b/src/test/java/com/arangodb/async/JwtAuthTest.java
@@ -0,0 +1,102 @@
+package com.arangodb.async;
+
+import com.arangodb.ArangoDB;
+import com.arangodb.ArangoDBException;
+import com.arangodb.util.ArangoSerialization;
+import com.arangodb.velocystream.Request;
+import com.arangodb.velocystream.RequestType;
+import com.arangodb.velocystream.Response;
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+public class JwtAuthTest {
+
+ private static String jwt;
+ private ArangoDBAsync arangoDB;
+
+ @BeforeClass
+ public static void init() {
+ ArangoDB arangoDB = new ArangoDB.Builder().build();
+ jwt = getJwt(arangoDB);
+ arangoDB.shutdown();
+ }
+
+ @After
+ public void after() {
+ if (arangoDB != null)
+ arangoDB.shutdown();
+ }
+
+ @Test
+ public void notAuthenticated() throws InterruptedException {
+ arangoDB = getBuilder().build();
+ try {
+ arangoDB.getVersion().get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e.getCause(), is(instanceOf(ArangoDBException.class)));
+ assertThat(((ArangoDBException) e.getCause()).getResponseCode(), is(401));
+ }
+ arangoDB.shutdown();
+ }
+
+ @Test
+ public void authenticated() throws ExecutionException, InterruptedException {
+ arangoDB = getBuilder()
+ .jwt(jwt)
+ .build();
+ arangoDB.getVersion().get();
+ arangoDB.shutdown();
+ }
+
+ @Test
+ public void updateJwt() throws ExecutionException, InterruptedException {
+ arangoDB = getBuilder()
+ .jwt(jwt)
+ .build();
+ arangoDB.getVersion().get();
+ arangoDB.updateJwt("bla");
+ try {
+ arangoDB.getVersion().get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e.getCause(), is(instanceOf(ArangoDBException.class)));
+ assertThat(((ArangoDBException) e.getCause()).getResponseCode(), is(401));
+ }
+
+ arangoDB.updateJwt(jwt);
+ arangoDB.getVersion().get();
+ arangoDB.shutdown();
+ }
+
+ private ArangoDBAsync.Builder getBuilder() {
+ return new ArangoDBAsync.Builder()
+ .jwt(null) // unset credentials from properties file
+ .user(null) // unset credentials from properties file
+ .password(null); // unset credentials from properties file
+ }
+
+ private static String getJwt(ArangoDB arangoDB) {
+ ArangoSerialization serde = arangoDB.util();
+ Map reqBody = new HashMap<>();
+ reqBody.put("username", "root");
+ reqBody.put("password", "test");
+
+ Request req = new Request("_system", RequestType.POST, "/_open/auth");
+ req.setBody(serde.serialize(reqBody));
+
+ Response resp = arangoDB.execute(req);
+ Map respBody = serde.deserialize(resp.getBody(), Map.class);
+ return respBody.get("jwt");
+ }
+}
diff --git a/src/test/java/com/arangodb/internal/HostHandlerTest.java b/src/test/java/com/arangodb/internal/HostHandlerTest.java
index 7a4d22db0..b25585e50 100644
--- a/src/test/java/com/arangodb/internal/HostHandlerTest.java
+++ b/src/test/java/com/arangodb/internal/HostHandlerTest.java
@@ -26,6 +26,7 @@
import com.arangodb.util.ArangoSerialization;
import org.junit.Test;
+import java.io.IOException;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -38,9 +39,31 @@
*/
public class HostHandlerTest {
- private static final Host HOST_0 = new HostImpl(null, new HostDescription("127.0.0.1", 8529));
- private static final Host HOST_1 = new HostImpl(null, new HostDescription("127.0.0.2", 8529));
- private static final Host HOST_2 = new HostImpl(null, new HostDescription("127.0.0.3", 8529));
+ private static final ConnectionPool mockCP = new ConnectionPool() {
+ @Override
+ public Connection createConnection(HostDescription host) {
+ return null;
+ }
+
+ @Override
+ public Connection connection() {
+ return null;
+ }
+
+ @Override
+ public void setJwt(String jwt) {
+
+ }
+
+ @Override
+ public void close() throws IOException {
+
+ }
+ };
+
+ private static final Host HOST_0 = new HostImpl(mockCP, new HostDescription("127.0.0.1", 8529));
+ private static final Host HOST_1 = new HostImpl(mockCP, new HostDescription("127.0.0.2", 8529));
+ private static final Host HOST_2 = new HostImpl(mockCP, new HostDescription("127.0.0.3", 8529));
private static final HostResolver SINGLE_HOST = new HostResolver() {