From 1c2d737865bbfefcc1ef74db17255f779fede02a Mon Sep 17 00:00:00 2001 From: Kushagra Thapar Date: Tue, 6 Aug 2019 19:23:52 -0700 Subject: [PATCH 1/4] Added Cosmos Key Credential storage holder. Added test cases for Cosmos Key Credential --- .../com/azure/data/cosmos/CosmosClient.java | 81 +++-- .../data/cosmos/CosmosClientBuilder.java | 33 +- .../data/cosmos/CosmosKeyCredential.java | 25 ++ .../cosmos/internal/AsyncDocumentClient.java | 44 ++- .../BaseAuthorizationTokenProvider.java | 79 ++-- .../cosmos/internal/RxDocumentClientImpl.java | 39 +- .../cosmos/internal/TestConfigurations.java | 12 +- .../GatewayServiceConfigurationReader.java | 6 +- .../data/cosmos/ClientUnderTestBuilder.java | 4 +- .../data/cosmos/CosmosKeyCredentialTest.java | 344 ++++++++++++++++++ .../data/cosmos/CosmosPartitionKeyTests.java | 8 +- .../internal/RxDocumentClientUnderTest.java | 6 +- .../cosmos/internal/SpyClientBuilder.java | 7 +- .../internal/SpyClientUnderTestFactory.java | 41 ++- ...GatewayServiceConfigurationReaderTest.java | 5 +- .../RntbdTransportClientTest.java | 7 +- .../azure/data/cosmos/rx/TestSuiteBase.java | 19 +- .../data/cosmos/rx/TokenResolverTest.java | 28 +- 18 files changed, 616 insertions(+), 172 deletions(-) create mode 100644 sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosKeyCredential.java create mode 100644 sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/CosmosKeyCredentialTest.java diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClient.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClient.java index ba16568db7640..7782517d01dff 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClient.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClient.java @@ -29,24 +29,27 @@ public class CosmosClient implements AutoCloseable { private final ConsistencyLevel desiredConsistencyLevel; private final List permissions; private final TokenResolver tokenResolver; + private final CosmosKeyCredential cosmosKeyCredential; CosmosClient(CosmosClientBuilder builder) { this.configs = builder.configs(); this.serviceEndpoint = builder.endpoint(); - this.keyOrResourceToken = builder.key(); - this.connectionPolicy = builder.connectionPolicy(); - this.desiredConsistencyLevel = builder.consistencyLevel(); - this.permissions = builder.permissions(); - this.tokenResolver = builder.tokenResolver(); - this.asyncDocumentClient = new AsyncDocumentClient.Builder() - .withServiceEndpoint(this.serviceEndpoint) - .withMasterKeyOrResourceToken(this.keyOrResourceToken) - .withConnectionPolicy(this.connectionPolicy) - .withConsistencyLevel(this.desiredConsistencyLevel) - .withConfigs(this.configs) - .withTokenResolver(this.tokenResolver) - .build(); + this.keyOrResourceToken = builder.key(); + this.connectionPolicy = builder.connectionPolicy(); + this.desiredConsistencyLevel = builder.consistencyLevel(); + this.permissions = builder.permissions(); + this.tokenResolver = builder.tokenResolver(); + this.cosmosKeyCredential = builder.cosmosKeyCredential(); + this.asyncDocumentClient = new AsyncDocumentClient.Builder() + .withServiceEndpoint(this.serviceEndpoint) + .withMasterKeyOrResourceToken(this.keyOrResourceToken) + .withConnectionPolicy(this.connectionPolicy) + .withConsistencyLevel(this.desiredConsistencyLevel) + .withConfigs(this.configs) + .withTokenResolver(this.tokenResolver) + .withCosmosKeyCredential(this.cosmosKeyCredential) + .build(); } AsyncDocumentClient getContextClient() { @@ -121,10 +124,18 @@ TokenResolver getTokenResolver() { return tokenResolver; } + /** + * Gets the cosmos key credential + * @return cosmos key credential + */ + CosmosKeyCredential getCosmosKeyCredential() { + return cosmosKeyCredential; + } + /** * CREATE a Database if it does not already exist on the service - * - * The {@link Mono} upon successful completion will contain a single cosmos database response with the + * + * The {@link Mono} upon successful completion will contain a single cosmos database response with the * created or existing database. * @param databaseSettings CosmosDatabaseProperties * @return a {@link Mono} containing the cosmos database response with the created or existing database or @@ -136,7 +147,7 @@ public Mono createDatabaseIfNotExists(CosmosDatabaseProp /** * CREATE a Database if it does not already exist on the service - * The {@link Mono} upon successful completion will contain a single cosmos database response with the + * The {@link Mono} upon successful completion will contain a single cosmos database response with the * created or existing database. * @param id the id of the database * @return a {@link Mono} containing the cosmos database response with the created or existing database or @@ -145,7 +156,7 @@ public Mono createDatabaseIfNotExists(CosmosDatabaseProp public Mono createDatabaseIfNotExists(String id) { return createDatabaseIfNotExistsInternal(getDatabase(id)); } - + private Mono createDatabaseIfNotExistsInternal(CosmosDatabase database){ return database.read().onErrorResume(exception -> { if (exception instanceof CosmosClientException) { @@ -160,12 +171,12 @@ private Mono createDatabaseIfNotExistsInternal(CosmosDat /** * Creates a database. - * - * After subscription the operation will be performed. - * The {@link Mono} upon successful completion will contain a single resource response with the + * + * After subscription the operation will be performed. + * The {@link Mono} upon successful completion will contain a single resource response with the * created database. * In case of failure the {@link Mono} will error. - * + * * @param databaseSettings {@link CosmosDatabaseProperties} * @param options {@link CosmosDatabaseRequestOptions} * @return an {@link Mono} containing the single cosmos database response with the created database or an error. @@ -183,12 +194,12 @@ public Mono createDatabase(CosmosDatabaseProperties data /** * Creates a database. - * - * After subscription the operation will be performed. - * The {@link Mono} upon successful completion will contain a single resource response with the + * + * After subscription the operation will be performed. + * The {@link Mono} upon successful completion will contain a single resource response with the * created database. * In case of failure the {@link Mono} will error. - * + * * @param databaseSettings {@link CosmosDatabaseProperties} * @return an {@link Mono} containing the single cosmos database response with the created database or an error. */ @@ -198,12 +209,12 @@ public Mono createDatabase(CosmosDatabaseProperties data /** * Creates a database. - * - * After subscription the operation will be performed. - * The {@link Mono} upon successful completion will contain a single resource response with the + * + * After subscription the operation will be performed. + * The {@link Mono} upon successful completion will contain a single resource response with the * created database. * In case of failure the {@link Mono} will error. - * + * * @param id id of the database * @return a {@link Mono} containing the single cosmos database response with the created database or an error. */ @@ -275,11 +286,11 @@ public Mono createDatabase(String id, int throughput) { /** * Reads all databases. - * - * After subscription the operation will be performed. + * + * After subscription the operation will be performed. * The {@link Flux} will contain one or several feed response of the read databases. * In case of failure the {@link Flux} will error. - * + * * @param options {@link FeedOptions} * @return a {@link Flux} containing one or several feed response pages of read databases or an error. */ @@ -291,11 +302,11 @@ public Flux> readAllDatabases(FeedOptions /** * Reads all databases. - * - * After subscription the operation will be performed. + * + * After subscription the operation will be performed. * The {@link Flux} will contain one or several feed response of the read databases. * In case of failure the {@link Flux} will error. - * + * * @return a {@link Flux} containing one or several feed response pages of read databases or an error. */ public Flux> readAllDatabases() { diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClientBuilder.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClientBuilder.java index 2c799c92c6966..baf7369c0c666 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClientBuilder.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClientBuilder.java @@ -4,6 +4,7 @@ import com.azure.data.cosmos.internal.Configs; import com.azure.data.cosmos.internal.Permission; +import org.apache.commons.lang3.StringUtils; import java.util.List; @@ -33,6 +34,7 @@ public class CosmosClientBuilder { private ConsistencyLevel desiredConsistencyLevel; private List permissions; private TokenResolver tokenResolver; + private CosmosKeyCredential cosmosKeyCredential; CosmosClientBuilder() { } @@ -137,7 +139,7 @@ public CosmosClientBuilder permissions(List permissions) { } /** - * Gets the (@link ConsistencyLevel) to be used + * Gets the {@link ConsistencyLevel} to be used * @return the consistency level */ public ConsistencyLevel consistencyLevel() { @@ -145,7 +147,7 @@ public ConsistencyLevel consistencyLevel() { } /** - * Sets the (@link ConsistencyLevel) to be used + * Sets the {@link ConsistencyLevel} to be used * @param desiredConsistencyLevel {@link ConsistencyLevel} * @return current Builder */ @@ -163,7 +165,7 @@ public ConnectionPolicy connectionPolicy() { } /** - * Sets the (@link ConnectionPolicy) to be used + * Sets the {@link ConnectionPolicy} to be used * @param connectionPolicy {@link ConnectionPolicy} * @return current Builder */ @@ -172,6 +174,24 @@ public CosmosClientBuilder connectionPolicy(ConnectionPolicy connectionPolicy) { return this; } + /** + * Gets the {@link CosmosKeyCredential} to be used + * @return cosmosKeyCredential + */ + public CosmosKeyCredential cosmosKeyCredential() { + return cosmosKeyCredential; + } + + /** + * Sets the {@link CosmosKeyCredential} to be used + * @param cosmosKeyCredential {@link CosmosKeyCredential} + * @return current builder + */ + public CosmosClientBuilder cosmosKeyCredential(CosmosKeyCredential cosmosKeyCredential) { + this.cosmosKeyCredential = cosmosKeyCredential; + return this; + } + /** * Builds a cosmos configuration object with the provided properties * @return CosmosClient @@ -180,8 +200,11 @@ public CosmosClient build() { ifThrowIllegalArgException(this.serviceEndpoint == null, "cannot build client without service endpoint"); ifThrowIllegalArgException( - this.keyOrResourceToken == null && (permissions == null || permissions.isEmpty()) && this.tokenResolver == null, - "cannot build client without any one of key, resource token, permissions, and token resolver"); + this.keyOrResourceToken == null && (permissions == null || permissions.isEmpty()) + && this.tokenResolver == null && this.cosmosKeyCredential == null, + "cannot build client without any one of key, resource token, permissions, token resolver, and cosmos key credential"); + ifThrowIllegalArgException(cosmosKeyCredential != null && StringUtils.isEmpty(cosmosKeyCredential.getMasterKey()), + "cannot build client without key credential"); return new CosmosClient(this); } diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosKeyCredential.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosKeyCredential.java new file mode 100644 index 0000000000000..2a99143783944 --- /dev/null +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosKeyCredential.java @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.data.cosmos; + +/** + * Cosmos Key Credential is used to store key credentials, in order to support dynamic key rotation. + * Singleton instance should be used to support multiple keys. + * SDK insures to use the updated key provided in the same singleton instance which was used when building {@link CosmosClient} + */ +public class CosmosKeyCredential { + + private String masterKey; + + public CosmosKeyCredential(String masterKey) { + this.masterKey = masterKey; + } + + public String getMasterKey() { + return masterKey; + } + + public void setMasterKey(String masterKey) { + this.masterKey = masterKey; + } +} diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/AsyncDocumentClient.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/AsyncDocumentClient.java index 5439e4c66ebcd..63508a65c1928 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/AsyncDocumentClient.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/AsyncDocumentClient.java @@ -5,10 +5,12 @@ import com.azure.data.cosmos.ChangeFeedOptions; import com.azure.data.cosmos.ConnectionPolicy; import com.azure.data.cosmos.ConsistencyLevel; +import com.azure.data.cosmos.CosmosKeyCredential; import com.azure.data.cosmos.FeedOptions; import com.azure.data.cosmos.FeedResponse; import com.azure.data.cosmos.SqlQuerySpec; import com.azure.data.cosmos.TokenResolver; +import org.apache.commons.lang3.StringUtils; import reactor.core.publisher.Flux; import java.net.URI; @@ -71,6 +73,7 @@ class Builder { String masterKeyOrResourceToken; URI serviceEndpoint; TokenResolver tokenResolver; + CosmosKeyCredential cosmosKeyCredential; public Builder withServiceEndpoint(String serviceEndpoint) { try { @@ -134,23 +137,13 @@ public Builder withConnectionPolicy(ConnectionPolicy connectionPolicy) { return this; } - /** - * This method will accept tokenResolver which is rx function, it takes arguments
- * T1 requestVerb(STRING),
- * T2 resourceIdOrFullName(STRING),
- * T3 resourceType(com.azure.data.cosmos.internal.ResourceType),
- * T4 request headers(Map)
- *
- * and return
- * R authenticationToken(STRING)
- * - * @param tokenResolver tokenResolver function for authentication. - * @return current Builder. - */ - /*public Builder withTokenResolver(Func4, STRING> tokenResolver) { - this.tokenResolver = tokenResolver; + public Builder withCosmosKeyCredential(CosmosKeyCredential cosmosKeyCredential) { + if (cosmosKeyCredential != null && StringUtils.isEmpty(cosmosKeyCredential.getMasterKey())) { + throw new IllegalArgumentException("Cannot build client with empty key credential"); + } + this.cosmosKeyCredential = cosmosKeyCredential; return this; - }*/ + } /** * This method will accept functional interface TokenResolver which helps in generation authorization @@ -173,8 +166,12 @@ public AsyncDocumentClient build() { ifThrowIllegalArgException(this.serviceEndpoint == null, "cannot build client without service endpoint"); ifThrowIllegalArgException( - this.masterKeyOrResourceToken == null && (permissionFeed == null || permissionFeed.isEmpty()) && this.tokenResolver == null, - "cannot build client without any one of masterKey, resource token, permissionFeed and tokenResolver"); + this.masterKeyOrResourceToken == null && (permissionFeed == null || permissionFeed.isEmpty()) + && this.tokenResolver == null && this.cosmosKeyCredential == null, + "cannot build client without any one of masterKey, " + + "resource token, permissionFeed, tokenResolver and cosmos key credential"); + ifThrowIllegalArgException(cosmosKeyCredential != null && StringUtils.isEmpty(cosmosKeyCredential.getMasterKey()), + "cannot build client without key credential"); RxDocumentClientImpl client = new RxDocumentClientImpl(serviceEndpoint, masterKeyOrResourceToken, @@ -182,7 +179,8 @@ public AsyncDocumentClient build() { connectionPolicy, desiredConsistencyLevel, configs, - tokenResolver); + tokenResolver, + cosmosKeyCredential); client.init(); return client; } @@ -242,6 +240,14 @@ public TokenResolver getTokenResolver() { public void setTokenResolver(TokenResolver tokenResolver) { this.tokenResolver = tokenResolver; } + + public CosmosKeyCredential getCosmosKeyCredential() { + return cosmosKeyCredential; + } + + public void setCosmosKeyCredential(CosmosKeyCredential cosmosKeyCredential) { + this.cosmosKeyCredential = cosmosKeyCredential; + } } /** diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/BaseAuthorizationTokenProvider.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/BaseAuthorizationTokenProvider.java index 7bcb46c33a381..2e51f7c9e0efa 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/BaseAuthorizationTokenProvider.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/BaseAuthorizationTokenProvider.java @@ -3,6 +3,7 @@ package com.azure.data.cosmos.internal; +import com.azure.data.cosmos.CosmosKeyCredential; import com.azure.data.cosmos.internal.directconnectivity.HttpUtils; import org.apache.commons.lang3.StringUtils; @@ -24,19 +25,13 @@ public class BaseAuthorizationTokenProvider implements AuthorizationTokenProvider { private static final String AUTH_PREFIX = "type=master&ver=1.0&sig="; - private final String masterKey; - private final Mac macInstance; - - public BaseAuthorizationTokenProvider(String masterKey) { - this.masterKey = masterKey; - byte[] masterKeyDecodedBytes = Utils.Base64Decoder.decode(this.masterKey.getBytes()); - SecretKey signingKey = new SecretKeySpec(masterKeyDecodedBytes, "HMACSHA256"); - try { - this.macInstance = Mac.getInstance("HMACSHA256"); - this.macInstance.init(signingKey); - } catch (NoSuchAlgorithmException | InvalidKeyException e) { - throw new IllegalStateException(e); - } + private final CosmosKeyCredential cosmosKeyCredential; + private Mac macInstance; + private int masterKeyHashCode; + + public BaseAuthorizationTokenProvider(CosmosKeyCredential cosmosKeyCredential) { + this.cosmosKeyCredential = cosmosKeyCredential; + this.macInstance = getMacInstance(); } private static String getResourceSegment(ResourceType resourceType) { @@ -120,8 +115,8 @@ public String generateKeyAuthorizationSignature(String verb, throw new IllegalArgumentException("headers"); } - if (this.masterKey == null || this.masterKey.isEmpty()) { - throw new IllegalArgumentException("masterKey"); + if (StringUtils.isEmpty(this.cosmosKeyCredential.getMasterKey())) { + throw new IllegalArgumentException("key credentials cannot be empty"); } if(!PathsHelper.isNameBased(resourceIdOrFullName)) { @@ -149,12 +144,7 @@ public String generateKeyAuthorizationSignature(String verb, body.append('\n'); - Mac mac = null; - try { - mac = (Mac) this.macInstance.clone(); - } catch (CloneNotSupportedException e) { - throw new IllegalStateException(e); - } + Mac mac = getMacInstance(); byte[] digest = mac.doFinal(body.toString().getBytes()); @@ -221,8 +211,8 @@ public String generateKeyAuthorizationSignature(String verb, URI uri, Map headers) { + private String generateKeyAuthorizationSignatureNew(String verb, String resourceIdValue, String resourceType, + Map headers) { if (StringUtils.isEmpty(verb)) { throw new IllegalArgumentException(String.format(RMResources.StringArgumentNullOrEmpty, "verb")); } @@ -240,24 +230,43 @@ public String generateKeyAuthorizationSignatureNew(String verb, String resourceI // If any of the value is optional, it should still have the placeholder value // of "" // OperationType -> ResourceType -> ResourceId/OwnerId -> XDate -> Date - String verbInput = verb; - String resourceIdInput = resourceIdValue; - String resourceTypeInput = resourceType; - - String authResourceId = getAuthorizationResourceIdOrFullName(resourceTypeInput, resourceIdInput); - String payLoad = generateMessagePayload(verbInput, authResourceId, resourceTypeInput, headers); - Mac mac = null; - try { - mac = (Mac) this.macInstance.clone(); - } catch (CloneNotSupportedException e) { - throw new IllegalStateException(e); - } + + String authResourceId = getAuthorizationResourceIdOrFullName(resourceType, resourceIdValue); + String payLoad = generateMessagePayload(verb, authResourceId, resourceType, headers); + Mac mac = this.getMacInstance(); byte[] digest = mac.doFinal(payLoad.getBytes()); String authorizationToken = Utils.encodeBase64String(digest); String authtoken = AUTH_PREFIX + authorizationToken; return HttpUtils.urlEncode(authtoken); } + private Mac getMacInstance() { + int masterKeyLatestHashCode = this.cosmosKeyCredential.getMasterKey().hashCode(); + + // Master key has changed, or this is the first time we are getting mac instance + if (masterKeyLatestHashCode != this.masterKeyHashCode) { + byte[] masterKeyBytes = this.cosmosKeyCredential.getMasterKey().getBytes(); + byte[] masterKeyDecodedBytes = Utils.Base64Decoder.decode(masterKeyBytes); + SecretKey signingKey = new SecretKeySpec(masterKeyDecodedBytes, "HMACSHA256"); + try { + Mac macInstance = Mac.getInstance("HMACSHA256"); + macInstance.init(signingKey); + // Update the master key hash code + this.masterKeyHashCode = masterKeyLatestHashCode; + return macInstance; + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new IllegalStateException(e); + } + } else { + // Master key hasn't changed, return the cloned mac instance + try { + return (Mac)this.macInstance.clone(); + } catch (CloneNotSupportedException e) { + throw new IllegalStateException(e); + } + } + } + private String generateMessagePayload(String verb, String resourceId, String resourceType, Map headers) { String xDate = headers.get(HttpConstants.HttpHeaders.X_DATE); diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/RxDocumentClientImpl.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/RxDocumentClientImpl.java index 83ee6301bf10b..b70cc47dfbadf 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/RxDocumentClientImpl.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/RxDocumentClientImpl.java @@ -8,6 +8,7 @@ import com.azure.data.cosmos.ConnectionMode; import com.azure.data.cosmos.ConnectionPolicy; import com.azure.data.cosmos.ConsistencyLevel; +import com.azure.data.cosmos.CosmosKeyCredential; import com.azure.data.cosmos.CosmosResourceType; import com.azure.data.cosmos.FeedOptions; import com.azure.data.cosmos.FeedResponse; @@ -77,6 +78,7 @@ public class RxDocumentClientImpl implements AsyncDocumentClient, IAuthorization private final UserAgentContainer userAgentContainer; private final boolean hasAuthKeyResourceToken; private final Configs configs; + private CosmosKeyCredential cosmosKeyCredential; private TokenResolver tokenResolver; private SessionContainer sessionContainer; private String firstResourceTokenFromPermissionFeed = StringUtils.EMPTY; @@ -116,18 +118,20 @@ public RxDocumentClientImpl(URI serviceEndpoint, ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel, Configs configs, - TokenResolver tokenResolver) { - this(serviceEndpoint, masterKeyOrResourceToken, permissionFeed, connectionPolicy, consistencyLevel, configs); + TokenResolver tokenResolver, + CosmosKeyCredential cosmosKeyCredential) { + this(serviceEndpoint, masterKeyOrResourceToken, permissionFeed, connectionPolicy, consistencyLevel, configs, cosmosKeyCredential); this.tokenResolver = tokenResolver; } - public RxDocumentClientImpl(URI serviceEndpoint, + private RxDocumentClientImpl(URI serviceEndpoint, String masterKeyOrResourceToken, List permissionFeed, ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel, - Configs configs) { - this(serviceEndpoint, masterKeyOrResourceToken, connectionPolicy, consistencyLevel, configs); + Configs configs, + CosmosKeyCredential cosmosKeyCredential) { + this(serviceEndpoint, masterKeyOrResourceToken, connectionPolicy, consistencyLevel, configs, cosmosKeyCredential); if (permissionFeed != null && permissionFeed.size() > 0) { this.resourceTokensMap = new HashMap<>(); for (Permission permission : permissionFeed) { @@ -170,8 +174,8 @@ public RxDocumentClientImpl(URI serviceEndpoint, } } - public RxDocumentClientImpl(URI serviceEndpoint, String masterKeyOrResourceToken, ConnectionPolicy connectionPolicy, - ConsistencyLevel consistencyLevel, Configs configs) { + RxDocumentClientImpl(URI serviceEndpoint, String masterKeyOrResourceToken, ConnectionPolicy connectionPolicy, + ConsistencyLevel consistencyLevel, Configs configs, CosmosKeyCredential cosmosKeyCredential) { logger.info( "Initializing DocumentClient with" @@ -181,13 +185,18 @@ public RxDocumentClientImpl(URI serviceEndpoint, String masterKeyOrResourceToken this.configs = configs; this.masterKeyOrResourceToken = masterKeyOrResourceToken; this.serviceEndpoint = serviceEndpoint; + this.cosmosKeyCredential = cosmosKeyCredential; - if (masterKeyOrResourceToken != null && ResourceTokenAuthorizationHelper.isResourceToken(masterKeyOrResourceToken)) { + if (this.cosmosKeyCredential != null) { + hasAuthKeyResourceToken = false; + this.authorizationTokenProvider = new BaseAuthorizationTokenProvider(this.cosmosKeyCredential); + } else if (masterKeyOrResourceToken != null && ResourceTokenAuthorizationHelper.isResourceToken(masterKeyOrResourceToken)) { this.authorizationTokenProvider = null; hasAuthKeyResourceToken = true; } else if(masterKeyOrResourceToken != null && !ResourceTokenAuthorizationHelper.isResourceToken(masterKeyOrResourceToken)){ + this.cosmosKeyCredential = new CosmosKeyCredential(this.masterKeyOrResourceToken); hasAuthKeyResourceToken = false; - this.authorizationTokenProvider = new BaseAuthorizationTokenProvider(this.masterKeyOrResourceToken); + this.authorizationTokenProvider = new BaseAuthorizationTokenProvider(this.cosmosKeyCredential); } else { hasAuthKeyResourceToken = false; this.authorizationTokenProvider = null; @@ -222,7 +231,7 @@ private void initializeGatewayConfigurationReader() { } else if(!this.hasAuthKeyResourceToken && this.authorizationTokenProvider == null) { resourceToken = this.firstResourceTokenFromPermissionFeed; } else { - assert this.masterKeyOrResourceToken != null; + assert this.masterKeyOrResourceToken != null || this.cosmosKeyCredential != null; resourceToken = this.masterKeyOrResourceToken; } @@ -964,11 +973,9 @@ private Mono getCreateDocumentRequest(String documentC } private void populateHeaders(RxDocumentServiceRequest request, String httpMethod) { - if (this.masterKeyOrResourceToken != null) { - request.getHeaders().put(HttpConstants.HttpHeaders.X_DATE, Utils.nowAsRFC1123()); - } - - if (this.masterKeyOrResourceToken != null || this.resourceTokensMap != null || this.tokenResolver != null) { + request.getHeaders().put(HttpConstants.HttpHeaders.X_DATE, Utils.nowAsRFC1123()); + if (this.masterKeyOrResourceToken != null || this.resourceTokensMap != null + || this.tokenResolver != null || this.cosmosKeyCredential != null) { String resourceName = request.getResourceAddress(); String authorization = this.getUserAuthorizationToken( @@ -1003,7 +1010,7 @@ public String getUserAuthorizationToken(String resourceName, if (this.tokenResolver != null) { return this.tokenResolver.getAuthorizationToken(requestVerb, resourceName, this.resolveCosmosResourceType(resourceType), properties != null ? Collections.unmodifiableMap(properties) : null); - } else if (masterKeyOrResourceToken != null && !hasAuthKeyResourceToken) { + } else if (cosmosKeyCredential != null) { return this.authorizationTokenProvider.generateKeyAuthorizationSignature(requestVerb, resourceName, resourceType, headers); } else if (masterKeyOrResourceToken != null && hasAuthKeyResourceToken && resourceTokensMap == null) { diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/TestConfigurations.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/TestConfigurations.java index 119c3c7b3a771..ac054f5c99be5 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/TestConfigurations.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/TestConfigurations.java @@ -7,7 +7,7 @@ /** * Contains the configurations for tests. - * + * * For running tests, you can pass a customized endpoint configuration in one of the following * ways: *
    @@ -15,7 +15,7 @@ * command-line option. *
  • You can set ACCOUNT_KEY and ACCOUNT_HOST as environment variables.
  • *
- * + * * If none of the above is set, emulator endpoint will be used. */ public final class TestConfigurations { @@ -23,11 +23,17 @@ public final class TestConfigurations { // The default values are credentials of the local emulator, which are not used in any production environment. // public static String MASTER_KEY = - System.getProperty("ACCOUNT_KEY", + System.getProperty("ACCOUNT_KEY", StringUtils.defaultString(Strings.emptyToNull( System.getenv().get("ACCOUNT_KEY")), "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==")); + public static String SECONDARY_MASTER_KEY = + System.getProperty("SECONDARY_ACCOUNT_KEY", + StringUtils.defaultString(Strings.emptyToNull( + System.getenv().get("SECONDARY_ACCOUNT_KEY")), + "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==")); + public static String HOST = System.getProperty("ACCOUNT_HOST", StringUtils.defaultString(Strings.emptyToNull( diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/directconnectivity/GatewayServiceConfigurationReader.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/directconnectivity/GatewayServiceConfigurationReader.java index 25fab9dce714f..a281231933b60 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/directconnectivity/GatewayServiceConfigurationReader.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/directconnectivity/GatewayServiceConfigurationReader.java @@ -107,13 +107,15 @@ private Mono getDatabaseAccountAsync(URI serviceEndpoint) { httpHeaders.set(HttpConstants.HttpHeaders.USER_AGENT, userAgentContainer.getUserAgent()); httpHeaders.set(HttpConstants.HttpHeaders.API_TYPE, Constants.Properties.SQL_API_TYPE); + + String xDate = Utils.nowAsRFC1123(); + httpHeaders.set(HttpConstants.HttpHeaders.X_DATE, xDate); + String authorizationToken; if (this.hasAuthKeyResourceToken || baseAuthorizationTokenProvider == null) { authorizationToken = HttpUtils.urlEncode(this.authKeyResourceToken); } else { // Retrieve the document service properties. - String xDate = Utils.nowAsRFC1123(); - httpHeaders.set(HttpConstants.HttpHeaders.X_DATE, xDate); Map header = new HashMap<>(); header.put(HttpConstants.HttpHeaders.X_DATE, xDate); authorizationToken = baseAuthorizationTokenProvider diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/ClientUnderTestBuilder.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/ClientUnderTestBuilder.java index 2a3e509587159..ae651447b7ef3 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/ClientUnderTestBuilder.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/ClientUnderTestBuilder.java @@ -16,6 +16,7 @@ public ClientUnderTestBuilder(CosmosClientBuilder builder) { this.consistencyLevel(builder.consistencyLevel()); this.key(builder.key()); this.endpoint(builder.endpoint()); + this.cosmosKeyCredential(builder.cosmosKeyCredential()); } @Override @@ -27,7 +28,8 @@ public CosmosClient build() { this.key(), this.connectionPolicy(), this.consistencyLevel(), - this.configs()); + this.configs(), + this.cosmosKeyCredential()); } catch (URISyntaxException e) { throw new IllegalArgumentException(e.getMessage()); } diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/CosmosKeyCredentialTest.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/CosmosKeyCredentialTest.java new file mode 100644 index 0000000000000..99da69019f89e --- /dev/null +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/CosmosKeyCredentialTest.java @@ -0,0 +1,344 @@ +package com.azure.data.cosmos; + +import com.azure.data.cosmos.internal.FailureValidator; +import com.azure.data.cosmos.internal.TestConfigurations; +import com.azure.data.cosmos.rx.TestSuiteBase; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; +import reactor.core.publisher.Mono; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CosmosKeyCredentialTest extends TestSuiteBase { + + private static final int TIMEOUT = 50000; + private static final int SETUP_TIMEOUT = 20000; + private static final int SHUTDOWN_TIMEOUT = 20000; + + private final List databases = new ArrayList<>(); + private final String databaseId = CosmosDatabaseForTest.generateId(); + + private CosmosClient client; + private CosmosDatabase database; + private CosmosContainer container; + + @Factory(dataProvider = "clientBuildersWithDirect") + public CosmosKeyCredentialTest(CosmosClientBuilder clientBuilder) { + super(clientBuilder); + this.subscriberValidationTimeout = TIMEOUT; + } + + @DataProvider(name = "crudArgProvider") + public Object[][] crudArgProvider() { + return new Object[][] { + // collection name, is name base + { UUID.randomUUID().toString()} , + + // with special characters in the name. + {"+ -_,:.|~" + UUID.randomUUID().toString() + " +-_,:.|~"} , + }; + } + + private CosmosContainerProperties getCollectionDefinition(String collectionName) { + PartitionKeyDefinition partitionKeyDef = new PartitionKeyDefinition(); + ArrayList paths = new ArrayList<>(); + paths.add("/mypk"); + partitionKeyDef.paths(paths); + + return new CosmosContainerProperties( + collectionName, + partitionKeyDef); + } + + private CosmosItemProperties getDocumentDefinition(String documentId) { + final String uuid = UUID.randomUUID().toString(); + return new CosmosItemProperties(String.format("{ " + + "\"id\": \"%s\", " + + "\"mypk\": \"%s\", " + + "\"sgmts\": [[6519456, 1471916863], [2498434, 1455671440]]" + + "}" + , documentId, uuid)); + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT, dataProvider = "crudArgProvider") + public void createCollectionWithSecondaryKey(String collectionName) throws InterruptedException { + CosmosContainerProperties collectionDefinition = getCollectionDefinition(collectionName); + + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + + cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + Mono createObservable = database + .createContainer(collectionDefinition); + + CosmosResponseValidator validator = new CosmosResponseValidator.Builder() + .withId(collectionDefinition.id()).build(); + + validateSuccess(createObservable, validator); + + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + safeDeleteAllCollections(database); + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT, dataProvider = "crudArgProvider") + public void readCollectionWithSecondaryKey(String collectionName) throws InterruptedException { + CosmosContainerProperties collectionDefinition = getCollectionDefinition(collectionName); + + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + + Mono createObservable = database.createContainer(collectionDefinition); + CosmosContainer collection = createObservable.block().container(); + + cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + Mono readObservable = collection.read(); + + CosmosResponseValidator validator = + new CosmosResponseValidator.Builder() + .withId(collection.id()).build(); + validateSuccess(readObservable, validator); + + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + safeDeleteAllCollections(database); + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT, dataProvider = "crudArgProvider") + public void deleteCollectionWithSecondaryKey(String collectionName) throws InterruptedException { + CosmosContainerProperties collectionDefinition = getCollectionDefinition(collectionName); + + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + + Mono createObservable = database.createContainer(collectionDefinition); + CosmosContainer collection = createObservable.block().container(); + + cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + Mono deleteObservable = collection.delete(); + + CosmosResponseValidator validator = new CosmosResponseValidator.Builder() + .nullResource().build(); + validateSuccess(deleteObservable, validator); + + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT, dataProvider = "crudArgProvider") + public void replaceCollectionWithSecondaryKey(String collectionName) throws InterruptedException { + // create a collection + CosmosContainerProperties collectionDefinition = getCollectionDefinition(collectionName); + Mono createObservable = database.createContainer(collectionDefinition); + CosmosContainer collection = createObservable.block().container(); + + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + + CosmosContainerProperties collectionSettings = collection.read().block().properties(); + // sanity check + assertThat(collectionSettings.indexingPolicy().indexingMode()).isEqualTo(IndexingMode.CONSISTENT); + + cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + + // replace indexing mode + IndexingPolicy indexingMode = new IndexingPolicy(); + indexingMode.indexingMode(IndexingMode.LAZY); + collectionSettings.indexingPolicy(indexingMode); + Mono readObservable = collection.replace(collectionSettings, new CosmosContainerRequestOptions()); + + // validate + CosmosResponseValidator validator = new CosmosResponseValidator.Builder() + .indexingMode(IndexingMode.LAZY).build(); + validateSuccess(readObservable, validator); + + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + safeDeleteAllCollections(database); + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT, dataProvider = "crudArgProvider") + public void createDocumentWithSecondaryKey(String documentId) throws InterruptedException { + + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + + cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + + CosmosItemProperties properties = getDocumentDefinition(documentId); + Mono createObservable = container.createItem(properties, new CosmosItemRequestOptions()); + + CosmosResponseValidator validator = new CosmosResponseValidator.Builder() + .withId(properties.id()) + .build(); + + validateSuccess(createObservable, validator); + + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT, dataProvider = "crudArgProvider") + public void readDocumentWithSecondaryKey(String documentId) throws InterruptedException { + + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + + cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + + CosmosItemProperties docDefinition = getDocumentDefinition(documentId); + CosmosItem document = container.createItem(docDefinition, new CosmosItemRequestOptions()).block().item(); + + waitIfNeededForReplicasToCatchUp(clientBuilder()); + + CosmosItemRequestOptions options = new CosmosItemRequestOptions(); + options.partitionKey(new PartitionKey(docDefinition.get("mypk"))); + Mono readObservable = document.read(options); + + CosmosResponseValidator validator = new CosmosResponseValidator.Builder() + .withId(document.id()) + .build(); + + validateSuccess(readObservable, validator); + + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT, dataProvider = "crudArgProvider") + public void deleteDocumentWithSecondaryKey(String documentId) throws InterruptedException { + + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + + cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + + CosmosItemProperties docDefinition = getDocumentDefinition(documentId); + + CosmosItem document = container.createItem(docDefinition, new CosmosItemRequestOptions()).block().item(); + + CosmosItemRequestOptions options = new CosmosItemRequestOptions(); + options.partitionKey(new PartitionKey(docDefinition.get("mypk"))); + Mono deleteObservable = document.delete(options); + + + CosmosResponseValidator validator = new CosmosResponseValidator.Builder() + .nullResource().build(); + validateSuccess(deleteObservable, validator); + + // attempt to read document which was deleted + waitIfNeededForReplicasToCatchUp(clientBuilder()); + + Mono readObservable = document.read(options); + FailureValidator notFoundValidator = new FailureValidator.Builder().resourceNotFound().build(); + validateFailure(readObservable, notFoundValidator); + + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT) + public void createDatabaseWithSecondaryKey() throws Exception { + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + + cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + + CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); + databases.add(databaseDefinition.id()); + // create the database + Mono createObservable = client.createDatabase(databaseDefinition, new CosmosDatabaseRequestOptions()); + + // validate + CosmosResponseValidator validator = new CosmosResponseValidator.Builder() + .withId(databaseDefinition.id()).build(); + validateSuccess(createObservable, validator); + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT) + public void readDatabaseWithSecondaryKey() throws Exception { + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + + cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + + // read database + Mono readObservable = client.getDatabase(databaseId).read(); + + // validate + CosmosResponseValidator validator = new CosmosResponseValidator.Builder() + .withId(databaseId).build(); + validateSuccess(readObservable, validator); + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT) + public void deleteDatabaseWithSecondaryKey() throws Exception { + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + + cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + + // create the database + CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); + databases.add(databaseDefinition.id()); + + CosmosDatabase database = client.createDatabase(databaseDefinition, new CosmosDatabaseRequestOptions()).block().database(); + // delete the database + Mono deleteObservable = database.delete(); + + // validate + CosmosResponseValidator validator = new CosmosResponseValidator.Builder() + .nullResource().build(); + validateSuccess(deleteObservable, validator); + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT, + expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Illegal base64 character .*") + public void invalidSecondaryKey() throws Exception { + // sanity check + assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + + cosmosKeyCredential.setMasterKey("Invalid Secondary Key"); + + // create the database, and this should throw Illegal Argument Exception for secondary key + CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); + client.createDatabase(databaseDefinition, new CosmosDatabaseRequestOptions()).block().database(); + } + + @AfterMethod(groups = { "simple" }, timeOut = SETUP_TIMEOUT) + public void afterMethod() { + // Set back master key before every test + cosmosKeyCredential.setMasterKey(TestConfigurations.MASTER_KEY); + } + + @BeforeClass(groups = { "simple" }, timeOut = SETUP_TIMEOUT) + public void beforeClass() { + client = clientBuilder().build(); + database = createDatabase(client, databaseId); + container = getSharedMultiPartitionCosmosContainer(client); + } + + @AfterClass(groups = { "simple" }, timeOut = SHUTDOWN_TIMEOUT, alwaysRun = true) + public void afterClass() { + safeDeleteDatabase(database); + for(String dbId: databases) { + safeDeleteDatabase(client.getDatabase(dbId)); + } + safeClose(client); + } +} diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/CosmosPartitionKeyTests.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/CosmosPartitionKeyTests.java index ddf79725aab89..137816d3096bd 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/CosmosPartitionKeyTests.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/CosmosPartitionKeyTests.java @@ -62,17 +62,17 @@ private void createContainerWithoutPk() throws URISyntaxException, IOException { .withRequestTimeoutInMillis(connectionPolicy.requestTimeoutInMillis()); HttpClient httpClient = HttpClient.createFixed(httpClientConfig); - + // CREATE a non partitioned collection using the rest API and older version String resourceId = Paths.DATABASES_PATH_SEGMENT + "/" + createdDatabase.id(); String path = Paths.DATABASES_PATH_SEGMENT + "/" + createdDatabase.id() + "/" + Paths.COLLECTIONS_PATH_SEGMENT + "/"; DocumentCollection collection = new DocumentCollection(); collection.id(NON_PARTITIONED_CONTAINER_ID); - + HashMap headers = new HashMap(); headers.put(HttpConstants.HttpHeaders.X_DATE, Utils.nowAsRFC1123()); headers.put(HttpConstants.HttpHeaders.VERSION, "2018-09-17"); - BaseAuthorizationTokenProvider base = new BaseAuthorizationTokenProvider(TestConfigurations.MASTER_KEY); + BaseAuthorizationTokenProvider base = new BaseAuthorizationTokenProvider(new CosmosKeyCredential(TestConfigurations.MASTER_KEY)); String authorization = base.generateKeyAuthorizationSignature(HttpConstants.HttpMethods.POST, resourceId, Paths.COLLECTIONS_PATH_SEGMENT, headers); headers.put(HttpConstants.HttpHeaders.AUTHORIZATION, URLEncoder.encode(authorization, "UTF-8")); RxDocumentServiceRequest request = RxDocumentServiceRequest.create(OperationType.Create, @@ -87,7 +87,7 @@ private void createContainerWithoutPk() throws URISyntaxException, IOException { httpRequest.withBody(request.getContent()); String body = httpClient.send(httpRequest).block().bodyAsString().block(); assertThat(body).contains("\"id\":\"" + NON_PARTITIONED_CONTAINER_ID + "\""); - + // CREATE a document in the non partitioned collection using the rest API and older version resourceId = Paths.DATABASES_PATH_SEGMENT + "/" + createdDatabase.id() + "/" + Paths.COLLECTIONS_PATH_SEGMENT + "/" + collection.id(); path = Paths.DATABASES_PATH_SEGMENT + "/" + createdDatabase.id() + "/" + Paths.COLLECTIONS_PATH_SEGMENT diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/RxDocumentClientUnderTest.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/RxDocumentClientUnderTest.java index 1818f37cb8343..300aab9bbfd98 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/RxDocumentClientUnderTest.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/RxDocumentClientUnderTest.java @@ -5,6 +5,7 @@ import com.azure.data.cosmos.ClientUnderTestBuilder; import com.azure.data.cosmos.ConnectionPolicy; import com.azure.data.cosmos.ConsistencyLevel; +import com.azure.data.cosmos.CosmosKeyCredential; import com.azure.data.cosmos.internal.http.HttpClient; import com.azure.data.cosmos.internal.http.HttpRequest; import com.azure.data.cosmos.internal.http.HttpResponse; @@ -34,8 +35,9 @@ public RxDocumentClientUnderTest(URI serviceEndpoint, String masterKey, ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel, - Configs configs) { - super(serviceEndpoint, masterKey, connectionPolicy, consistencyLevel, configs); + Configs configs, + CosmosKeyCredential cosmosKeyCredential) { + super(serviceEndpoint, masterKey, connectionPolicy, consistencyLevel, configs, cosmosKeyCredential); init(); } diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/SpyClientBuilder.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/SpyClientBuilder.java index 3a6a49f462385..bc8e93eea0cf8 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/SpyClientBuilder.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/SpyClientBuilder.java @@ -11,6 +11,7 @@ public SpyClientBuilder(AsyncDocumentClient.Builder builder) { super.desiredConsistencyLevel = builder.desiredConsistencyLevel; super.masterKeyOrResourceToken = builder.masterKeyOrResourceToken; super.serviceEndpoint = builder.serviceEndpoint; + super.cosmosKeyCredential = builder.cosmosKeyCredential; } public SpyClientUnderTestFactory.ClientUnderTest build() { @@ -19,7 +20,7 @@ public SpyClientUnderTestFactory.ClientUnderTest build() { masterKeyOrResourceToken, connectionPolicy, desiredConsistencyLevel, - configs); + configs, cosmosKeyCredential); } public SpyClientUnderTestFactory.ClientWithGatewaySpy buildWithGatewaySpy() { @@ -28,7 +29,7 @@ public SpyClientUnderTestFactory.ClientWithGatewaySpy buildWithGatewaySpy() { masterKeyOrResourceToken, connectionPolicy, desiredConsistencyLevel, - configs); + configs, cosmosKeyCredential); } public SpyClientUnderTestFactory.DirectHttpsClientUnderTest buildWithDirectHttps() { @@ -36,6 +37,6 @@ public SpyClientUnderTestFactory.DirectHttpsClientUnderTest buildWithDirectHttps serviceEndpoint, masterKeyOrResourceToken, connectionPolicy, - desiredConsistencyLevel); + desiredConsistencyLevel, cosmosKeyCredential); } } diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/SpyClientUnderTestFactory.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/SpyClientUnderTestFactory.java index 5fbcb8e508824..faeac5af3da26 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/SpyClientUnderTestFactory.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/SpyClientUnderTestFactory.java @@ -5,6 +5,7 @@ import com.azure.data.cosmos.ConnectionMode; import com.azure.data.cosmos.ConnectionPolicy; import com.azure.data.cosmos.ConsistencyLevel; +import com.azure.data.cosmos.CosmosKeyCredential; import com.azure.data.cosmos.internal.directconnectivity.Protocol; import com.azure.data.cosmos.internal.directconnectivity.ReflectionUtils; import com.azure.data.cosmos.internal.http.HttpClient; @@ -31,12 +32,12 @@ public class SpyClientUnderTestFactory { public static abstract class SpyBaseClass extends RxDocumentClientImpl { - public SpyBaseClass(URI serviceEndpoint, String masterKeyOrResourceToken, ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel, Configs configs) { - super(serviceEndpoint, masterKeyOrResourceToken, connectionPolicy, consistencyLevel, configs); + public SpyBaseClass(URI serviceEndpoint, String masterKeyOrResourceToken, ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel, Configs configs, CosmosKeyCredential cosmosKeyCredential) { + super(serviceEndpoint, masterKeyOrResourceToken, connectionPolicy, consistencyLevel, configs, cosmosKeyCredential); } - + public abstract List getCapturedRequests(); - + public abstract void clearCapturedRequests(); protected static Configs createConfigsSpy(final Protocol protocol) { @@ -45,7 +46,7 @@ protected static Configs createConfigsSpy(final Protocol protocol) { return configs; } } - + public static class ClientWithGatewaySpy extends SpyBaseClass { private RxGatewayStoreModel origRxGatewayStoreModel; @@ -54,8 +55,8 @@ public static class ClientWithGatewaySpy extends SpyBaseClass requests; - ClientWithGatewaySpy(URI serviceEndpoint, String masterKey, ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel, Configs configs) { - super(serviceEndpoint, masterKey, connectionPolicy, consistencyLevel, configs); + ClientWithGatewaySpy(URI serviceEndpoint, String masterKey, ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel, Configs configs, CosmosKeyCredential cosmosKeyCredential) { + super(serviceEndpoint, masterKey, connectionPolicy, consistencyLevel, configs, cosmosKeyCredential); init(); } @@ -116,8 +117,8 @@ public static class ClientUnderTest extends SpyBaseClass { List>> requestsResponsePairs = Collections.synchronizedList(new ArrayList<>()); - ClientUnderTest(URI serviceEndpoint, String masterKey, ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel, Configs configs) { - super(serviceEndpoint, masterKey, connectionPolicy, consistencyLevel, configs); + ClientUnderTest(URI serviceEndpoint, String masterKey, ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel, Configs configs, CosmosKeyCredential cosmosKeyCredential) { + super(serviceEndpoint, masterKey, connectionPolicy, consistencyLevel, configs, cosmosKeyCredential); init(); } @@ -168,8 +169,8 @@ public static class DirectHttpsClientUnderTest extends SpyBaseClass List>> requestsResponsePairs = Collections.synchronizedList(new ArrayList<>()); - DirectHttpsClientUnderTest(URI serviceEndpoint, String masterKey, ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel) { - super(serviceEndpoint, masterKey, connectionPolicy, consistencyLevel, createConfigsSpy(Protocol.HTTPS)); + DirectHttpsClientUnderTest(URI serviceEndpoint, String masterKey, ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel, CosmosKeyCredential cosmosKeyCredential) { + super(serviceEndpoint, masterKey, connectionPolicy, consistencyLevel, createConfigsSpy(Protocol.HTTPS), cosmosKeyCredential); assert connectionPolicy.connectionMode() == ConnectionMode.DIRECT; init(); @@ -219,7 +220,7 @@ public HttpClient getSpyHttpClient() { return spyHttpClient; } } - + public static ClientWithGatewaySpy createClientWithGatewaySpy(AsyncDocumentClient.Builder builder) { return new SpyClientBuilder(builder).buildWithGatewaySpy(); } @@ -228,14 +229,15 @@ public static ClientWithGatewaySpy createClientWithGatewaySpy(URI serviceEndpoin String masterKey, ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel, - Configs configs) { - return new ClientWithGatewaySpy(serviceEndpoint, masterKey, connectionPolicy, consistencyLevel, configs); + Configs configs, + CosmosKeyCredential cosmosKeyCredential) { + return new ClientWithGatewaySpy(serviceEndpoint, masterKey, connectionPolicy, consistencyLevel, configs, cosmosKeyCredential); } public static ClientUnderTest createClientUnderTest(AsyncDocumentClient.Builder builder) { return new SpyClientBuilder(builder).build(); } - + public static DirectHttpsClientUnderTest createDirectHttpsClientUnderTest(AsyncDocumentClient.Builder builder) { return new SpyClientBuilder(builder).buildWithDirectHttps(); } @@ -244,8 +246,9 @@ public static ClientUnderTest createClientUnderTest(URI serviceEndpoint, String masterKey, ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel, - Configs configs) { - return new ClientUnderTest(serviceEndpoint, masterKey, connectionPolicy, consistencyLevel, configs) { + Configs configs, + CosmosKeyCredential cosmosKeyCredential) { + return new ClientUnderTest(serviceEndpoint, masterKey, connectionPolicy, consistencyLevel, configs, cosmosKeyCredential) { @Override RxGatewayStoreModel createRxGatewayProxy(ISessionContainer sessionContainer, @@ -274,7 +277,7 @@ RxGatewayStoreModel createRxGatewayProxy(ISessionContainer sessionContainer, } public static DirectHttpsClientUnderTest createDirectHttpsClientUnderTest(URI serviceEndpoint, String masterKey, - ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel) { - return new DirectHttpsClientUnderTest(serviceEndpoint, masterKey, connectionPolicy, consistencyLevel); + ConnectionPolicy connectionPolicy, ConsistencyLevel consistencyLevel, CosmosKeyCredential cosmosKeyCredential) { + return new DirectHttpsClientUnderTest(serviceEndpoint, masterKey, connectionPolicy, consistencyLevel, cosmosKeyCredential); } } diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/directconnectivity/GatewayServiceConfigurationReaderTest.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/directconnectivity/GatewayServiceConfigurationReaderTest.java index da14e7fb5153b..0401d60021aa5 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/directconnectivity/GatewayServiceConfigurationReaderTest.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/directconnectivity/GatewayServiceConfigurationReaderTest.java @@ -3,6 +3,7 @@ package com.azure.data.cosmos.internal.directconnectivity; +import com.azure.data.cosmos.CosmosKeyCredential; import com.azure.data.cosmos.internal.AsyncDocumentClient; import com.azure.data.cosmos.internal.AsyncDocumentClient.Builder; import com.azure.data.cosmos.BridgeInternal; @@ -56,7 +57,7 @@ public void setup() throws Exception { client = clientBuilder().build(); SpyClientUnderTestFactory.ClientUnderTest clientUnderTest = SpyClientUnderTestFactory.createClientUnderTest(this.clientBuilder()); HttpClient httpClient = clientUnderTest.getSpyHttpClient(); - baseAuthorizationTokenProvider = new BaseAuthorizationTokenProvider(TestConfigurations.MASTER_KEY); + baseAuthorizationTokenProvider = new BaseAuthorizationTokenProvider(new CosmosKeyCredential(TestConfigurations.MASTER_KEY)); connectionPolicy = ConnectionPolicy.defaultPolicy(); mockHttpClient = Mockito.mock(HttpClient.class); mockGatewayServiceConfigurationReader = new GatewayServiceConfigurationReader(new URI(TestConfigurations.HOST), @@ -151,4 +152,4 @@ private HttpResponse getMockResponse(String databaseAccountJson) { Mockito.doReturn(new HttpHeaders()).when(httpResponse).headers(); return httpResponse; } -} \ No newline at end of file +} diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/directconnectivity/RntbdTransportClientTest.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/directconnectivity/RntbdTransportClientTest.java index 9a095f948940e..607d78fe57cbb 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/directconnectivity/RntbdTransportClientTest.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/internal/directconnectivity/RntbdTransportClientTest.java @@ -5,6 +5,7 @@ import com.azure.data.cosmos.ConflictException; import com.azure.data.cosmos.CosmosClientException; +import com.azure.data.cosmos.CosmosKeyCredential; import com.azure.data.cosmos.ForbiddenException; import com.azure.data.cosmos.GoneException; import com.azure.data.cosmos.LockedException; @@ -17,10 +18,6 @@ import com.azure.data.cosmos.RetryWithException; import com.azure.data.cosmos.ServiceUnavailableException; import com.azure.data.cosmos.UnauthorizedException; -import com.azure.data.cosmos.internal.directconnectivity.RntbdTransportClient; -import com.azure.data.cosmos.internal.directconnectivity.ServerProperties; -import com.azure.data.cosmos.internal.directconnectivity.RntbdTransportClient; -import com.azure.data.cosmos.internal.directconnectivity.ServerProperties; import com.azure.data.cosmos.internal.directconnectivity.rntbd.RntbdContext; import com.azure.data.cosmos.internal.directconnectivity.rntbd.RntbdContextNegotiator; import com.azure.data.cosmos.internal.directconnectivity.rntbd.RntbdContextRequest; @@ -611,7 +608,7 @@ public void verifyGoneResponseMapsToGoneException() throws Exception { try (final RntbdTransportClient transportClient = new RntbdTransportClient(options, sslContext)) { final BaseAuthorizationTokenProvider authorizationTokenProvider = new BaseAuthorizationTokenProvider( - RntbdTestConfiguration.AccountKey + new CosmosKeyCredential(RntbdTestConfiguration.AccountKey) ); final URI physicalAddress = new URI("rntbd://" diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/rx/TestSuiteBase.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/rx/TestSuiteBase.java index f20082c85c413..cd83f3d4fb7dd 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/rx/TestSuiteBase.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/rx/TestSuiteBase.java @@ -23,6 +23,7 @@ import com.azure.data.cosmos.CosmosItem; import com.azure.data.cosmos.CosmosItemProperties; import com.azure.data.cosmos.CosmosItemResponse; +import com.azure.data.cosmos.CosmosKeyCredential; import com.azure.data.cosmos.CosmosResponse; import com.azure.data.cosmos.CosmosResponseValidator; import com.azure.data.cosmos.CosmosStoredProcedureRequestOptions; @@ -99,6 +100,8 @@ public class TestSuiteBase extends CosmosClientTest { private static final ImmutableList desiredConsistencies; private static final ImmutableList protocols; + protected static final CosmosKeyCredential cosmosKeyCredential; + protected int subscriberValidationTimeout = TIMEOUT; private static CosmosDatabase SHARED_DATABASE; @@ -113,7 +116,7 @@ public TestSuiteBase(CosmosClientBuilder clientBuilder) { protected static CosmosDatabase getSharedCosmosDatabase(CosmosClient client) { return CosmosBridgeInternal.getCosmosDatabaseWithNewClient(SHARED_DATABASE, client); } - + protected static CosmosContainer getSharedMultiPartitionCosmosContainer(CosmosClient client) { return CosmosBridgeInternal.getCosmosContainerWithNewClient(SHARED_MULTI_PARTITION_COLLECTION, SHARED_DATABASE, client); } @@ -140,6 +143,8 @@ protected static CosmosContainer getSharedSinglePartitionCosmosContainer(CosmosC objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); objectMapper.configure(JsonParser.Feature.ALLOW_TRAILING_COMMA, true); objectMapper.configure(JsonParser.Feature.STRICT_DUPLICATE_DETECTION, true); + + cosmosKeyCredential = new CosmosKeyCredential(TestConfigurations.MASTER_KEY); } protected TestSuiteBase() { @@ -519,7 +524,7 @@ public static void deleteCollectionIfExists(CosmosClient client, String database .flatMap(page -> Flux.fromIterable(page.results())) .collectList() .block(); - + if (!res.isEmpty()) { deleteCollection(database, collectionId); } @@ -803,7 +808,7 @@ public static Object[][] simpleClientBuildersWithDirectHttps() { private static Object[][] simpleClientBuildersWithDirect(Protocol... protocols) { logger.info("Max test consistency to use is [{}]", accountConsistency); List testConsistencies = ImmutableList.of(ConsistencyLevel.EVENTUAL); - + boolean isMultiMasterEnabled = preferredLocations != null && accountConsistency == ConsistencyLevel.SESSION; List cosmosConfigurations = new ArrayList<>(); @@ -876,7 +881,7 @@ static List parseDesiredConsistencies(String consistencies) { static List allEqualOrLowerConsistencies(ConsistencyLevel accountConsistency) { List testConsistencies = new ArrayList<>(); switch (accountConsistency) { - + case STRONG: testConsistencies.add(ConsistencyLevel.STRONG); case BOUNDED_STALENESS: @@ -924,7 +929,7 @@ static protected CosmosClientBuilder createGatewayHouseKeepingDocumentClient() { options.maxRetryWaitTimeInSeconds(SUITE_SETUP_TIMEOUT); connectionPolicy.retryOptions(options); return CosmosClient.builder().endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) + .cosmosKeyCredential(cosmosKeyCredential) .connectionPolicy(connectionPolicy) .consistencyLevel(ConsistencyLevel.SESSION); } @@ -935,7 +940,7 @@ static protected CosmosClientBuilder createGatewayRxDocumentClient(ConsistencyLe connectionPolicy.usingMultipleWriteLocations(multiMasterEnabled); connectionPolicy.preferredLocations(preferredLocations); return CosmosClient.builder().endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) + .cosmosKeyCredential(cosmosKeyCredential) .connectionPolicy(connectionPolicy) .consistencyLevel(consistencyLevel); } @@ -963,7 +968,7 @@ static protected CosmosClientBuilder createDirectRxDocumentClient(ConsistencyLev doAnswer((Answer)invocation -> protocol).when(configs).getProtocol(); CosmosClientBuilder builder = CosmosClient.builder().endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) + .cosmosKeyCredential(cosmosKeyCredential) .connectionPolicy(connectionPolicy) .consistencyLevel(consistencyLevel); diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/rx/TokenResolverTest.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/rx/TokenResolverTest.java index 1425f65fae460..94d8e958a9e2e 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/rx/TokenResolverTest.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/rx/TokenResolverTest.java @@ -44,13 +44,13 @@ public class TokenResolverTest extends TestSuiteBase { private class UserClass { public String userName; public int userId; - + public UserClass(String userName, int userId) { this.userName = userName; this.userId = userId; } } - + private Database createdDatabase; private DocumentCollection createdCollection; private User userWithReadPermission; @@ -296,12 +296,12 @@ public void createAndExecuteSprocWithWritePermission(ConnectionMode connectionMo " }" + " }'" + "}"); - + Flux> createObservable = asyncClientWithTokenResolver.createStoredProcedure(createdCollection.selfLink(), sproc, null); ResourceResponseValidator createSucessValidator = new ResourceResponseValidator.Builder() .withId(sprocId).build(); validateSuccess(createObservable, createSucessValidator); - + RequestOptions options = new RequestOptions(); options.setPartitionKey(new PartitionKey("")); String sprocLink = "dbs/" + createdDatabase.id() + "/colls/" + createdCollection.id() + "/sprocs/" + sprocId; @@ -317,7 +317,7 @@ public void readDocumentsWithAllPermission(ConnectionMode connectionMode) { AsyncDocumentClient asyncClientWithTokenResolver = null; String id1 = UUID.randomUUID().toString(); String id2 = UUID.randomUUID().toString(); - + try { asyncClientWithTokenResolver = buildClient(connectionMode, PermissionMode.ALL); Document document1 = asyncClientWithTokenResolver.createDocument(createdCollection.selfLink(), new Document("{'id': '" + id1 + "'}"), null, false) @@ -330,7 +330,7 @@ public void readDocumentsWithAllPermission(ConnectionMode connectionMode) { expectedIds.add(rid1); expectedIds.add(rid2); String query = "SELECT * FROM r WHERE r._rid=\"" + rid1 + "\" or r._rid=\"" + rid2 + "\""; - + FeedOptions options = new FeedOptions(); options.enableCrossPartitionQuery(true); Flux> queryObservable = asyncClientWithTokenResolver.queryDocuments(createdCollection.selfLink(), query, options); @@ -365,7 +365,7 @@ public void readChangeFeedWithAllPermission(ConnectionMode connectionMode) throw try { asyncClientWithTokenResolver = buildClient(connectionMode, PermissionMode.ALL); OffsetDateTime befTime = OffsetDateTime.now(); - Thread.sleep(1000); + Thread.sleep(1500); document1 = asyncClientWithTokenResolver .createDocument(createdCollection.selfLink(), document1, null, false).single().block() @@ -397,7 +397,7 @@ public void readChangeFeedWithAllPermission(ConnectionMode connectionMode) throw @Test(groups = {"simple"}, dataProvider = "connectionMode", timeOut = TIMEOUT) public void verifyRuntimeExceptionWhenUserModifiesProperties(ConnectionMode connectionMode) { AsyncDocumentClient asyncClientWithTokenResolver = null; - + try { ConnectionPolicy connectionPolicy = new ConnectionPolicy(); connectionPolicy.connectionMode(connectionMode); @@ -412,12 +412,12 @@ public void verifyRuntimeExceptionWhenUserModifiesProperties(ConnectionMode conn options.setProperties(new HashMap()); Flux> readObservable = asyncClientWithTokenResolver.readCollection(createdCollection.selfLink(), options); FailureValidator validator = new FailureValidator.Builder().withRuntimeExceptionClass(UnsupportedOperationException.class).build(); - validateFailure(readObservable, validator); + validateFailure(readObservable, validator); } finally { safeClose(asyncClientWithTokenResolver); } } - + @Test(groups = {"simple"}, dataProvider = "connectionMode", timeOut = TIMEOUT) public void verifyBlockListedUserThrows(ConnectionMode connectionMode) { String field = "user"; @@ -434,7 +434,7 @@ public void verifyBlockListedUserThrows(ConnectionMode connectionMode) { .withConsistencyLevel(ConsistencyLevel.SESSION) .withTokenResolver(getTokenResolverWithBlockList(PermissionMode.READ, field, blockListedUser, errorMessage)) .build(); - + RequestOptions options = new RequestOptions(); HashMap properties = new HashMap(); properties.put(field, blockListedUser); @@ -518,14 +518,14 @@ private TokenResolver getBadTokenResolver() { return null; }; } - + private TokenResolver getTokenResolverWithBlockList(PermissionMode permissionMode, String field, UserClass blockListedUser, String errorMessage) { return (String requestVerb, String resourceIdOrFullName, CosmosResourceType resourceType, Map properties) -> { UserClass currentUser = null; if (properties != null && properties.get(field) != null) { currentUser = (UserClass) properties.get(field); } - + if (resourceType == CosmosResourceType.System) { return readPermission.getToken(); } else if (currentUser != null && @@ -541,4 +541,4 @@ private TokenResolver getTokenResolverWithBlockList(PermissionMode permissionMod } }; } -} \ No newline at end of file +} From b1795142700cb6b07ad8996c2d0d6d85f08d0d8c Mon Sep 17 00:00:00 2001 From: Kushagra Thapar Date: Wed, 7 Aug 2019 10:53:33 -0700 Subject: [PATCH 2/4] Added SECONDARY_ACCOUNT_KEY to tests.yml for CI pipeline to test Cosmos Key Credential feature --- sdk/cosmos/tests.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sdk/cosmos/tests.yml b/sdk/cosmos/tests.yml index 6233b0cef2003..771b32bcbb580 100644 --- a/sdk/cosmos/tests.yml +++ b/sdk/cosmos/tests.yml @@ -14,6 +14,7 @@ jobs: EnvVars: ACCOUNT_HOST: $(java-cosmos-session-singleregion-host) ACCOUNT_KEY: $(java-cosmos-session-singleregion-key) + SECONDARY_ACCOUNT_KEY: $(java-cosmos-session-singleregion-secondary-key) MaxParallel: 1 Matrix: # 59m 59s, timed out @@ -65,6 +66,7 @@ jobs: EnvVars: ACCOUNT_HOST: $(java-cosmos-session-singleregion-one-host) ACCOUNT_KEY: $(java-cosmos-session-singleregion-one-key) + SECONDARY_ACCOUNT_KEY: $(java-cosmos-session-singleregion-one-secondary-key) DESIRED_CONSISTENCIES: '["Session"]' PROTOCOLS: '["Tcp"]' MaxParallel: 1 @@ -126,6 +128,7 @@ jobs: EnvVars: ACCOUNT_HOST: $(java-cosmos-strong-singleregion-host) ACCOUNT_KEY: $(java-cosmos-strong-singleregion-key) + SECONDARY_ACCOUNT_KEY: $(java-cosmos-strong-singleregion-secondary-key) MaxParallel: 1 Matrix: # 09m 32s, expect passed @@ -179,6 +182,7 @@ jobs: EnvVars: ACCOUNT_HOST: $(java-cosmos-strong-singleregion-host) ACCOUNT_KEY: $(java-cosmos-strong-singleregion-key) + SECONDARY_ACCOUNT_KEY: $(java-cosmos-strong-singleregion-secondary-key) MaxParallel: 1 Matrix: # 59m 58s, timed out @@ -239,6 +243,7 @@ jobs: EnvVars: ACCOUNT_HOST: $(java-cosmos-multimaster-multiregion-host) ACCOUNT_KEY: $(java-cosmos-multimaster-multiregion-key) + SECONDARY_ACCOUNT_KEY: $(java-cosmos-multimaster-multiregion-secondary-key) DESIRED_CONSISTENCIES: '["Session"]' PREFERRED_LOCATIONS: '["East US 2"]' MaxParallel: 1 @@ -293,6 +298,7 @@ jobs: EnvVars: ACCOUNT_HOST: $(java-cosmos-multimaster-singleregion-host) ACCOUNT_KEY: $(java-cosmos-multimaster-singleregion-key) + SECONDARY_ACCOUNT_KEY: $(java-cosmos-multimaster-singleregion-secondary-key) DESIRED_CONSISTENCIES: '["Session"]' PREFERRED_LOCATIONS: null MaxParallel: 1 From b83391fa4d14ab00ccc48d5e3dadd747807dee90 Mon Sep 17 00:00:00 2001 From: Kushagra Thapar Date: Wed, 7 Aug 2019 16:38:34 -0700 Subject: [PATCH 3/4] Code review changes --- .../data/cosmos/CosmosBridgeInternal.java | 6 +- .../com/azure/data/cosmos/CosmosClient.java | 6 +- .../data/cosmos/CosmosClientBuilder.java | 2 +- .../com/azure/data/cosmos/CosmosDatabase.java | 8 +-- .../data/cosmos/CosmosKeyCredential.java | 24 +++++-- .../cosmos/internal/AsyncDocumentClient.java | 8 +-- .../BaseAuthorizationTokenProvider.java | 8 ++- .../data/cosmos/CosmosKeyCredentialTest.java | 66 +++++++++---------- 8 files changed, 68 insertions(+), 60 deletions(-) diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosBridgeInternal.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosBridgeInternal.java index 54a73efcad149..4c48a5bd20655 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosBridgeInternal.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosBridgeInternal.java @@ -12,7 +12,7 @@ * DO NOT USE. For internal use only by the SDK. These methods might break at any time. No support will be provided. */ public class CosmosBridgeInternal { - + public static DocumentCollection toDocumentCollection(CosmosContainerProperties cosmosContainerProperties) { return new DocumentCollection(cosmosContainerProperties.toJson()); } @@ -20,11 +20,11 @@ public static DocumentCollection toDocumentCollection(CosmosContainerProperties public static AsyncDocumentClient getAsyncDocumentClient(CosmosClient client) { return client.getDocClientWrapper(); } - + public static CosmosDatabase getCosmosDatabaseWithNewClient(CosmosDatabase cosmosDatabase, CosmosClient client) { return new CosmosDatabase(cosmosDatabase.id(), client); } - + public static CosmosContainer getCosmosContainerWithNewClient(CosmosContainer cosmosContainer, CosmosDatabase cosmosDatabase, CosmosClient client) { return new CosmosContainer(cosmosContainer.id(), CosmosBridgeInternal.getCosmosDatabaseWithNewClient(cosmosDatabase, client)); } diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClient.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClient.java index 7782517d01dff..cafe847f1b5b2 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClient.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClient.java @@ -128,7 +128,7 @@ TokenResolver getTokenResolver() { * Gets the cosmos key credential * @return cosmos key credential */ - CosmosKeyCredential getCosmosKeyCredential() { + CosmosKeyCredential cosmosKeyCredential() { return cosmosKeyCredential; } @@ -296,7 +296,7 @@ public Mono createDatabase(String id, int throughput) { */ public Flux> readAllDatabases(FeedOptions options) { return getDocClientWrapper().readDatabases(options) - .map(response-> BridgeInternal.createFeedResponse(CosmosDatabaseProperties.getFromV2Results(response.results()), + .map(response-> BridgeInternal.createFeedResponse(CosmosDatabaseProperties.getFromV2Results(response.results()), response.responseHeaders())); } @@ -342,7 +342,7 @@ public Flux> queryDatabases(String query, */ public Flux> queryDatabases(SqlQuerySpec querySpec, FeedOptions options){ return getDocClientWrapper().queryDatabases(querySpec, options) - .map(response-> BridgeInternal.createFeedResponse( + .map(response-> BridgeInternal.createFeedResponse( CosmosDatabaseProperties.getFromV2Results(response.results()), response.responseHeaders())); } diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClientBuilder.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClientBuilder.java index baf7369c0c666..8ecbe528ec05d 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClientBuilder.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClientBuilder.java @@ -203,7 +203,7 @@ public CosmosClient build() { this.keyOrResourceToken == null && (permissions == null || permissions.isEmpty()) && this.tokenResolver == null && this.cosmosKeyCredential == null, "cannot build client without any one of key, resource token, permissions, token resolver, and cosmos key credential"); - ifThrowIllegalArgException(cosmosKeyCredential != null && StringUtils.isEmpty(cosmosKeyCredential.getMasterKey()), + ifThrowIllegalArgException(cosmosKeyCredential != null && StringUtils.isEmpty(cosmosKeyCredential.key()), "cannot build client without key credential"); return new CosmosClient(this); diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosDatabase.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosDatabase.java index 286066c0929bb..0b8ce7d03e1be 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosDatabase.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosDatabase.java @@ -26,7 +26,7 @@ public class CosmosDatabase { /** * Get the id of the CosmosDatabase - * + * * @return the id of the CosmosDatabase */ public String id() { @@ -35,7 +35,7 @@ public String id() { /** * Set the id of the CosmosDatabase - * + * * @param id the id of the CosmosDatabase * @return the same CosmosConflict that had the id set */ @@ -415,7 +415,7 @@ public Flux> queryContainers(SqlQuerySpe /** * Gets a CosmosContainer object without making a service call - * + * * @param id id of the container * @return Cosmos Container */ @@ -622,4 +622,4 @@ String getLink() { builder.append(id()); return builder.toString(); } -} \ No newline at end of file +} diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosKeyCredential.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosKeyCredential.java index 2a99143783944..f47c891a83e38 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosKeyCredential.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosKeyCredential.java @@ -9,17 +9,27 @@ */ public class CosmosKeyCredential { - private String masterKey; + private String key; - public CosmosKeyCredential(String masterKey) { - this.masterKey = masterKey; + // Stores key's hashcode for performance improvements + private int keyHashCode; + + public CosmosKeyCredential(String key) { + this.key = key; + this.keyHashCode = key.hashCode(); + } + + public String key() { + return key; } - public String getMasterKey() { - return masterKey; + public CosmosKeyCredential key(String key) { + this.key = key; + this.keyHashCode = key.hashCode(); + return this; } - public void setMasterKey(String masterKey) { - this.masterKey = masterKey; + public int keyHashCode() { + return this.keyHashCode; } } diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/AsyncDocumentClient.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/AsyncDocumentClient.java index 63508a65c1928..0a0d95eb6c413 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/AsyncDocumentClient.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/AsyncDocumentClient.java @@ -138,7 +138,7 @@ public Builder withConnectionPolicy(ConnectionPolicy connectionPolicy) { } public Builder withCosmosKeyCredential(CosmosKeyCredential cosmosKeyCredential) { - if (cosmosKeyCredential != null && StringUtils.isEmpty(cosmosKeyCredential.getMasterKey())) { + if (cosmosKeyCredential != null && StringUtils.isEmpty(cosmosKeyCredential.key())) { throw new IllegalArgumentException("Cannot build client with empty key credential"); } this.cosmosKeyCredential = cosmosKeyCredential; @@ -170,7 +170,7 @@ public AsyncDocumentClient build() { && this.tokenResolver == null && this.cosmosKeyCredential == null, "cannot build client without any one of masterKey, " + "resource token, permissionFeed, tokenResolver and cosmos key credential"); - ifThrowIllegalArgException(cosmosKeyCredential != null && StringUtils.isEmpty(cosmosKeyCredential.getMasterKey()), + ifThrowIllegalArgException(cosmosKeyCredential != null && StringUtils.isEmpty(cosmosKeyCredential.key()), "cannot build client without key credential"); RxDocumentClientImpl client = new RxDocumentClientImpl(serviceEndpoint, @@ -244,10 +244,6 @@ public void setTokenResolver(TokenResolver tokenResolver) { public CosmosKeyCredential getCosmosKeyCredential() { return cosmosKeyCredential; } - - public void setCosmosKeyCredential(CosmosKeyCredential cosmosKeyCredential) { - this.cosmosKeyCredential = cosmosKeyCredential; - } } /** diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/BaseAuthorizationTokenProvider.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/BaseAuthorizationTokenProvider.java index 2e51f7c9e0efa..dbab2969155aa 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/BaseAuthorizationTokenProvider.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/BaseAuthorizationTokenProvider.java @@ -27,6 +27,8 @@ public class BaseAuthorizationTokenProvider implements AuthorizationTokenProvide private static final String AUTH_PREFIX = "type=master&ver=1.0&sig="; private final CosmosKeyCredential cosmosKeyCredential; private Mac macInstance; + + // stores current master key's hashcode for performance reasons. private int masterKeyHashCode; public BaseAuthorizationTokenProvider(CosmosKeyCredential cosmosKeyCredential) { @@ -115,7 +117,7 @@ public String generateKeyAuthorizationSignature(String verb, throw new IllegalArgumentException("headers"); } - if (StringUtils.isEmpty(this.cosmosKeyCredential.getMasterKey())) { + if (StringUtils.isEmpty(this.cosmosKeyCredential.key())) { throw new IllegalArgumentException("key credentials cannot be empty"); } @@ -241,11 +243,11 @@ private String generateKeyAuthorizationSignatureNew(String verb, String resource } private Mac getMacInstance() { - int masterKeyLatestHashCode = this.cosmosKeyCredential.getMasterKey().hashCode(); + int masterKeyLatestHashCode = this.cosmosKeyCredential.keyHashCode(); // Master key has changed, or this is the first time we are getting mac instance if (masterKeyLatestHashCode != this.masterKeyHashCode) { - byte[] masterKeyBytes = this.cosmosKeyCredential.getMasterKey().getBytes(); + byte[] masterKeyBytes = this.cosmosKeyCredential.key().getBytes(); byte[] masterKeyDecodedBytes = Utils.Base64Decoder.decode(masterKeyBytes); SecretKey signingKey = new SecretKeySpec(masterKeyDecodedBytes, "HMACSHA256"); try { diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/CosmosKeyCredentialTest.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/CosmosKeyCredentialTest.java index 99da69019f89e..ab9d4957f5a65 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/CosmosKeyCredentialTest.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/CosmosKeyCredentialTest.java @@ -73,9 +73,9 @@ public void createCollectionWithSecondaryKey(String collectionName) throws Inter CosmosContainerProperties collectionDefinition = getCollectionDefinition(collectionName); // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.MASTER_KEY); - cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + cosmosKeyCredential.key(TestConfigurations.SECONDARY_MASTER_KEY); Mono createObservable = database .createContainer(collectionDefinition); @@ -85,7 +85,7 @@ public void createCollectionWithSecondaryKey(String collectionName) throws Inter validateSuccess(createObservable, validator); // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); safeDeleteAllCollections(database); } @@ -94,12 +94,12 @@ public void readCollectionWithSecondaryKey(String collectionName) throws Interru CosmosContainerProperties collectionDefinition = getCollectionDefinition(collectionName); // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.MASTER_KEY); Mono createObservable = database.createContainer(collectionDefinition); CosmosContainer collection = createObservable.block().container(); - cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + cosmosKeyCredential.key(TestConfigurations.SECONDARY_MASTER_KEY); Mono readObservable = collection.read(); CosmosResponseValidator validator = @@ -108,7 +108,7 @@ public void readCollectionWithSecondaryKey(String collectionName) throws Interru validateSuccess(readObservable, validator); // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); safeDeleteAllCollections(database); } @@ -117,12 +117,12 @@ public void deleteCollectionWithSecondaryKey(String collectionName) throws Inter CosmosContainerProperties collectionDefinition = getCollectionDefinition(collectionName); // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.MASTER_KEY); Mono createObservable = database.createContainer(collectionDefinition); CosmosContainer collection = createObservable.block().container(); - cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + cosmosKeyCredential.key(TestConfigurations.SECONDARY_MASTER_KEY); Mono deleteObservable = collection.delete(); CosmosResponseValidator validator = new CosmosResponseValidator.Builder() @@ -130,7 +130,7 @@ public void deleteCollectionWithSecondaryKey(String collectionName) throws Inter validateSuccess(deleteObservable, validator); // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); } @Test(groups = { "simple" }, timeOut = TIMEOUT, dataProvider = "crudArgProvider") @@ -141,13 +141,13 @@ public void replaceCollectionWithSecondaryKey(String collectionName) throws Inte CosmosContainer collection = createObservable.block().container(); // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.MASTER_KEY); CosmosContainerProperties collectionSettings = collection.read().block().properties(); // sanity check assertThat(collectionSettings.indexingPolicy().indexingMode()).isEqualTo(IndexingMode.CONSISTENT); - cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + cosmosKeyCredential.key(TestConfigurations.SECONDARY_MASTER_KEY); // replace indexing mode IndexingPolicy indexingMode = new IndexingPolicy(); @@ -161,7 +161,7 @@ public void replaceCollectionWithSecondaryKey(String collectionName) throws Inte validateSuccess(readObservable, validator); // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); safeDeleteAllCollections(database); } @@ -169,9 +169,9 @@ public void replaceCollectionWithSecondaryKey(String collectionName) throws Inte public void createDocumentWithSecondaryKey(String documentId) throws InterruptedException { // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.MASTER_KEY); - cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + cosmosKeyCredential.key(TestConfigurations.SECONDARY_MASTER_KEY); CosmosItemProperties properties = getDocumentDefinition(documentId); Mono createObservable = container.createItem(properties, new CosmosItemRequestOptions()); @@ -183,16 +183,16 @@ public void createDocumentWithSecondaryKey(String documentId) throws Interrupted validateSuccess(createObservable, validator); // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); } @Test(groups = { "simple" }, timeOut = TIMEOUT, dataProvider = "crudArgProvider") public void readDocumentWithSecondaryKey(String documentId) throws InterruptedException { // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.MASTER_KEY); - cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + cosmosKeyCredential.key(TestConfigurations.SECONDARY_MASTER_KEY); CosmosItemProperties docDefinition = getDocumentDefinition(documentId); CosmosItem document = container.createItem(docDefinition, new CosmosItemRequestOptions()).block().item(); @@ -210,16 +210,16 @@ public void readDocumentWithSecondaryKey(String documentId) throws InterruptedEx validateSuccess(readObservable, validator); // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); } @Test(groups = { "simple" }, timeOut = TIMEOUT, dataProvider = "crudArgProvider") public void deleteDocumentWithSecondaryKey(String documentId) throws InterruptedException { // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.MASTER_KEY); - cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + cosmosKeyCredential.key(TestConfigurations.SECONDARY_MASTER_KEY); CosmosItemProperties docDefinition = getDocumentDefinition(documentId); @@ -242,15 +242,15 @@ public void deleteDocumentWithSecondaryKey(String documentId) throws Interrupted validateFailure(readObservable, notFoundValidator); // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); } @Test(groups = { "simple" }, timeOut = TIMEOUT) public void createDatabaseWithSecondaryKey() throws Exception { // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.MASTER_KEY); - cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + cosmosKeyCredential.key(TestConfigurations.SECONDARY_MASTER_KEY); CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); databases.add(databaseDefinition.id()); @@ -262,15 +262,15 @@ public void createDatabaseWithSecondaryKey() throws Exception { .withId(databaseDefinition.id()).build(); validateSuccess(createObservable, validator); // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); } @Test(groups = { "simple" }, timeOut = TIMEOUT) public void readDatabaseWithSecondaryKey() throws Exception { // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.MASTER_KEY); - cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + cosmosKeyCredential.key(TestConfigurations.SECONDARY_MASTER_KEY); // read database Mono readObservable = client.getDatabase(databaseId).read(); @@ -280,15 +280,15 @@ public void readDatabaseWithSecondaryKey() throws Exception { .withId(databaseId).build(); validateSuccess(readObservable, validator); // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); } @Test(groups = { "simple" }, timeOut = TIMEOUT) public void deleteDatabaseWithSecondaryKey() throws Exception { // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.MASTER_KEY); - cosmosKeyCredential.setMasterKey(TestConfigurations.SECONDARY_MASTER_KEY); + cosmosKeyCredential.key(TestConfigurations.SECONDARY_MASTER_KEY); // create the database CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); @@ -303,7 +303,7 @@ public void deleteDatabaseWithSecondaryKey() throws Exception { .nullResource().build(); validateSuccess(deleteObservable, validator); // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.SECONDARY_MASTER_KEY); } @Test(groups = { "simple" }, timeOut = TIMEOUT, @@ -311,9 +311,9 @@ public void deleteDatabaseWithSecondaryKey() throws Exception { expectedExceptionsMessageRegExp = "Illegal base64 character .*") public void invalidSecondaryKey() throws Exception { // sanity check - assertThat(client.getCosmosKeyCredential().getMasterKey()).isEqualTo(TestConfigurations.MASTER_KEY); + assertThat(client.cosmosKeyCredential().key()).isEqualTo(TestConfigurations.MASTER_KEY); - cosmosKeyCredential.setMasterKey("Invalid Secondary Key"); + cosmosKeyCredential.key("Invalid Secondary Key"); // create the database, and this should throw Illegal Argument Exception for secondary key CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); @@ -323,7 +323,7 @@ public void invalidSecondaryKey() throws Exception { @AfterMethod(groups = { "simple" }, timeOut = SETUP_TIMEOUT) public void afterMethod() { // Set back master key before every test - cosmosKeyCredential.setMasterKey(TestConfigurations.MASTER_KEY); + cosmosKeyCredential.key(TestConfigurations.MASTER_KEY); } @BeforeClass(groups = { "simple" }, timeOut = SETUP_TIMEOUT) From 4860536d8a67258bb1f966fd4591e62a697b7709 Mon Sep 17 00:00:00 2001 From: Kushagra Thapar Date: Wed, 7 Aug 2019 17:34:43 -0700 Subject: [PATCH 4/4] Java doc additions, and code review comments --- .../azure/data/cosmos/CosmosKeyCredential.java | 16 +++++++++++++++- .../internal/BaseAuthorizationTokenProvider.java | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosKeyCredential.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosKeyCredential.java index f47c891a83e38..6ada00a4db5b0 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosKeyCredential.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosKeyCredential.java @@ -5,7 +5,8 @@ /** * Cosmos Key Credential is used to store key credentials, in order to support dynamic key rotation. * Singleton instance should be used to support multiple keys. - * SDK insures to use the updated key provided in the same singleton instance which was used when building {@link CosmosClient} + * Azure client library for Cosmos ensures to use the updated key provided in the same singleton instance + * which was used when building {@link CosmosClient} */ public class CosmosKeyCredential { @@ -19,16 +20,29 @@ public CosmosKeyCredential(String key) { this.keyHashCode = key.hashCode(); } + /** + * Returns the key stored in Cosmos Key Credential + * @return key + */ public String key() { return key; } + /** + * Sets the key to be used in CosmosKeyCredential + * @param key key to be used in CosmosKeyCredential + * @return current CosmosKeyCredential + */ public CosmosKeyCredential key(String key) { this.key = key; this.keyHashCode = key.hashCode(); return this; } + /** + * CosmosKeyCredential stores the computed hashcode of the key for performance improvements. + * @return hashcode of the key + */ public int keyHashCode() { return this.keyHashCode; } diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/BaseAuthorizationTokenProvider.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/BaseAuthorizationTokenProvider.java index dbab2969155aa..51ea66597f35c 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/BaseAuthorizationTokenProvider.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/internal/BaseAuthorizationTokenProvider.java @@ -26,7 +26,7 @@ public class BaseAuthorizationTokenProvider implements AuthorizationTokenProvide private static final String AUTH_PREFIX = "type=master&ver=1.0&sig="; private final CosmosKeyCredential cosmosKeyCredential; - private Mac macInstance; + private final Mac macInstance; // stores current master key's hashcode for performance reasons. private int masterKeyHashCode;