From be6d68736fe83f92860f86073f0a815eda230001 Mon Sep 17 00:00:00 2001 From: milismsft-mac Date: Wed, 27 Jan 2021 20:48:34 -0800 Subject: [PATCH 01/11] add initial implementation to generate SAS tokens --- .../azure/cosmos/implementation/Paths.java | 2 +- .../ControlPlanePermissionScope.java | 116 ++++ .../sastokens/DataPlanePermissionScope.java | 134 ++++ .../sastokens/PermissionScopeValues.java | 111 ++++ .../sastokens/SasTokenImpl.java | 599 ++++++++++++++++++ .../SasTokenPartitionKeyValueRangeImpl.java | 59 ++ .../SasTokenPartitionKeyValueRange.java | 53 ++ .../cosmos/models/SasTokenPermissionKind.java | 69 ++ .../cosmos/models/SasTokenProperties.java | 272 ++++++++ .../sastokens/SasTokenTests.java | 55 ++ 10 files changed, 1469 insertions(+), 1 deletion(-) create mode 100644 sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/ControlPlanePermissionScope.java create mode 100644 sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/DataPlanePermissionScope.java create mode 100644 sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/PermissionScopeValues.java create mode 100644 sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java create mode 100644 sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenPartitionKeyValueRangeImpl.java create mode 100644 sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPartitionKeyValueRange.java create mode 100644 sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPermissionKind.java create mode 100644 sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenProperties.java create mode 100644 sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Paths.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Paths.java index 1a7e8cdb207b..352b25d7c327 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Paths.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Paths.java @@ -7,7 +7,7 @@ * Used internally. Contains string constants to work with the paths in the Azure Cosmos DB database service. */ public class Paths { - static final String ROOT = "/"; + public static final String ROOT = "/"; static final char ROOT_CHAR = '/'; static final char ESCAPE_CHAR = '\\'; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/ControlPlanePermissionScope.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/ControlPlanePermissionScope.java new file mode 100644 index 000000000000..6e1d7e22af51 --- /dev/null +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/ControlPlanePermissionScope.java @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.cosmos.implementation.sastokens; + +import java.util.Locale; + +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_ACCOUNT_CREATE_DATABASES_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_ACCOUNT_DELETE_DATABASES_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_ACCOUNT_LIST_DATABASES_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_ACCOUNT_READ_ALL_ACCESS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_ACCOUNT_READ_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_ACCOUNT_WRITE_ALL_ACCESS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINERS_READ_ALL_ACCESS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINERS_WRITE_ALL_ACCESS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_DELETE_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_READ_OFFER_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_READ_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_REPLACE_OFFER_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_REPLACE_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_DATABASE_CREATE_CONTAINERS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_DATABASE_DELETE_CONTAINERS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_DATABASE_DELETE_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_DATABASE_LIST_CONTAINERS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_DATABASE_READ_ALL_ACCESS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_DATABASE_READ_OFFER_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_DATABASE_READ_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_DATABASE_REPLACE_OFFER_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_DATABASE_WRITE_ALL_ACCESS_VALUE; + +/** + * Represents permission scope settings applicable to control plane related operations. + */ +public enum ControlPlanePermissionScope { + // REQUIRED: This enum must be kept in sync with the ControlPlanePermissionScope enum in backend services. + + /** + * Cosmos account read scope. + */ + SCOPE_ACCOUNT_READ("AccountRead", SCOPE_ACCOUNT_READ_VALUE), + SCOPE_ACCOUNT_LIST_DATABASES("AccountListDatabases", SCOPE_ACCOUNT_LIST_DATABASES_VALUE), + + /** + * Cosmos database read scope. + */ + SCOPE_DATABASE_READ("DatabaseRead", SCOPE_DATABASE_READ_VALUE), + SCOPE_DATABASE_READ_OFFER("DatabaseReadOffer", SCOPE_DATABASE_READ_OFFER_VALUE), + SCOPE_DATABASE_LIST_CONTAINERS("DatabaseListContainers", SCOPE_DATABASE_LIST_CONTAINERS_VALUE), + + /** + * Cosmos Container read scope. + */ + SCOPE_CONTAINER_READ("ContainerRead", SCOPE_CONTAINER_READ_VALUE), + SCOPE_CONTAINER_READ_OFFER("ContainerReadOffer", SCOPE_CONTAINER_READ_OFFER_VALUE), + + /** + * Composite read scopes. + */ + SCOPE_ACCOUNT_READ_ALL_ACCESS("AccountReadAllAccess", SCOPE_ACCOUNT_READ_ALL_ACCESS_VALUE), + SCOPE_DATABASE_READ_ALL_ACCESS("DatabaseReadAllAccess", SCOPE_DATABASE_READ_ALL_ACCESS_VALUE), + SCOPE_CONTAINER_READ_ALL_ACCESS("ContainersReadAllAccess", SCOPE_CONTAINERS_READ_ALL_ACCESS_VALUE), + + /** + * Cosmos account write scope. + */ + SCOPE_ACCOUNT_CREATE_DATABASES("AccountCreateDatabases", SCOPE_ACCOUNT_CREATE_DATABASES_VALUE), + SCOPE_ACCOUNT_DELETE_DATABASES("AccountDeleteDatabases", SCOPE_ACCOUNT_DELETE_DATABASES_VALUE), + + /** + * Cosmos database write scope. + */ + SCOPE_DATABASE_DELETE("DatabaseDelete", SCOPE_DATABASE_DELETE_VALUE), + SCOPE_DATABASE_REPLACE_OFFER("DatabaseReplaceOffer", SCOPE_DATABASE_REPLACE_OFFER_VALUE), + SCOPE_DATABASE_CREATE_CONTAINERS("DatabaseCreateContainers", SCOPE_DATABASE_CREATE_CONTAINERS_VALUE), + SCOPE_DATABASE_DELETE_CONTAINERS("DatabaseDeleteContainers", SCOPE_DATABASE_DELETE_CONTAINERS_VALUE), + + /** + * Cosmos Container write scope. + */ + SCOPE_CONTAINER_REPLACE("ContainerReplace", SCOPE_CONTAINER_REPLACE_VALUE), + SCOPE_CONTAINER_DELETE("ContainerDelete", SCOPE_CONTAINER_DELETE_VALUE), + SCOPE_CONTAINER_REPLACE_OFFER("ContainerReplaceOffer", SCOPE_CONTAINER_REPLACE_OFFER_VALUE), + + /** + * Composite write scopes. + */ + SCOPE_ACCOUNT_WRITE_ALL_ACCESS("AccountFullAllAccess", SCOPE_ACCOUNT_WRITE_ALL_ACCESS_VALUE), + SCOPE_DATABASE_WRITE_ALL_ACCESS("DatabaseWriteAllAccess", SCOPE_DATABASE_WRITE_ALL_ACCESS_VALUE), + SCOPE_CONTAINER_WRITE_ALL_ACCESS("ContainersWriteAllAccess", SCOPE_CONTAINERS_WRITE_ALL_ACCESS_VALUE), + + NONE("None", (short) 0x0); + + + private final short value; + private final String stringValue; + private final String toLowerStringValue; + + ControlPlanePermissionScope(String stringValue, short scopeBitMask) { + this.stringValue = stringValue; + this.toLowerStringValue = stringValue.toLowerCase(Locale.ROOT); + this.value = scopeBitMask; + } + + @Override + public String toString() { + return this.stringValue; + } + + public String toLowerCase() { + return this.toLowerStringValue; + } + + public short value() { + return this.value; + } +} diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/DataPlanePermissionScope.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/DataPlanePermissionScope.java new file mode 100644 index 000000000000..27667fd9cd06 --- /dev/null +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/DataPlanePermissionScope.java @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.cosmos.implementation.sastokens; + +import java.util.Locale; + +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_CREATE_ITEMS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_CREATE_STORED_PROCEDURES_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_CREATE_TRIGGERS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_CREATE_USER_DEFINED_FUNCTIONS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_DELETE_CONFLICTS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_DELETE_ITEMS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_DELETE_STORED_PROCEDURES_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_DELETE_TRIGGERS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_DELETE_USER_DEFINED_FUNCTIONS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_EXECUTE_QUERIES_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_EXECUTE_STORED_PROCEDURES_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_READ_ALL_ACCESS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_READ_CONFLICTS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_READ_FEEDS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_READ_STORED_PROCEDURES_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_READ_TRIGGERS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_READ_USER_DEFINED_FUNCTIONS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_REPLACE_ITEMS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_REPLACE_STORED_PROCEDURES_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_REPLACE_TRIGGERS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_REPLACE_USER_DEFINED_FUNCTIONS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_UPSERT_ITEMS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_CONTAINER_WRITE_ALL_ACCESS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_ITEM_DELETE_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_ITEM_READ_ALL_ACCESS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_ITEM_READ_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_ITEM_REPLACE_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_ITEM_UPSERT_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_ITEM_WRITE_ALL_ACCESS_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_STORED_PROCEDURE_DELETE_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_STORED_PROCEDURE_EXECUTE_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_STORED_PROCEDURE_READ_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_STORED_PROCEDURE_REPLACE_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_TRIGGER_DELETE_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_TRIGGER_READ_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_TRIGGER_REPLACE_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_USER_DEFINED_FUNCTION_DELETE_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_USER_DEFINED_FUNCTION_READ_VALUE; +import static com.azure.cosmos.implementation.sastokens.PermissionScopeValues.SCOPE_USER_DEFINED_FUNCTION_REPLACE_VALUE; + +/** + * Represents permission scope settings applicable to data plane related operations. + */ +public enum DataPlanePermissionScope { + // REQUIRED: This enum must be kept in sync with the DataPlanePermissionScope enum in backend services. + + /** + * Cosmos Container read scope. + */ + SCOPE_CONTAINER_EXECUTE_QUERIES("ContainerExecuteQueriesFeeds", SCOPE_CONTAINER_EXECUTE_QUERIES_VALUE), + SCOPE_CONTAINER_READ_FEEDS("ContainerReadFeeds", SCOPE_CONTAINER_READ_FEEDS_VALUE), + SCOPE_CONTAINER_READ_STORED_PROCEDURES("ContainerReadStoredProcedures", SCOPE_CONTAINER_READ_STORED_PROCEDURES_VALUE), + SCOPE_CONTAINER_READ_USER_DEFINED_FUNCTIONS("ContainerUserDefinedFunctions", SCOPE_CONTAINER_READ_USER_DEFINED_FUNCTIONS_VALUE), + SCOPE_CONTAINER_READ_TRIGGERS("ContainerReadTriggers", SCOPE_CONTAINER_READ_TRIGGERS_VALUE), + SCOPE_CONTAINER_READ_CONFLICTS("ContainerReadConflicts", SCOPE_CONTAINER_READ_CONFLICTS_VALUE), + SCOPE_ITEM_READ("ItemRead", SCOPE_ITEM_READ_VALUE), + SCOPE_STORED_PROCEDURE_READ("StoreProcedureRead", SCOPE_STORED_PROCEDURE_READ_VALUE), + SCOPE_USER_DEFINED_FUNCTION_READ("UserDefinedFunctionRead", SCOPE_USER_DEFINED_FUNCTION_READ_VALUE), + SCOPE_TRIGGER_READ("TriggerRead", SCOPE_TRIGGER_READ_VALUE), + + /** + * Cosmos Container read scope. + */ + SCOPE_CONTAINER_CREATE_ITEMS("ContainerCreateItems", SCOPE_CONTAINER_CREATE_ITEMS_VALUE), + SCOPE_CONTAINER_REPLACE_ITEMS("ContainerReplaceItems", SCOPE_CONTAINER_REPLACE_ITEMS_VALUE), + SCOPE_CONTAINER_UPSERT_ITEMS("ContainerUpsertItems", SCOPE_CONTAINER_UPSERT_ITEMS_VALUE), + SCOPE_CONTAINER_DELETE_ITEMS("ContainerDeleteItems", SCOPE_CONTAINER_DELETE_ITEMS_VALUE), + SCOPE_CONTAINER_CREATE_STORED_PROCEDURES("ContainerCreateStoredProcedures", SCOPE_CONTAINER_CREATE_STORED_PROCEDURES_VALUE), + SCOPE_CONTAINER_REPLACE_STORED_PROCEDURES("ContainerReplaceStoredProcedures", SCOPE_CONTAINER_REPLACE_STORED_PROCEDURES_VALUE), + SCOPE_CONTAINER_DELETE_STORED_PROCEDURES("ContainerDeleteStoredProcedures", SCOPE_CONTAINER_DELETE_STORED_PROCEDURES_VALUE), + SCOPE_CONTAINER_EXECUTE_STORED_PROCEDURES("ContainerDeleteStoredProcedures", SCOPE_CONTAINER_EXECUTE_STORED_PROCEDURES_VALUE), + SCOPE_CONTAINER_CREATE_TRIGGERS("ContainerCreateTriggers", SCOPE_CONTAINER_CREATE_TRIGGERS_VALUE), + SCOPE_CONTAINER_REPLACE_TRIGGERS("ContainerReplaceTriggers", SCOPE_CONTAINER_REPLACE_TRIGGERS_VALUE), + SCOPE_CONTAINER_DELETE_TRIGGERS("ContainerDeleteTriggers", SCOPE_CONTAINER_DELETE_TRIGGERS_VALUE), + SCOPE_CONTAINER_CREATE_USER_DEFINED_FUNCTIONS("ContainerCreateUserDefinedFunctions", SCOPE_CONTAINER_CREATE_USER_DEFINED_FUNCTIONS_VALUE), + SCOPE_CONTAINER_REPLACE_USER_DEFINED_FUNCTIONS("ContainerReplaceUserDefinedFunctions", SCOPE_CONTAINER_REPLACE_USER_DEFINED_FUNCTIONS_VALUE), + SCOPE_CONTAINER_DELETE_USER_DEFINED_FUNCTIONS("ContainerCreateUserDefinedFunctions", SCOPE_CONTAINER_DELETE_USER_DEFINED_FUNCTIONS_VALUE), + SCOPE_CONTAINER_DELETE_CONFLICTS("ContainerDeleteConflics", SCOPE_CONTAINER_DELETE_CONFLICTS_VALUE), + SCOPE_ITEM_REPLACE("ItemReplace", SCOPE_ITEM_REPLACE_VALUE), + SCOPE_ITEM_UPSERT("ItemUpsert", SCOPE_ITEM_UPSERT_VALUE), + SCOPE_ITEM_DELETE("ItemDelete", SCOPE_ITEM_DELETE_VALUE), + SCOPE_STORED_PROCEDURE_REPLACE("StoredProcedureReplace", SCOPE_STORED_PROCEDURE_REPLACE_VALUE), + SCOPE_STORED_PROCEDURE_DELETE("StoredProcedureReplace", SCOPE_STORED_PROCEDURE_DELETE_VALUE), + SCOPE_STORED_PROCEDURE_EXECUTE("StoredProcedureReplace", SCOPE_STORED_PROCEDURE_EXECUTE_VALUE), + SCOPE_USER_DEFINED_FUNCTION_REPLACE("UserDefinedFunctionReplace", SCOPE_USER_DEFINED_FUNCTION_REPLACE_VALUE), + SCOPE_USER_DEFINED_FUNCTION_DELETE("UserDefinedFunctionReplace", SCOPE_USER_DEFINED_FUNCTION_DELETE_VALUE), + SCOPE_TRIGGER_REPLACE("TriggerReplace", SCOPE_TRIGGER_REPLACE_VALUE), + SCOPE_TRIGGER_DELETE("TriggerDelete", SCOPE_TRIGGER_DELETE_VALUE), + + /** + * Composite read scope. + */ + SCOPE_CONTAINER_READ_ALL_ACCESS("ContainersReadAllAccess", SCOPE_CONTAINER_READ_ALL_ACCESS_VALUE), + SCOPE_ITEM_READ_ALL_ACCESS("ItemReadAllAccess", SCOPE_ITEM_READ_ALL_ACCESS_VALUE), + + /** + * Composite write scope. + */ + SCOPE_CONTAINER_WRITE_ALL_ACCESS("ContainersWriteAllAccess", SCOPE_CONTAINER_WRITE_ALL_ACCESS_VALUE), + SCOPE_ITEM_WRITE_ALL_ACCESS("ItemWriteAllAccess", SCOPE_ITEM_WRITE_ALL_ACCESS_VALUE), + + NONE("None", 0x0); + + + private final int value; + private final String stringValue; + private final String toLowerStringValue; + + DataPlanePermissionScope(String stringValue, int scopeBitMask) { + this.stringValue = stringValue; + this.toLowerStringValue = stringValue.toLowerCase(Locale.ROOT); + this.value = scopeBitMask; + } + + @Override + public String toString() { + return this.stringValue; + } + + public String toLowerCase() { + return this.toLowerStringValue; + } + + public int value() { + return this.value; + } +} diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/PermissionScopeValues.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/PermissionScopeValues.java new file mode 100644 index 000000000000..aae381dd053d --- /dev/null +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/PermissionScopeValues.java @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.cosmos.implementation.sastokens; + +/** + * Represents permission scope values. + */ +class PermissionScopeValues { + /** + * Values which set permission scope applicable to control plane related operations. + */ + static final short SCOPE_ACCOUNT_READ_VALUE = (short) 0x0001; + static final short SCOPE_ACCOUNT_LIST_DATABASES_VALUE = (short) 0x0002; + static final short SCOPE_DATABASE_READ_VALUE = (short) 0x0004; + static final short SCOPE_DATABASE_READ_OFFER_VALUE = (short) 0x0008; + static final short SCOPE_DATABASE_LIST_CONTAINERS_VALUE = (short) 0x0010; + static final short SCOPE_CONTAINER_READ_VALUE = (short) 0x0020; + static final short SCOPE_CONTAINER_READ_OFFER_VALUE = (short) 0x0040; + + static final short SCOPE_ACCOUNT_CREATE_DATABASES_VALUE = (short) 0x0001; + static final short SCOPE_ACCOUNT_DELETE_DATABASES_VALUE = (short) 0x0002; + static final short SCOPE_DATABASE_DELETE_VALUE = (short) 0x0004; + static final short SCOPE_DATABASE_REPLACE_OFFER_VALUE = (short) 0x0008; + static final short SCOPE_DATABASE_CREATE_CONTAINERS_VALUE = (short) 0x0010; + static final short SCOPE_DATABASE_DELETE_CONTAINERS_VALUE = (short) 0x0020; + static final short SCOPE_CONTAINER_REPLACE_VALUE = (short) 0x0040; + static final short SCOPE_CONTAINER_DELETE_VALUE = (short) 0x0080; + static final short SCOPE_CONTAINER_REPLACE_OFFER_VALUE = (short) 0x0100; + + static final short SCOPE_ACCOUNT_READ_ALL_ACCESS_VALUE = (short) 0xFFFF; + static final short SCOPE_DATABASE_READ_ALL_ACCESS_VALUE = + SCOPE_DATABASE_READ_VALUE + | SCOPE_DATABASE_READ_OFFER_VALUE + | SCOPE_DATABASE_LIST_CONTAINERS_VALUE + | SCOPE_CONTAINER_READ_VALUE + | SCOPE_CONTAINER_READ_OFFER_VALUE; + static final short SCOPE_CONTAINERS_READ_ALL_ACCESS_VALUE = + SCOPE_CONTAINER_READ_VALUE + | SCOPE_CONTAINER_READ_OFFER_VALUE; + + static final short SCOPE_ACCOUNT_WRITE_ALL_ACCESS_VALUE = (short) 0xFFFF; + static final short SCOPE_DATABASE_WRITE_ALL_ACCESS_VALUE = + SCOPE_DATABASE_DELETE_VALUE + | SCOPE_DATABASE_REPLACE_OFFER_VALUE + | SCOPE_DATABASE_CREATE_CONTAINERS_VALUE + | SCOPE_DATABASE_DELETE_CONTAINERS_VALUE + | SCOPE_CONTAINER_REPLACE_VALUE + | SCOPE_CONTAINER_DELETE_VALUE + | SCOPE_CONTAINER_REPLACE_OFFER_VALUE; + static final short SCOPE_CONTAINERS_WRITE_ALL_ACCESS_VALUE = + SCOPE_CONTAINER_REPLACE_VALUE + | SCOPE_CONTAINER_DELETE_VALUE + | SCOPE_CONTAINER_REPLACE_OFFER_VALUE; + + /** + * Values which set permission scope applicable to data plane related operations. + */ + static final int SCOPE_CONTAINER_EXECUTE_QUERIES_VALUE = 0x00000001; + static final int SCOPE_CONTAINER_READ_FEEDS_VALUE = 0x00000002; + static final int SCOPE_CONTAINER_READ_STORED_PROCEDURES_VALUE = 0x00000004; + static final int SCOPE_CONTAINER_READ_USER_DEFINED_FUNCTIONS_VALUE = 0x00000008; + static final int SCOPE_CONTAINER_READ_TRIGGERS_VALUE = 0x00000010; + static final int SCOPE_CONTAINER_READ_CONFLICTS_VALUE = 0x00000020; + static final int SCOPE_ITEM_READ_VALUE = 0x00000040; + static final int SCOPE_STORED_PROCEDURE_READ_VALUE = 0x00000080; + static final int SCOPE_USER_DEFINED_FUNCTION_READ_VALUE = 0x00000100; + static final int SCOPE_TRIGGER_READ_VALUE = 0x00000200; + + static final int SCOPE_CONTAINER_CREATE_ITEMS_VALUE = 0x00000001; + static final int SCOPE_CONTAINER_REPLACE_ITEMS_VALUE = 0x00000002; + static final int SCOPE_CONTAINER_UPSERT_ITEMS_VALUE = 0x00000004; + static final int SCOPE_CONTAINER_DELETE_ITEMS_VALUE = 0x00000008; + static final int SCOPE_CONTAINER_CREATE_STORED_PROCEDURES_VALUE = 0x00000010; + static final int SCOPE_CONTAINER_REPLACE_STORED_PROCEDURES_VALUE = 0x00000020; + static final int SCOPE_CONTAINER_DELETE_STORED_PROCEDURES_VALUE = 0x00000040; + static final int SCOPE_CONTAINER_EXECUTE_STORED_PROCEDURES_VALUE = 0x00000080; + static final int SCOPE_CONTAINER_CREATE_TRIGGERS_VALUE = 0x00000100; + static final int SCOPE_CONTAINER_REPLACE_TRIGGERS_VALUE = 0x00000200; + static final int SCOPE_CONTAINER_DELETE_TRIGGERS_VALUE = 0x00000400; + static final int SCOPE_CONTAINER_CREATE_USER_DEFINED_FUNCTIONS_VALUE = 0x00000800; + static final int SCOPE_CONTAINER_REPLACE_USER_DEFINED_FUNCTIONS_VALUE = 0x00001000; + static final int SCOPE_CONTAINER_DELETE_USER_DEFINED_FUNCTIONS_VALUE = 0x00002000; + static final int SCOPE_CONTAINER_DELETE_CONFLICTS_VALUE = 0x00004000; + static final int SCOPE_ITEM_REPLACE_VALUE = 0x00010000; + static final int SCOPE_ITEM_UPSERT_VALUE = 0x00020000; + static final int SCOPE_ITEM_DELETE_VALUE = 0x00040000; + static final int SCOPE_STORED_PROCEDURE_REPLACE_VALUE = 0x00100000; + static final int SCOPE_STORED_PROCEDURE_DELETE_VALUE = 0x00200000; + static final int SCOPE_STORED_PROCEDURE_EXECUTE_VALUE = 0x00400000; + static final int SCOPE_USER_DEFINED_FUNCTION_REPLACE_VALUE = 0x00800000; + static final int SCOPE_USER_DEFINED_FUNCTION_DELETE_VALUE = 0x01000000; + static final int SCOPE_TRIGGER_REPLACE_VALUE = 0x02000000; + static final int SCOPE_TRIGGER_DELETE_VALUE = 0x04000000; + + static final int SCOPE_CONTAINER_READ_ALL_ACCESS_VALUE = 0xFFFFFFFF; + static final int SCOPE_ITEM_READ_ALL_ACCESS_VALUE = + SCOPE_CONTAINER_EXECUTE_QUERIES_VALUE + | SCOPE_ITEM_READ_VALUE; + static final int SCOPE_CONTAINER_WRITE_ALL_ACCESS_VALUE = 0xFFFFFFFF; + static final int SCOPE_ITEM_WRITE_ALL_ACCESS_VALUE = + SCOPE_CONTAINER_CREATE_ITEMS_VALUE + | SCOPE_CONTAINER_REPLACE_ITEMS_VALUE + | SCOPE_CONTAINER_UPSERT_ITEMS_VALUE + | SCOPE_CONTAINER_DELETE_ITEMS_VALUE + | SCOPE_ITEM_REPLACE_VALUE + | SCOPE_ITEM_UPSERT_VALUE + | SCOPE_ITEM_DELETE_VALUE; + + static final int NONE_VALUE = 0; +} diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java new file mode 100644 index 000000000000..5d2e7fcde09f --- /dev/null +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java @@ -0,0 +1,599 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.cosmos.implementation.sastokens; + +import com.azure.cosmos.implementation.Paths; +import com.azure.cosmos.implementation.Utils; +import com.azure.cosmos.models.SasTokenPartitionKeyValueRange; +import com.azure.cosmos.models.SasTokenPermissionKind; +import com.azure.cosmos.models.SasTokenProperties; + +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; + +import static com.azure.cosmos.implementation.sastokens.ControlPlanePermissionScope.SCOPE_CONTAINER_READ; +import static com.azure.cosmos.implementation.sastokens.ControlPlanePermissionScope.SCOPE_CONTAINER_READ_OFFER; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_CREATE_ITEMS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_CREATE_STORED_PROCEDURES; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_CREATE_TRIGGERS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_CREATE_USER_DEFINED_FUNCTIONS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_DELETE_CONFLICTS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_DELETE_ITEMS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_DELETE_STORED_PROCEDURES; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_DELETE_TRIGGERS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_DELETE_USER_DEFINED_FUNCTIONS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_EXECUTE_QUERIES; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_EXECUTE_STORED_PROCEDURES; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_READ_ALL_ACCESS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_READ_CONFLICTS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_READ_FEEDS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_READ_STORED_PROCEDURES; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_READ_TRIGGERS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_READ_USER_DEFINED_FUNCTIONS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_REPLACE_ITEMS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_REPLACE_STORED_PROCEDURES; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_REPLACE_TRIGGERS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_REPLACE_USER_DEFINED_FUNCTIONS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_UPSERT_ITEMS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_ITEM_DELETE; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_ITEM_READ; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_ITEM_READ_ALL_ACCESS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_ITEM_REPLACE; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_ITEM_UPSERT; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_ITEM_WRITE_ALL_ACCESS; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_STORED_PROCEDURE_DELETE; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_STORED_PROCEDURE_EXECUTE; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_STORED_PROCEDURE_READ; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_STORED_PROCEDURE_REPLACE; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_TRIGGER_DELETE; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_TRIGGER_READ; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_TRIGGER_REPLACE; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_USER_DEFINED_FUNCTION_DELETE; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_USER_DEFINED_FUNCTION_READ; +import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_USER_DEFINED_FUNCTION_REPLACE; + +/** + * Represents the implementation of a permission configuration object to be used when creating a Cosmos shared access + * signature token. + */ +public class SasTokenImpl implements SasTokenProperties { + private static final String AUTH_PREFIX = "type=sas&ver=1.0&sig="; + + String user; + String userTag; + String databaseName; + String containerName; + String resourceName; + String resourcePath; + CosmosContainerChildResourceKind resourceKind; + List partitionKeyValueRanges; + Instant startTime; + Instant expiryTime; + + byte keyType; + + short controlPlaneReaderScope; + short controlPlaneWriterScope; + int dataPlaneReaderScope; + int dataPlaneWriterScope; + + public SasTokenImpl() { + this.user = ""; + this.userTag = ""; + this.databaseName = ""; + this.containerName = ""; + this.resourceName = ""; + this.resourcePath = ""; + this.startTime = Instant.now(); + this.expiryTime = null; + this.controlPlaneReaderScope = 0; + this.controlPlaneWriterScope = 0; + this.dataPlaneReaderScope = 0; + this.dataPlaneWriterScope = 0; + this.keyType = 0; + } + + /** + * Generates the payload representing the permission configuration for the sas token. + *

+ * The payload is composed of the following: + * {@literal + * user\n + * userTag\n + * resourcePathPrefix\n + * partitionRangesCommaSeparated\n + * epochStartTime\n + * epochExpiryTime\n + * keyKind\n + * shortAsHexControlPlaneReaderScope\n + * shortAsHexControlPlaneWriterScope\n + * intAsHexDataPlaneReaderScope\n + * intAsHexDataPlaneWriterScope\n + * } + * + * @return the permission configuration payload. + */ + private String generatePayload() { + StringBuilder resourcePrefixPath = new StringBuilder(); + + if (!this.databaseName.isEmpty()) { + resourcePrefixPath.append(Paths.ROOT).append(Paths.DATABASES_PATH_SEGMENT) + .append("/").append(this.databaseName); + } + + if (!this.containerName.isEmpty()) { + if (this.databaseName.isEmpty()) { + throw new IllegalArgumentException("databaseName"); + } + + resourcePrefixPath.append(Paths.ROOT).append(Paths.COLLECTIONS_PATH_SEGMENT) + .append(Paths.ROOT).append(this.containerName); + } + + if (!this.resourceName.isEmpty()) { + if (this.containerName.isEmpty()) { + throw new IllegalArgumentException("containerName"); + } + + switch (this.resourceKind) { + case ITEM: + resourcePrefixPath.append(Paths.ROOT).append(Paths.DOCUMENTS_PATH_SEGMENT); + break; + case STORED_PROCEDURE: + resourcePrefixPath.append(Paths.ROOT).append(Paths.STORED_PROCEDURES_PATH_SEGMENT); + break; + case USER_DEFINED_FUNCTION: + resourcePrefixPath.append(Paths.ROOT).append(Paths.USER_DEFINED_FUNCTIONS_PATH_SEGMENT); + break; + case TRIGGER: + resourcePrefixPath.append(Paths.ROOT).append(Paths.TRIGGERS_PATH_SEGMENT); + break; + default: + throw new IllegalArgumentException("resourceKind"); + } + + resourcePrefixPath.append(Paths.ROOT).append(this.resourceName); + } + + resourcePrefixPath.append(Paths.ROOT); + this.resourcePath = resourcePrefixPath.toString(); + + StringBuilder partitionRanges = new StringBuilder(); + if (this.partitionKeyValueRanges != null && !this.partitionKeyValueRanges.isEmpty()) { + if (this.resourceKind != CosmosContainerChildResourceKind.ITEM) { + throw new IllegalArgumentException("partitionKeyValueRanges"); + } + + this.partitionKeyValueRanges.forEach(range -> partitionRanges.append(range.encode()).append(",")); + } + + if (this.expiryTime == null) { + this.expiryTime = this.startTime.plus(Duration.ofHours(2)); + } + + if (this.controlPlaneReaderScope == 0) { + this.controlPlaneReaderScope |= SCOPE_CONTAINER_READ.value(); + this.controlPlaneReaderScope |= SCOPE_CONTAINER_READ_OFFER.value(); + } + + if (this.dataPlaneReaderScope == 0 && this.dataPlaneWriterScope == 0) { + this.dataPlaneReaderScope |= SCOPE_CONTAINER_READ_ALL_ACCESS.value(); + } + + StringBuilder payload = new StringBuilder(this.user).append("\n") + .append(this.userTag).append("\n") + .append(resourcePrefixPath).append("\n") + .append(partitionRanges).append("\n") + .append(String.format("%X", this.startTime.getEpochSecond())).append("\n") + .append(String.format("%X", this.expiryTime.getEpochSecond())).append("\n") + .append(String.format("%X", this.keyType)).append("\n") + .append(String.format("%X", this.controlPlaneReaderScope)). append("\n") + .append(String.format("%X", this.controlPlaneWriterScope)). append("\n") + .append(String.format("%X", this.dataPlaneReaderScope)). append("\n") + .append(String.format("%X", this.dataPlaneWriterScope)). append("\n"); + + return Base64.getUrlEncoder().withoutPadding().encodeToString(payload.toString().getBytes(StandardCharsets.UTF_8)); + } + + private String getSasTokenWithHMACSHA256(String key) { + byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); + byte[] keyDecodedBytes = Utils.Base64Decoder.decode(key); + SecretKey signingKey = new SecretKeySpec(keyDecodedBytes, "HMACSHA256"); + try { + Mac macInstance = Mac.getInstance("HMACSHA256"); + macInstance.init(signingKey); + + // Get payload which is Base64 encoded + String payload = this.generatePayload(); + byte[] payloadBytes = payload.getBytes(StandardCharsets.UTF_8); + byte[] digest = macInstance.doFinal(payloadBytes); + String authorizationToken = Utils.encodeBase64String(digest); + + StringBuilder token = new StringBuilder(AUTH_PREFIX) + .append(authorizationToken) + .append(",") + .append(payload); + + return token.toString(); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new IllegalStateException(e); + } + } + + /** + * Creates a Cosmos shared access signature token using the specified account key and a HMACSHA256 encoder. + * + * @param key the Cosmos key that will be used to generate a shared access signature token. + * @return the shared access signature token. + */ + @Override + public String getSasTokenValueUsingHMAC(String key) { + this.keyType = 0; + if (key == null || key.isEmpty()) { + throw new IllegalArgumentException("key"); + } + + return getSasTokenWithHMACSHA256(key); + } + + /** + * Create a Cosmos shared access signature token using the specified account key and a HMACSHA256 encoder. + *

+ * Providing key type will help expedite the authentication and authorization executed by the Cosmos service. + * + * @param key the Cosmos key that will be used to generate a shared access signature token. + * @param keyType the Cosmos key type that will be used to generate a shared access signature token. + * @return the shared access signature token. + */ + @Override + public String getSasTokenValueUsingHMAC(String key, CosmosKeyType keyType) { + if (key == null || key.isEmpty()) { + throw new IllegalArgumentException("key"); + } + + switch (keyType) { + case PRIMARY_MASTER: + this.keyType = 1; + break; + case SECONDARY_MASTER: + this.keyType = 2; + break; + case PRIMARY_READONLY: + this.keyType = 3; + break; + case SECONDARY_READONLY: + this.keyType = 4; + break; + default: + throw new IllegalArgumentException("keyType"); + } + + return getSasTokenWithHMACSHA256(key); + } + + @Override + public String getDatabaseName() { + return this.databaseName; + } + + @Override + public SasTokenProperties setDatabaseName(String databaseName) { + if (databaseName == null || Utils.trimBeginningAndEndingSlashes(databaseName).isEmpty()) { + throw new IllegalArgumentException("databaseName"); + } + + this.databaseName = Utils.trimBeginningAndEndingSlashes(databaseName); + + return this; + } + + @Override + public String getContainerName() { + return this.containerName; + } + + @Override + public SasTokenProperties setContainerName(String containerName) { + if (containerName == null || Utils.trimBeginningAndEndingSlashes(containerName).isEmpty()) { + throw new IllegalArgumentException("containerName"); + } + + this.containerName = Utils.trimBeginningAndEndingSlashes(containerName); + + return this; + } + + @Override + public CosmosContainerChildResourceKind getResourceKind() { + return this.resourceKind; + } + + @Override + public String getResourceName() { + return this.resourceName; + } + + @Override + public SasTokenProperties setResourceName(CosmosContainerChildResourceKind kind, String resourceName) { + if (resourceName == null) { + throw new IllegalArgumentException("resourceName"); + } + + this.resourceName = Utils.trimBeginningAndEndingSlashes(resourceName); + this.resourceKind = kind; + + return this; + } + + @Override + public String getUser() { + return this.user; + } + + @Override + public SasTokenProperties setUser(String user) { + if (user == null || user.isEmpty()) { + throw new IllegalArgumentException("user"); + } + + this.user = user; + + return this; + } + + @Override + public String getUserTag() { + return this.userTag; + } + + @Override + public SasTokenProperties setUserTag(String userTag) { + if (userTag == null) { + throw new IllegalArgumentException("userTag"); + } + + this.userTag = userTag; + + return this; + } + + @Override + public Instant getExpiryTime() { + return this.expiryTime; + } + + @Override + public SasTokenProperties setExpiryTime(Duration expiryTime) { + if (expiryTime == null) { + throw new IllegalArgumentException("expiryTime"); + } + + this.expiryTime = startTime.plus(expiryTime); + + return this; + } + + @Override + public Instant getStartTime() { + return this.startTime; + } + + @Override + public SasTokenProperties setStartTime(Instant startTime) { + if (startTime == null) { + throw new IllegalArgumentException("startTime"); + } + + this.startTime = startTime; + + return this; + } + + @Override + public Iterable getPartitionKeyValueRanges() { + return this.partitionKeyValueRanges; + } + + @Override + public SasTokenProperties setPartitionKeyValueRanges(Iterable partitionKeyValues) { + if (partitionKeyValues != null) { + this.partitionKeyValueRanges = new ArrayList<>(); + partitionKeyValues.forEach(partitionKey -> this.partitionKeyValueRanges.add(SasTokenPartitionKeyValueRange.create(partitionKey))); + } else { + this.partitionKeyValueRanges = null; + } + + return this; + } + + @Override + public SasTokenProperties addPartitionKeyValue(String partitionKeyValue) { + if (this.partitionKeyValueRanges == null) { + this.partitionKeyValueRanges = new ArrayList<>(); + } + + this.partitionKeyValueRanges.add(SasTokenPartitionKeyValueRange.create(partitionKeyValue)); + + return this; + } + + @Override + public SasTokenProperties addPermission(SasTokenPermissionKind permissionKind) { + switch (permissionKind) { + + // Container data all. + case CONTAINER_CREATE_ITEMS: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_CREATE_ITEMS.value(); + break; + } + case CONTAINER_REPLACE_ITEMS: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_REPLACE_ITEMS.value(); + break; + } + case CONTAINER_UPSERT_ITEMS: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_UPSERT_ITEMS.value(); + break; + } + case CONTAINER_DELETE_ITEMS: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_DELETE_ITEMS.value(); + break; + } + case CONTAINER_EXECUTE_QUERIES: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_EXECUTE_QUERIES.value(); + break; + } + case CONTAINER_READ_FEEDS: { + this.dataPlaneReaderScope |= SCOPE_CONTAINER_READ_FEEDS.value(); + break; + } + case CONTAINER_CREATE_STORE_PROCEDURES: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_CREATE_STORED_PROCEDURES.value(); + break; + } + case CONTAINER_READ_STORE_PROCEDURES: { + this.dataPlaneReaderScope |= SCOPE_CONTAINER_READ_STORED_PROCEDURES.value(); + break; + } + case CONTAINER_REPLACE_STORE_PROCEDURES: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_REPLACE_STORED_PROCEDURES.value(); + break; + } + case CONTAINER_DELETE_STORE_PROCEDURES: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_DELETE_STORED_PROCEDURES.value(); + break; + } + case CONTAINER_CREATE_TRIGGERS: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_CREATE_TRIGGERS.value(); + break; + } + case CONTAINER_READ_TRIGGERS: { + this.dataPlaneReaderScope |= SCOPE_CONTAINER_READ_TRIGGERS.value(); + break; + } + case CONTAINER_REPLACE_TRIGGERS: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_REPLACE_TRIGGERS.value(); + break; + } + case CONTAINER_DELETE_TRIGGERS: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_DELETE_TRIGGERS.value(); + break; + } + case CONTAINER_CREATE_USER_DEFINED_FUNCTIONS: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_CREATE_USER_DEFINED_FUNCTIONS.value(); + break; + } + case CONTAINER_READ_USER_DEFINED_FUNCTIONS: { + this.dataPlaneReaderScope |= SCOPE_CONTAINER_READ_USER_DEFINED_FUNCTIONS.value(); + break; + } + case CONTAINER_REPLACE_USER_DEFINED_FUNCTIONS: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_REPLACE_USER_DEFINED_FUNCTIONS.value(); + break; + } + case CONTAINER_DELETE_USER_DEFINED_FUNCTIONS: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_DELETE_USER_DEFINED_FUNCTIONS.value(); + break; + } + case CONTAINER_EXECUTE_STORE_PROCEDURES: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_EXECUTE_STORED_PROCEDURES.value(); + break; + } + case CONTAINER_READ_CONFLICTS: { + this.dataPlaneReaderScope |= SCOPE_CONTAINER_READ_CONFLICTS.value(); + break; + } + case CONTAINER_DELETE_CONFLICTS: { + this.dataPlaneWriterScope |= SCOPE_CONTAINER_DELETE_CONFLICTS.value(); + break; + } + + // Cosmos container item scope. + case ITEM_FULL_ACCESS: { + this.dataPlaneWriterScope |= SCOPE_ITEM_WRITE_ALL_ACCESS.value(); + this.addPermission(SasTokenPermissionKind.ITEM_READ_ALL_ACCESS); + break; + } + case ITEM_READ_ALL_ACCESS: { + this.dataPlaneReaderScope |= SCOPE_ITEM_READ_ALL_ACCESS.value(); + break; + } + case ITEM_READ: { + this.dataPlaneReaderScope |= SCOPE_ITEM_READ.value(); + break; + } + case ITEM_REPLACE: { + this.dataPlaneWriterScope |= SCOPE_ITEM_REPLACE.value(); + break; + } + case ITEM_UPSERT: { + this.dataPlaneWriterScope |= SCOPE_ITEM_UPSERT.value(); + break; + } + case ITEM_DELETE: { + this.dataPlaneWriterScope |= SCOPE_ITEM_DELETE.value(); + break; + } + + // Cosmos container store procedure scope. + case STORE_PROCEDURE_READ: { + this.dataPlaneReaderScope |= SCOPE_STORED_PROCEDURE_READ.value(); + break; + } + case STORE_PROCEDURE_REPLACE: { + this.dataPlaneWriterScope |= SCOPE_STORED_PROCEDURE_REPLACE.value(); + break; + } + case STORE_PROCEDURE_DELETE: { + this.dataPlaneWriterScope |= SCOPE_STORED_PROCEDURE_DELETE.value(); + break; + } + case STORE_PROCEDURE_EXECUTE: { + this.dataPlaneWriterScope |= SCOPE_STORED_PROCEDURE_EXECUTE.value(); + break; + } + + // Cosmos container user defined function scope. + case USER_DEFINED_FUNCTION_READ: { + this.dataPlaneReaderScope |= SCOPE_USER_DEFINED_FUNCTION_READ.value(); + break; + } + case USER_DEFINED_FUNCTION_REPLACE: { + this.dataPlaneWriterScope |= SCOPE_USER_DEFINED_FUNCTION_REPLACE.value(); + break; + } + case USER_DEFINED_FUNCTION_DELETE: { + this.dataPlaneWriterScope |= SCOPE_USER_DEFINED_FUNCTION_DELETE.value(); + break; + } + + // Cosmos container trigger scope. + case TRIGGER_READ: { + this.dataPlaneReaderScope |= SCOPE_TRIGGER_READ.value(); + break; + } + case TRIGGER_REPLACE: { + this.dataPlaneWriterScope |= SCOPE_TRIGGER_REPLACE.value(); + break; + } + case TRIGGER_DELETE: { + this.dataPlaneWriterScope |= SCOPE_TRIGGER_DELETE.value(); + break; + } + + default: + throw new IllegalArgumentException("permissionKind"); + } + + return this; + } +} diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenPartitionKeyValueRangeImpl.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenPartitionKeyValueRangeImpl.java new file mode 100644 index 000000000000..578eb20b4c56 --- /dev/null +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenPartitionKeyValueRangeImpl.java @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.cosmos.implementation.sastokens; + +import com.azure.cosmos.models.SasTokenPartitionKeyValueRange; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +/** + * Represents a partition key value range to be used when creating a shared access signature token. + */ +public class SasTokenPartitionKeyValueRangeImpl implements SasTokenPartitionKeyValueRange { + String partitionKeyValue; + + /** + * Constructs the partition key value range for which to grant access. + * + * @param partitionKeyValue the partition key value for which to grant access. + */ + public SasTokenPartitionKeyValueRangeImpl(String partitionKeyValue) { + this.partitionKeyValue = partitionKeyValue; + } + + /** + * Gets the partition key value. + * + * @return the partition key value. + */ + @Override + public String getPartitionKey() { + return this.partitionKeyValue; + } + + /** + * Encodes the current partition key value range to be used when generating a shared access signature token. + * + * + * @return a string formatted as "base64_of_partitionKey". + */ + @Override + public String encode() { + return Base64.getUrlEncoder().withoutPadding().encodeToString(this.partitionKeyValue.getBytes(StandardCharsets.UTF_8)); + } + + /** + * Decodes a representation of a partition key value range used when generating a shared access signature token. + * + * The input string must be in "base64_of_partitionKey" format. + * + * @return an instance of SasTokenPartitionKeyValueRange representing the input string. + */ + public static SasTokenPartitionKeyValueRange decode(String encoding) { + byte[] decodedString = Base64.getDecoder().decode(encoding); + + return new SasTokenPartitionKeyValueRangeImpl(new String(decodedString, StandardCharsets.UTF_8)); + } +} diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPartitionKeyValueRange.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPartitionKeyValueRange.java new file mode 100644 index 000000000000..bb838f5cdcd9 --- /dev/null +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPartitionKeyValueRange.java @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.cosmos.models; + +import com.azure.cosmos.implementation.sastokens.SasTokenPartitionKeyValueRangeImpl; +import com.azure.cosmos.util.Beta; + +/** + * Represents a partition key value range to be used when creating a shared access signature token. + */ +@Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) +public interface SasTokenPartitionKeyValueRange { + + /** + * Creates a {@link SasTokenPartitionKeyValueRange} representing the partition key value for which to grant access. + * + * @param partitionKeyValue the partition key value for which to grant access. + * @return a {@link SasTokenPartitionKeyValueRange} representing the partition key value for which to grant access. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + static SasTokenPartitionKeyValueRange create(String partitionKeyValue) { + return new SasTokenPartitionKeyValueRangeImpl(partitionKeyValue); + } + + /** + * Gets the partition key value. + * + * @return the partition key value. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + String getPartitionKey(); + + /** + * Encodes the current partition key value range to be used when generating a shared access signature token. + * + * @return a string formatted as "base64_of_partitionKey". + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + String encode(); + + /** + * Decodes a representation of a partition key value range used when generating a shared access signature token. + * + * The input string must be in "base64_of_partitionKey" format. + * + * @return an instance of SasTokenPartitionKeyValueRange representing the input string. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + static SasTokenPartitionKeyValueRange decode(String encoding) { + return SasTokenPartitionKeyValueRangeImpl.decode(encoding); + } +} diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPermissionKind.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPermissionKind.java new file mode 100644 index 000000000000..89c0d4316f13 --- /dev/null +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPermissionKind.java @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.cosmos.models; + +import com.azure.cosmos.util.Beta; + +/** + * Defines permission scopes applicable when generating a Cosmos DB shared access signature token. + */ +@Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) +public enum SasTokenPermissionKind { + /** + * Cosmos DB Container Resources scope. + */ + CONTAINER_CREATE_ITEMS, + CONTAINER_REPLACE_ITEMS, + CONTAINER_UPSERT_ITEMS, + CONTAINER_DELETE_ITEMS, + CONTAINER_EXECUTE_QUERIES, + CONTAINER_READ_FEEDS, + CONTAINER_CREATE_STORE_PROCEDURES, + CONTAINER_READ_STORE_PROCEDURES, + CONTAINER_REPLACE_STORE_PROCEDURES, + CONTAINER_DELETE_STORE_PROCEDURES, + CONTAINER_CREATE_TRIGGERS, + CONTAINER_READ_TRIGGERS, + CONTAINER_REPLACE_TRIGGERS, + CONTAINER_DELETE_TRIGGERS, + CONTAINER_CREATE_USER_DEFINED_FUNCTIONS, + CONTAINER_READ_USER_DEFINED_FUNCTIONS, + CONTAINER_REPLACE_USER_DEFINED_FUNCTIONS, + CONTAINER_DELETE_USER_DEFINED_FUNCTIONS, + CONTAINER_EXECUTE_STORE_PROCEDURES, + CONTAINER_READ_CONFLICTS, + CONTAINER_DELETE_CONFLICTS, + + /** + * Cosmos DB Item scope. + */ + ITEM_READ_ALL_ACCESS, + ITEM_FULL_ACCESS, + ITEM_READ, + ITEM_REPLACE, + ITEM_UPSERT, + ITEM_DELETE, + + /** + * Cosmos DB Store Procedure scope. + */ + STORE_PROCEDURE_READ, + STORE_PROCEDURE_REPLACE, + STORE_PROCEDURE_DELETE, + STORE_PROCEDURE_EXECUTE, + + /** + * Cosmos DB User Defined Function scope. + */ + USER_DEFINED_FUNCTION_READ, + USER_DEFINED_FUNCTION_REPLACE, + USER_DEFINED_FUNCTION_DELETE, + + /** + * Cosmos DB Trigger scope. + */ + TRIGGER_READ, + TRIGGER_REPLACE, + TRIGGER_DELETE, +} diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenProperties.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenProperties.java new file mode 100644 index 000000000000..94a35445dfbe --- /dev/null +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenProperties.java @@ -0,0 +1,272 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.cosmos.models; + +import com.azure.cosmos.implementation.sastokens.SasTokenImpl; +import com.azure.cosmos.util.Beta; + +import java.time.Duration; +import java.time.Instant; + +/** + * Represents a permission configuration object to be used when creating a Cosmos DB shared access signature token. + */ +@Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) +public interface SasTokenProperties { + /** + * Gets the name of the Cosmos DB database to grant access to. + * + * If database name is an empty string then the access is granted at account level. + * + * @return the name of the database to grant access to. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + String getDatabaseName(); + + /** + * Sets the name of the Cosmos DB database within which the target resource belongs to and for which to grant access to. + * + * If database name is an empty string then the access is granted at account level. + * + * @param databaseName the name of the database to grant access to. + * @return the current permission configuration object. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + SasTokenProperties setDatabaseName(String databaseName); + + /** + * Gets the name of the Cosmos DB container to grant access to or as the parent resource of the target reosource. + * + * A valid non-empty database name must be set; if the container name is an empty string then + * the access is granted at database level. + * + * @return the name of the container to grant access to. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + String getContainerName(); + + /** + * Gets the name of the Cosmos DB container to grant access to. + * + * A valid non-empty database name must be set; if the container name is an empty string then + * the access is granted at database level. + * + * @param containerName the name of the container to grant access to. + * @return the current permission configuration object. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + SasTokenProperties setContainerName(String containerName); + + /** + * Gets the type of the Cosmos resources to grant access to. + * + * A valid non-empty container name must be set in combination with this setting. + * + * @return the type of the resources to grant access to. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + CosmosContainerChildResourceKind getResourceKind(); + + /** + * Gets the name of the Cosmos DB resources to grant access to. + * + * A valid non-empty container name must be set in combination with this setting. + * + * @return the name of the resources to grant access to. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + String getResourceName(); + + /** + * Sets the name of the Cosmos DB resource to grant access to. + * + * A valid non-empty container name must be set in combination with this setting. + * + * @param kind the type of the resource (item, stored procedure etc) to grant access to. + * @param resourceName the prefix name of the resources to grant access to. + * @return the current permission configuration object. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + SasTokenProperties setResourceName(CosmosContainerChildResourceKind kind, String resourceName); + + /** + * Gets the user name or ID registered with this permission object. + * + * A user name or ID is a unique identifier which will be used for tracing and auditing purposes in combination + * with the permission token when authenticating and authorizing Cosmos operations. + * + * @return the name or ID of the user associated with this token. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + String getUser(); + + /** + * Sets the user name or ID registered with this permission object. + * + * A user name or ID is a unique identifier which will be used for tracing and auditing purposes in combination + * with the permission token when authenticating and authorizing Cosmos operations. A non-empty and maximum of + * 40 characters in length string can be set for the user; if a user name or ID is not specified, a random 10 + * length string will be used instead. + * + * @param user the name or ID of the user associated with this token. + * @return the current permission configuration object. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + SasTokenProperties setUser(String user); + + /** + * Gets the tag for the user name or ID registered with this permission object. + * + * The user tag is a unique identifier which will be used for tracing and auditing purposes in combination + * with the permission token when authenticating and authorizing Cosmos operations. + * + * @return the tag corresponding to the user associated with this token. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + String getUserTag(); + + /** + * Sets the tag for the user name or ID registered with this permission object. + * + * The user tag is a unique identifier which will be used for tracing and auditing purposes in combination + * with the permission token when authenticating and authorizing Cosmos operations. + * + * @param userTag the tag corresponding to the user associated with this token. + * @return the current permission configuration object. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + SasTokenProperties setUserTag(String userTag); + + /** + * Gets the expiry time (GMT time zone) for the shared access signature associated with the permission instance. + * + * @return the expiry time (GMT time zone) for a shared access signature associated with the permission instance. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + Instant getExpiryTime(); + + /** + * Sets the expiry time for the shared access signature associated with the permission instance. + *

+ * Default is 2 hours from start time; the maximum duration allowed to be set as expiry time is 24 hours from + * the specified start time. + * + * @param expiryTime the expiry time for a shared access signature token associated with the permission + * instance, up to 24 hours from the start time. + * @return the current permission configuration object. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + SasTokenProperties setExpiryTime(Duration expiryTime); + + /** + * Gets the start time (GMT time zone) for the shared access signature associated with the permission instance. + * + * @return the start time (GMT time zone) for a shared access signature associated with the permission instance. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + Instant getStartTime(); + + /** + * Sets the start time for the shared access signature associated with the permission instance. + * + * Default is current time (now). + * + * @param startTime the start time for a shared access signature associated with the permission instance. + * @return the current permission configuration object. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + SasTokenProperties setStartTime(Instant startTime); + + /** + * Gets the list of partition key value ranges to be used when creating a shared access signature token. + * + * @return the set of partition key value ranges to be used when creating a shared access signature token. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + Iterable getPartitionKeyValueRanges(); + + /** + * Sets the list of partition key values to be used when creating a shared access signature token. + * + * In the presence of this setting only operation using documents with the partition key value within these ranges + * are allowed; default is empty set, any partition key value is allowed. + * + * @param partitionKeyValues the list of partition key values to be used when creating a shared access signature token. + * @return the current permission configuration object. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + SasTokenProperties setPartitionKeyValueRanges(Iterable partitionKeyValues); + + /** + * Adds a partition key value to be used when creating a shared access signature token. + * + * In the presence of this setting only operation using documents with the partition key value within these ranges + * are allowed; default is empty set, any partition key value is allowed. + * + * @param partitionKeyValue the partition key value to be used when creating a shared access signature token. + * @return the current permission configuration object. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + SasTokenProperties addPartitionKeyValue(String partitionKeyValue); + + /** + * Adds a permission setting to execute specific Cosmos operation or set of operations. + * + * @param permissionKind the permission setting which allows execution of specific Cosmos operation or set of operations. + * @return the current permission configuration object. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + SasTokenProperties addPermission(SasTokenPermissionKind permissionKind); + + /** + * Creates a permission configuration to be used when creating a Cosmos shared access signature token. + * + * @param user the user that will be associated with this shared access signature token. + * @return an instance of {@link SasTokenProperties} that will be used to generated the token. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + static SasTokenProperties create(String user, String databaseName, String containerName) { + return new SasTokenImpl() + .setUser(user) + .setDatabaseName(databaseName) + .setContainerName(containerName); + } + + /** + * Creates a Cosmos shared access signature token using the specified account key and a HMACSHA256 encoder. + * + * @param key the Cosmos key that will be used to generate a shared access signature token. + * @return the shared access signature token. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + String getSasTokenValueUsingHMAC(String key); + + /** + * Creates a Cosmos shared access signature token using the specified account key and a HMACSHA256 encoder. + *

+ * Providing key type will help expedite the authentication and authorization executed by the Cosmos service. + * + * @param key the Cosmos key that will be used to generate a shared access signature token. + * @param keyType the Cosmos key type that will be used to generate a shared access signature token. + * @return the shared access signature token. + */ + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + String getSasTokenValueUsingHMAC(String key, CosmosKeyType keyType); + + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + enum CosmosContainerChildResourceKind { + ITEM, + STORED_PROCEDURE, + USER_DEFINED_FUNCTION, + TRIGGER + } + + @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) + enum CosmosKeyType { + PRIMARY_MASTER, + SECONDARY_MASTER, + PRIMARY_READONLY, + SECONDARY_READONLY + } +} diff --git a/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java b/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java new file mode 100644 index 000000000000..b526e2b60688 --- /dev/null +++ b/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.cosmos.implementation.sastokens; + +import com.azure.cosmos.models.SasTokenProperties; +import org.testng.annotations.Test; + +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.Base64; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SasTokenTests { + private final static String TEST_KEY = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; + private final static String TEST_EXPECTED_SASTOKEN = "type=sas&ver=1.0&sig=rQNGPRFDzgJbi9/W4fUmuLBGjgtSKjJCWsrtNO72NKk=,dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKMAoxQzIwCjAKNjAKMApGRkZGRkZGRgowCg"; + private final static String TEST_EXPECTED_PAYLOAD = "user1\n" + + "\n" + + "/dbs/db1/colls/coll1/\n" + + "\n" + + "0\n" + + "1C20\n" + + "0\n" + + "60\n" + + "0\n" + + "FFFFFFFF\n" + + "0\n"; + + @Test(groups = "unit") + public void createSimpleSasToken() { + SasTokenProperties sasTokenProperties = SasTokenProperties.create("user1", "db1", "coll1") + .setStartTime(Instant.ofEpochMilli(0)); + assertThat("user1").isEqualTo(sasTokenProperties.getUser()); + assertThat("").isEqualTo(sasTokenProperties.getUserTag()); + assertThat("db1").isEqualTo(sasTokenProperties.getDatabaseName()); + assertThat("coll1").isEqualTo(sasTokenProperties.getContainerName()); + assertThat("").isEqualTo(sasTokenProperties.getUserTag()); + assertThat("").isEqualTo(sasTokenProperties.getUserTag()); + + String sasTokenValue = sasTokenProperties.getSasTokenValueUsingHMAC(TEST_KEY); + assertThat(TEST_EXPECTED_SASTOKEN) + .isEqualTo(sasTokenValue); + + String[] tokenSegments = sasTokenValue.split("&"); + assertThat(3).isEqualTo(tokenSegments.length); + + String[] sasTokenParts = tokenSegments[2].split(","); + assertThat(2).isEqualTo(sasTokenParts.length); + + byte[] decodedString = Base64.getDecoder().decode(sasTokenParts[1]); + String sasTokenPayload = new String(decodedString, StandardCharsets.UTF_8); + assertThat(TEST_EXPECTED_PAYLOAD) + .isEqualTo(sasTokenPayload); + } +} From 8f662c2ee1f0dee711dc9d3913f0b5125474b799 Mon Sep 17 00:00:00 2001 From: milismsft-mac Date: Wed, 27 Jan 2021 21:28:53 -0800 Subject: [PATCH 02/11] test updates --- .../sastokens/SasTokenTests.java | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java b/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java index b526e2b60688..0f1b90a3f0a0 100644 --- a/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java +++ b/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java @@ -6,6 +6,7 @@ import org.testng.annotations.Test; import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.time.Instant; import java.util.Base64; @@ -13,8 +14,9 @@ public class SasTokenTests { private final static String TEST_KEY = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; - private final static String TEST_EXPECTED_SASTOKEN = "type=sas&ver=1.0&sig=rQNGPRFDzgJbi9/W4fUmuLBGjgtSKjJCWsrtNO72NKk=,dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKMAoxQzIwCjAKNjAKMApGRkZGRkZGRgowCg"; - private final static String TEST_EXPECTED_PAYLOAD = "user1\n" + + + private final static String TEST_EXPECTED_SIMPLE_SASTOKEN = "type=sas&ver=1.0&sig=rQNGPRFDzgJbi9/W4fUmuLBGjgtSKjJCWsrtNO72NKk=,dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKMAoxQzIwCjAKNjAKMApGRkZGRkZGRgowCg"; + private final static String TEST_EXPECTED_SIMPLE_PAYLOAD = "user1\n" + "\n" + "/dbs/db1/colls/coll1/\n" + "\n" + @@ -34,11 +36,49 @@ public void createSimpleSasToken() { assertThat("").isEqualTo(sasTokenProperties.getUserTag()); assertThat("db1").isEqualTo(sasTokenProperties.getDatabaseName()); assertThat("coll1").isEqualTo(sasTokenProperties.getContainerName()); + + String sasTokenValue = sasTokenProperties.getSasTokenValueUsingHMAC(TEST_KEY); + assertThat(TEST_EXPECTED_SIMPLE_SASTOKEN) + .isEqualTo(sasTokenValue); + + String[] tokenSegments = sasTokenValue.split("&"); + assertThat(3).isEqualTo(tokenSegments.length); + + String[] sasTokenParts = tokenSegments[2].split(","); + assertThat(2).isEqualTo(sasTokenParts.length); + + byte[] decodedString = Base64.getDecoder().decode(sasTokenParts[1]); + String sasTokenPayload = new String(decodedString, StandardCharsets.UTF_8); + assertThat(TEST_EXPECTED_SIMPLE_PAYLOAD) + .isEqualTo(sasTokenPayload); + } + + private final static String TEST_EXPECTED_EXPIRY_SASTOKEN = "type=sas&ver=1.0&sig=pCgZFxV9JQN1i3vzYNTfQldW1No7I+MSgN628TZcJAI=,dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKNUZFRTY2MDEKNjIxM0I3MDEKMAo2MAowCkZGRkZGRkZGCjAK"; + private final static String TEST_EXPECTED_EXPIRY_PAYLOAD = "user1\n" + + "\n" + + "/dbs/db1/colls/coll1/\n" + + "\n" + + "5FEE6601\n" + + "6213B701\n" + + "0\n" + + "60\n" + + "0\n" + + "FFFFFFFF\n" + + "0\n"; + @Test(groups = "unit") + public void createSasTokenWithExpiryTime() { + SasTokenProperties sasTokenProperties = SasTokenProperties.create("user1", "db1", "coll1") + .setStartTime(Instant.parse("2021-01-01T00:00:01.00Z")) + .setExpiryTime(Duration.ofHours(10000)); + assertThat("user1").isEqualTo(sasTokenProperties.getUser()); assertThat("").isEqualTo(sasTokenProperties.getUserTag()); - assertThat("").isEqualTo(sasTokenProperties.getUserTag()); + assertThat("db1").isEqualTo(sasTokenProperties.getDatabaseName()); + assertThat("coll1").isEqualTo(sasTokenProperties.getContainerName()); + assertThat(1609459201L).isEqualTo(sasTokenProperties.getStartTime().getEpochSecond()); + assertThat(1645459201L).isEqualTo(sasTokenProperties.getExpiryTime().getEpochSecond()); String sasTokenValue = sasTokenProperties.getSasTokenValueUsingHMAC(TEST_KEY); - assertThat(TEST_EXPECTED_SASTOKEN) + assertThat(TEST_EXPECTED_EXPIRY_SASTOKEN) .isEqualTo(sasTokenValue); String[] tokenSegments = sasTokenValue.split("&"); @@ -49,7 +89,7 @@ public void createSimpleSasToken() { byte[] decodedString = Base64.getDecoder().decode(sasTokenParts[1]); String sasTokenPayload = new String(decodedString, StandardCharsets.UTF_8); - assertThat(TEST_EXPECTED_PAYLOAD) + assertThat(TEST_EXPECTED_EXPIRY_PAYLOAD) .isEqualTo(sasTokenPayload); } } From 9ce4c7684c324761153cbf10ad2de6b46ca94742 Mon Sep 17 00:00:00 2001 From: Milis Date: Sat, 30 Jan 2021 00:56:14 -0800 Subject: [PATCH 03/11] fix build related failures due to missing documentation items. --- .../com/azure/cosmos/models/SasTokenPartitionKeyValueRange.java | 1 + .../main/java/com/azure/cosmos/models/SasTokenProperties.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPartitionKeyValueRange.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPartitionKeyValueRange.java index bb838f5cdcd9..f98bf1f2ef97 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPartitionKeyValueRange.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPartitionKeyValueRange.java @@ -44,6 +44,7 @@ static SasTokenPartitionKeyValueRange create(String partitionKeyValue) { * * The input string must be in "base64_of_partitionKey" format. * + * @param encoding the encoded input string. * @return an instance of SasTokenPartitionKeyValueRange representing the input string. */ @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenProperties.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenProperties.java index 94a35445dfbe..0c79b711a705 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenProperties.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenProperties.java @@ -223,6 +223,8 @@ public interface SasTokenProperties { * Creates a permission configuration to be used when creating a Cosmos shared access signature token. * * @param user the user that will be associated with this shared access signature token. + * @param databaseName the database name that will be associated with this shared access signature token. + * @param containerName the container name that will be associated with this shared access signature token. * @return an instance of {@link SasTokenProperties} that will be used to generated the token. */ @Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) From 5d2a91bdb46072eeb76e984a4bba96e7f59c8b72 Mon Sep 17 00:00:00 2001 From: milismsft-mac Date: Mon, 1 Feb 2021 13:22:39 -0800 Subject: [PATCH 04/11] the encrypted payload must be Url encoded and without the padding in order to reuse current parsing implementation which adds extra logic on how "=" is treated. --- .../azure/cosmos/implementation/sastokens/SasTokenImpl.java | 2 +- .../azure/cosmos/implementation/sastokens/SasTokenTests.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java index 5d2e7fcde09f..12044f22f0cf 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java @@ -217,7 +217,7 @@ private String getSasTokenWithHMACSHA256(String key) { String payload = this.generatePayload(); byte[] payloadBytes = payload.getBytes(StandardCharsets.UTF_8); byte[] digest = macInstance.doFinal(payloadBytes); - String authorizationToken = Utils.encodeBase64String(digest); + String authorizationToken = Base64.getUrlEncoder().withoutPadding().encodeToString(digest); StringBuilder token = new StringBuilder(AUTH_PREFIX) .append(authorizationToken) diff --git a/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java b/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java index 0f1b90a3f0a0..eac23f60868c 100644 --- a/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java +++ b/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java @@ -15,7 +15,7 @@ public class SasTokenTests { private final static String TEST_KEY = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; - private final static String TEST_EXPECTED_SIMPLE_SASTOKEN = "type=sas&ver=1.0&sig=rQNGPRFDzgJbi9/W4fUmuLBGjgtSKjJCWsrtNO72NKk=,dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKMAoxQzIwCjAKNjAKMApGRkZGRkZGRgowCg"; + private final static String TEST_EXPECTED_SIMPLE_SASTOKEN = "type=sas&ver=1.0&sig=rQNGPRFDzgJbi9_W4fUmuLBGjgtSKjJCWsrtNO72NKk,dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKMAoxQzIwCjAKNjAKMApGRkZGRkZGRgowCg"; private final static String TEST_EXPECTED_SIMPLE_PAYLOAD = "user1\n" + "\n" + "/dbs/db1/colls/coll1/\n" + @@ -53,7 +53,7 @@ public void createSimpleSasToken() { .isEqualTo(sasTokenPayload); } - private final static String TEST_EXPECTED_EXPIRY_SASTOKEN = "type=sas&ver=1.0&sig=pCgZFxV9JQN1i3vzYNTfQldW1No7I+MSgN628TZcJAI=,dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKNUZFRTY2MDEKNjIxM0I3MDEKMAo2MAowCkZGRkZGRkZGCjAK"; + private final static String TEST_EXPECTED_EXPIRY_SASTOKEN = "type=sas&ver=1.0&sig=pCgZFxV9JQN1i3vzYNTfQldW1No7I-MSgN628TZcJAI,dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKNUZFRTY2MDEKNjIxM0I3MDEKMAo2MAowCkZGRkZGRkZGCjAK"; private final static String TEST_EXPECTED_EXPIRY_PAYLOAD = "user1\n" + "\n" + "/dbs/db1/colls/coll1/\n" + From 8d573dfc81c1f6756cf38ba5406a743ea1002c89 Mon Sep 17 00:00:00 2001 From: Milis Date: Mon, 1 Feb 2021 13:29:43 -0800 Subject: [PATCH 05/11] add handling for Sas Token type in the CosmosClient implementation. --- .../AuthorizationTokenType.java | 5 ++- .../cosmos/implementation/Constants.java | 1 + .../implementation/RxDocumentClientImpl.java | 25 +++++++++--- .../BarrierRequestHelper.java | 4 ++ .../SasTokenAuthorizationHelper.java | 39 +++++++++++++++++++ 5 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenAuthorizationHelper.java diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/AuthorizationTokenType.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/AuthorizationTokenType.java index 603496dcb09f..55d97f788bc7 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/AuthorizationTokenType.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/AuthorizationTokenType.java @@ -8,9 +8,10 @@ public enum AuthorizationTokenType { PrimaryReadonlyMasterKey, SecondaryMasterKey, SecondaryReadonlyMasterKey, - SystemReadOnly, + SystemReadOnly, SystemReadWrite, SystemAll, ResourceToken, - AadToken + AadToken, + SasToken } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Constants.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Constants.java index 5af5deb1448b..b63c157ef358 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Constants.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Constants.java @@ -170,6 +170,7 @@ public static final class Properties { public static final String MASTER_TOKEN = "master"; public static final String RESOURCE_TOKEN = "resource"; public static final String AAD_TOKEN = "aad"; + public static final String SAS_TOKEN = "sas"; public static final String TOKEN_VERSION = "1.0"; public static final String AUTH_SCHEMA_TYPE = "type"; public static final String AUTH_VERSION = "ver"; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java index 5dfeff17b83a..6b3c34961bee 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java @@ -48,6 +48,7 @@ import com.azure.cosmos.implementation.routing.PartitionKeyInternalHelper; import com.azure.cosmos.implementation.routing.PartitionKeyRangeIdentity; import com.azure.cosmos.implementation.routing.Range; +import com.azure.cosmos.implementation.sastokens.SasTokenAuthorizationHelper; import com.azure.cosmos.models.CosmosChangeFeedRequestOptions; import com.azure.cosmos.models.CosmosItemIdentity; import com.azure.cosmos.models.CosmosQueryRequestOptions; @@ -120,6 +121,7 @@ public class RxDocumentClientImpl implements AsyncDocumentClient, IAuthorization private final BaseAuthorizationTokenProvider authorizationTokenProvider; private final UserAgentContainer userAgentContainer; private final boolean hasAuthKeyResourceToken; + private final boolean hasAuthKeySasToken; private final Configs configs; private final boolean connectionSharingAcrossClientsEnabled; private AzureKeyCredential credential; @@ -291,19 +293,30 @@ private RxDocumentClientImpl(URI serviceEndpoint, this.authorizationTokenType = AuthorizationTokenType.Invalid; if (this.credential != null) { - hasAuthKeyResourceToken = false; + this.hasAuthKeyResourceToken = false; + this.hasAuthKeySasToken = false; this.authorizationTokenProvider = new BaseAuthorizationTokenProvider(this.credential); } else if (masterKeyOrResourceToken != null && ResourceTokenAuthorizationHelper.isResourceToken(masterKeyOrResourceToken)) { this.authorizationTokenProvider = null; - hasAuthKeyResourceToken = true; + this.hasAuthKeyResourceToken = true; + this.hasAuthKeySasToken = false; this.authorizationTokenType = AuthorizationTokenType.ResourceToken; - } else if(masterKeyOrResourceToken != null && !ResourceTokenAuthorizationHelper.isResourceToken(masterKeyOrResourceToken)) { + } else if (masterKeyOrResourceToken != null && SasTokenAuthorizationHelper.isSasToken(masterKeyOrResourceToken)) { + this.authorizationTokenProvider = null; + this.hasAuthKeyResourceToken = false; + this.hasAuthKeySasToken = true; + this.authorizationTokenType = AuthorizationTokenType.SasToken; + } else if(masterKeyOrResourceToken != null + && !ResourceTokenAuthorizationHelper.isResourceToken(masterKeyOrResourceToken) + && !SasTokenAuthorizationHelper.isSasToken(masterKeyOrResourceToken)) { this.credential = new AzureKeyCredential(this.masterKeyOrResourceToken); - hasAuthKeyResourceToken = false; + this.hasAuthKeyResourceToken = false; + this.hasAuthKeySasToken = false; this.authorizationTokenType = AuthorizationTokenType.PrimaryMasterKey; this.authorizationTokenProvider = new BaseAuthorizationTokenProvider(this.credential); } else { - hasAuthKeyResourceToken = false; + this.hasAuthKeyResourceToken = false; + this.hasAuthKeySasToken = false; this.authorizationTokenProvider = null; if (tokenCredential != null) { this.tokenCredentialScopes = new String[] { @@ -1500,7 +1513,7 @@ public String getUserAuthorizationToken(String resourceName, } else if (credential != null) { return this.authorizationTokenProvider.generateKeyAuthorizationSignature(requestVerb, resourceName, resourceType, headers); - } else if (masterKeyOrResourceToken != null && hasAuthKeyResourceToken && resourceTokensMap == null) { + } else if (masterKeyOrResourceToken != null && (hasAuthKeyResourceToken || hasAuthKeySasToken) && resourceTokensMap == null) { return masterKeyOrResourceToken; } else { assert resourceTokensMap != null; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/directconnectivity/BarrierRequestHelper.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/directconnectivity/BarrierRequestHelper.java index 71f8ce5447e8..4f209cfad367 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/directconnectivity/BarrierRequestHelper.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/directconnectivity/BarrierRequestHelper.java @@ -102,6 +102,10 @@ public static Mono createAsync( authorizationToken = request.getHeaders().get(HttpConstants.HttpHeaders.AUTHORIZATION); break; + case SasToken: + authorizationToken = request.getHeaders().get(HttpConstants.HttpHeaders.AUTHORIZATION); + break; + case AadToken: hasAadToken = true; break; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenAuthorizationHelper.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenAuthorizationHelper.java new file mode 100644 index 000000000000..e095f4adec8b --- /dev/null +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenAuthorizationHelper.java @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.cosmos.implementation.sastokens; + +import com.azure.cosmos.implementation.Constants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is used internally and act as a helper in authorization of + * resources from SAS tokens. + * + */ +public class SasTokenAuthorizationHelper { + private static final Logger logger = LoggerFactory.getLogger(SasTokenAuthorizationHelper.class); + + /** + * This method help to differentiate between master key and SAS token. + * + * @param token SAS token provided. + * @return Whether given token is a SAS token or not. + */ + public static boolean isSasToken(String token) { + int typeSeparatorPosition = token.indexOf('&'); + if (typeSeparatorPosition == -1) { + return false; + } + String authType = token.substring(0, typeSeparatorPosition); + int typeKeyValueSepartorPosition = authType.indexOf('='); + if (typeKeyValueSepartorPosition == -1 || !authType.substring(0, typeKeyValueSepartorPosition) + .equalsIgnoreCase(Constants.Properties.AUTH_SCHEMA_TYPE)) { + return false; + } + + String authTypeValue = authType.substring(typeKeyValueSepartorPosition + 1); + + return authTypeValue.equalsIgnoreCase(Constants.Properties.SAS_TOKEN); + } +} From 7d5a04940c27cdf16010480432395b34f591c00c Mon Sep 17 00:00:00 2001 From: milismsft-mac Date: Tue, 2 Feb 2021 11:51:23 -0800 Subject: [PATCH 06/11] update SAS token separator --- .../azure/cosmos/implementation/sastokens/SasTokenImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java index 12044f22f0cf..110ddc30252b 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java @@ -68,6 +68,7 @@ */ public class SasTokenImpl implements SasTokenProperties { private static final String AUTH_PREFIX = "type=sas&ver=1.0&sig="; + private static final String SAS_TOKEN_SEPARATOR = ";"; String user; String userTag; @@ -221,7 +222,7 @@ private String getSasTokenWithHMACSHA256(String key) { StringBuilder token = new StringBuilder(AUTH_PREFIX) .append(authorizationToken) - .append(",") + .append(SAS_TOKEN_SEPARATOR) .append(payload); return token.toString(); From 5f818bf2a1bc5e1a739b76cbec0aaa3eb158eeed Mon Sep 17 00:00:00 2001 From: milismsft-mac Date: Tue, 2 Feb 2021 13:18:27 -0800 Subject: [PATCH 07/11] test fixes --- .../cosmos/implementation/sastokens/SasTokenTests.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java b/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java index eac23f60868c..019caae92849 100644 --- a/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java +++ b/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java @@ -15,7 +15,7 @@ public class SasTokenTests { private final static String TEST_KEY = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; - private final static String TEST_EXPECTED_SIMPLE_SASTOKEN = "type=sas&ver=1.0&sig=rQNGPRFDzgJbi9_W4fUmuLBGjgtSKjJCWsrtNO72NKk,dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKMAoxQzIwCjAKNjAKMApGRkZGRkZGRgowCg"; + private final static String TEST_EXPECTED_SIMPLE_SASTOKEN = "type=sas&ver=1.0&sig=rQNGPRFDzgJbi9_W4fUmuLBGjgtSKjJCWsrtNO72NKk;dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKMAoxQzIwCjAKNjAKMApGRkZGRkZGRgowCg"; private final static String TEST_EXPECTED_SIMPLE_PAYLOAD = "user1\n" + "\n" + "/dbs/db1/colls/coll1/\n" + @@ -44,7 +44,7 @@ public void createSimpleSasToken() { String[] tokenSegments = sasTokenValue.split("&"); assertThat(3).isEqualTo(tokenSegments.length); - String[] sasTokenParts = tokenSegments[2].split(","); + String[] sasTokenParts = tokenSegments[2].split(";"); assertThat(2).isEqualTo(sasTokenParts.length); byte[] decodedString = Base64.getDecoder().decode(sasTokenParts[1]); @@ -53,7 +53,7 @@ public void createSimpleSasToken() { .isEqualTo(sasTokenPayload); } - private final static String TEST_EXPECTED_EXPIRY_SASTOKEN = "type=sas&ver=1.0&sig=pCgZFxV9JQN1i3vzYNTfQldW1No7I-MSgN628TZcJAI,dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKNUZFRTY2MDEKNjIxM0I3MDEKMAo2MAowCkZGRkZGRkZGCjAK"; + private final static String TEST_EXPECTED_EXPIRY_SASTOKEN = "type=sas&ver=1.0&sig=pCgZFxV9JQN1i3vzYNTfQldW1No7I-MSgN628TZcJAI;dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKNUZFRTY2MDEKNjIxM0I3MDEKMAo2MAowCkZGRkZGRkZGCjAK"; private final static String TEST_EXPECTED_EXPIRY_PAYLOAD = "user1\n" + "\n" + "/dbs/db1/colls/coll1/\n" + @@ -84,7 +84,7 @@ public void createSasTokenWithExpiryTime() { String[] tokenSegments = sasTokenValue.split("&"); assertThat(3).isEqualTo(tokenSegments.length); - String[] sasTokenParts = tokenSegments[2].split(","); + String[] sasTokenParts = tokenSegments[2].split(";"); assertThat(2).isEqualTo(sasTokenParts.length); byte[] decodedString = Base64.getDecoder().decode(sasTokenParts[1]); From 75eb9156f2304b3fdf0123c35b76a730fd6ecc36 Mon Sep 17 00:00:00 2001 From: milismsft-mac Date: Tue, 2 Feb 2021 16:35:33 -0800 Subject: [PATCH 08/11] Backend does not support encoding without padding currently; to ease the implementation, we will use default Base64 encoding. --- .../azure/cosmos/implementation/sastokens/SasTokenTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java b/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java index 019caae92849..2cbe61d55d85 100644 --- a/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java +++ b/sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/implementation/sastokens/SasTokenTests.java @@ -15,7 +15,7 @@ public class SasTokenTests { private final static String TEST_KEY = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; - private final static String TEST_EXPECTED_SIMPLE_SASTOKEN = "type=sas&ver=1.0&sig=rQNGPRFDzgJbi9_W4fUmuLBGjgtSKjJCWsrtNO72NKk;dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKMAoxQzIwCjAKNjAKMApGRkZGRkZGRgowCg"; + private final static String TEST_EXPECTED_SIMPLE_SASTOKEN = "type=sas&ver=1.0&sig=nRJ8Lp6toJT3SVzplvdNud5Z7LnSPxEf2/suT4up0X4=;dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKMAoxQzIwCjAKNjAKMApGRkZGRkZGRgowCg=="; private final static String TEST_EXPECTED_SIMPLE_PAYLOAD = "user1\n" + "\n" + "/dbs/db1/colls/coll1/\n" + @@ -53,7 +53,7 @@ public void createSimpleSasToken() { .isEqualTo(sasTokenPayload); } - private final static String TEST_EXPECTED_EXPIRY_SASTOKEN = "type=sas&ver=1.0&sig=pCgZFxV9JQN1i3vzYNTfQldW1No7I-MSgN628TZcJAI;dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKNUZFRTY2MDEKNjIxM0I3MDEKMAo2MAowCkZGRkZGRkZGCjAK"; + private final static String TEST_EXPECTED_EXPIRY_SASTOKEN = "type=sas&ver=1.0&sig=pCgZFxV9JQN1i3vzYNTfQldW1No7I+MSgN628TZcJAI=;dXNlcjEKCi9kYnMvZGIxL2NvbGxzL2NvbGwxLwoKNUZFRTY2MDEKNjIxM0I3MDEKMAo2MAowCkZGRkZGRkZGCjAK"; private final static String TEST_EXPECTED_EXPIRY_PAYLOAD = "user1\n" + "\n" + "/dbs/db1/colls/coll1/\n" + From 391ca9874408fbc1cac034cf59a93e357c95002f Mon Sep 17 00:00:00 2001 From: milismsft-mac Date: Tue, 2 Feb 2021 17:17:20 -0800 Subject: [PATCH 09/11] Add permission wildcards at container level, read-any and full (default read-any if not other permission is set). --- .../sastokens/DataPlanePermissionScope.java | 4 ++-- .../sastokens/SasTokenImpl.java | 21 ++++++++++++++----- .../cosmos/models/SasTokenPermissionKind.java | 6 ++++-- .../cosmos/models/SasTokenProperties.java | 1 + 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/DataPlanePermissionScope.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/DataPlanePermissionScope.java index 27667fd9cd06..003f129854ea 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/DataPlanePermissionScope.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/DataPlanePermissionScope.java @@ -97,13 +97,13 @@ public enum DataPlanePermissionScope { /** * Composite read scope. */ - SCOPE_CONTAINER_READ_ALL_ACCESS("ContainersReadAllAccess", SCOPE_CONTAINER_READ_ALL_ACCESS_VALUE), + SCOPE_CONTAINER_READ_ALL_ACCESS("ContainerReadAllAccess", SCOPE_CONTAINER_READ_ALL_ACCESS_VALUE), SCOPE_ITEM_READ_ALL_ACCESS("ItemReadAllAccess", SCOPE_ITEM_READ_ALL_ACCESS_VALUE), /** * Composite write scope. */ - SCOPE_CONTAINER_WRITE_ALL_ACCESS("ContainersWriteAllAccess", SCOPE_CONTAINER_WRITE_ALL_ACCESS_VALUE), + SCOPE_CONTAINER_WRITE_ALL_ACCESS("ContainerWriteAllAccess", SCOPE_CONTAINER_WRITE_ALL_ACCESS_VALUE), SCOPE_ITEM_WRITE_ALL_ACCESS("ItemWriteAllAccess", SCOPE_ITEM_WRITE_ALL_ACCESS_VALUE), NONE("None", 0x0); diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java index 110ddc30252b..1b1c0f53c931 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java @@ -23,6 +23,7 @@ import static com.azure.cosmos.implementation.sastokens.ControlPlanePermissionScope.SCOPE_CONTAINER_READ; import static com.azure.cosmos.implementation.sastokens.ControlPlanePermissionScope.SCOPE_CONTAINER_READ_OFFER; +import static com.azure.cosmos.implementation.sastokens.ControlPlanePermissionScope.SCOPE_CONTAINER_WRITE_ALL_ACCESS; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_CREATE_ITEMS; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_CREATE_STORED_PROCEDURES; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_CREATE_TRIGGERS; @@ -203,7 +204,7 @@ private String generatePayload() { .append(String.format("%X", this.dataPlaneReaderScope)). append("\n") .append(String.format("%X", this.dataPlaneWriterScope)). append("\n"); - return Base64.getUrlEncoder().withoutPadding().encodeToString(payload.toString().getBytes(StandardCharsets.UTF_8)); + return Utils.encodeBase64String(payload.toString().getBytes(StandardCharsets.UTF_8)); } private String getSasTokenWithHMACSHA256(String key) { @@ -218,7 +219,7 @@ private String getSasTokenWithHMACSHA256(String key) { String payload = this.generatePayload(); byte[] payloadBytes = payload.getBytes(StandardCharsets.UTF_8); byte[] digest = macInstance.doFinal(payloadBytes); - String authorizationToken = Base64.getUrlEncoder().withoutPadding().encodeToString(digest); + String authorizationToken = Utils.encodeBase64String(digest); StringBuilder token = new StringBuilder(AUTH_PREFIX) .append(authorizationToken) @@ -505,7 +506,7 @@ public SasTokenProperties addPermission(SasTokenPermissionKind permissionKind) { this.dataPlaneWriterScope |= SCOPE_CONTAINER_DELETE_USER_DEFINED_FUNCTIONS.value(); break; } - case CONTAINER_EXECUTE_STORE_PROCEDURES: { + case CONTAINER_EXECUTE_STORED_PROCEDURES: { this.dataPlaneWriterScope |= SCOPE_CONTAINER_EXECUTE_STORED_PROCEDURES.value(); break; } @@ -517,14 +518,24 @@ public SasTokenProperties addPermission(SasTokenPermissionKind permissionKind) { this.dataPlaneWriterScope |= SCOPE_CONTAINER_DELETE_CONFLICTS.value(); break; } + case CONTAINER_READ_ANY: { + this.dataPlaneReaderScope |= SCOPE_CONTAINER_READ_ALL_ACCESS.value(); + break; + } + case CONTAINER_FULL_ACCESS: { + this.dataPlaneReaderScope |= SCOPE_CONTAINER_READ_ALL_ACCESS.value(); + this.dataPlaneWriterScope |= SCOPE_CONTAINER_WRITE_ALL_ACCESS.value(); + break; + } + // Cosmos container item scope. case ITEM_FULL_ACCESS: { this.dataPlaneWriterScope |= SCOPE_ITEM_WRITE_ALL_ACCESS.value(); - this.addPermission(SasTokenPermissionKind.ITEM_READ_ALL_ACCESS); + this.addPermission(SasTokenPermissionKind.ITEM_READ_ANY); break; } - case ITEM_READ_ALL_ACCESS: { + case ITEM_READ_ANY: { this.dataPlaneReaderScope |= SCOPE_ITEM_READ_ALL_ACCESS.value(); break; } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPermissionKind.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPermissionKind.java index 89c0d4316f13..b1a5bbbe3dd3 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPermissionKind.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPermissionKind.java @@ -31,14 +31,16 @@ public enum SasTokenPermissionKind { CONTAINER_READ_USER_DEFINED_FUNCTIONS, CONTAINER_REPLACE_USER_DEFINED_FUNCTIONS, CONTAINER_DELETE_USER_DEFINED_FUNCTIONS, - CONTAINER_EXECUTE_STORE_PROCEDURES, + CONTAINER_EXECUTE_STORED_PROCEDURES, CONTAINER_READ_CONFLICTS, CONTAINER_DELETE_CONFLICTS, + CONTAINER_READ_ANY, + CONTAINER_FULL_ACCESS, /** * Cosmos DB Item scope. */ - ITEM_READ_ALL_ACCESS, + ITEM_READ_ANY, ITEM_FULL_ACCESS, ITEM_READ, ITEM_REPLACE, diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenProperties.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenProperties.java index 0c79b711a705..ccf021c4655e 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenProperties.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenProperties.java @@ -212,6 +212,7 @@ public interface SasTokenProperties { /** * Adds a permission setting to execute specific Cosmos operation or set of operations. + * If no specific permission was set, default is read permissions at the container level. * * @param permissionKind the permission setting which allows execution of specific Cosmos operation or set of operations. * @return the current permission configuration object. From 2ece12c1714312db9d997ea6ad954d2b3169af04 Mon Sep 17 00:00:00 2001 From: milismsft-mac Date: Wed, 3 Feb 2021 14:09:18 -0800 Subject: [PATCH 10/11] Remove some of the permission scopes that are not required in the first version of the SAS token. --- .../sastokens/SasTokenImpl.java | 94 +------------------ .../cosmos/models/SasTokenPermissionKind.java | 32 +------ 2 files changed, 2 insertions(+), 124 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java index 1b1c0f53c931..143b49579670 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java @@ -458,63 +458,12 @@ public SasTokenProperties addPermission(SasTokenPermissionKind permissionKind) { this.dataPlaneReaderScope |= SCOPE_CONTAINER_READ_FEEDS.value(); break; } - case CONTAINER_CREATE_STORE_PROCEDURES: { - this.dataPlaneWriterScope |= SCOPE_CONTAINER_CREATE_STORED_PROCEDURES.value(); - break; - } - case CONTAINER_READ_STORE_PROCEDURES: { - this.dataPlaneReaderScope |= SCOPE_CONTAINER_READ_STORED_PROCEDURES.value(); - break; - } - case CONTAINER_REPLACE_STORE_PROCEDURES: { - this.dataPlaneWriterScope |= SCOPE_CONTAINER_REPLACE_STORED_PROCEDURES.value(); - break; - } - case CONTAINER_DELETE_STORE_PROCEDURES: { - this.dataPlaneWriterScope |= SCOPE_CONTAINER_DELETE_STORED_PROCEDURES.value(); - break; - } - case CONTAINER_CREATE_TRIGGERS: { - this.dataPlaneWriterScope |= SCOPE_CONTAINER_CREATE_TRIGGERS.value(); - break; - } - case CONTAINER_READ_TRIGGERS: { - this.dataPlaneReaderScope |= SCOPE_CONTAINER_READ_TRIGGERS.value(); - break; - } - case CONTAINER_REPLACE_TRIGGERS: { - this.dataPlaneWriterScope |= SCOPE_CONTAINER_REPLACE_TRIGGERS.value(); - break; - } - case CONTAINER_DELETE_TRIGGERS: { - this.dataPlaneWriterScope |= SCOPE_CONTAINER_DELETE_TRIGGERS.value(); - break; - } - case CONTAINER_CREATE_USER_DEFINED_FUNCTIONS: { - this.dataPlaneWriterScope |= SCOPE_CONTAINER_CREATE_USER_DEFINED_FUNCTIONS.value(); - break; - } - case CONTAINER_READ_USER_DEFINED_FUNCTIONS: { - this.dataPlaneReaderScope |= SCOPE_CONTAINER_READ_USER_DEFINED_FUNCTIONS.value(); - break; - } - case CONTAINER_REPLACE_USER_DEFINED_FUNCTIONS: { - this.dataPlaneWriterScope |= SCOPE_CONTAINER_REPLACE_USER_DEFINED_FUNCTIONS.value(); - break; - } - case CONTAINER_DELETE_USER_DEFINED_FUNCTIONS: { - this.dataPlaneWriterScope |= SCOPE_CONTAINER_DELETE_USER_DEFINED_FUNCTIONS.value(); - break; - } case CONTAINER_EXECUTE_STORED_PROCEDURES: { this.dataPlaneWriterScope |= SCOPE_CONTAINER_EXECUTE_STORED_PROCEDURES.value(); break; } - case CONTAINER_READ_CONFLICTS: { + case CONTAINER_MANAGE_CONFLICTS: { this.dataPlaneReaderScope |= SCOPE_CONTAINER_READ_CONFLICTS.value(); - break; - } - case CONTAINER_DELETE_CONFLICTS: { this.dataPlaneWriterScope |= SCOPE_CONTAINER_DELETE_CONFLICTS.value(); break; } @@ -556,52 +505,11 @@ public SasTokenProperties addPermission(SasTokenPermissionKind permissionKind) { break; } - // Cosmos container store procedure scope. - case STORE_PROCEDURE_READ: { - this.dataPlaneReaderScope |= SCOPE_STORED_PROCEDURE_READ.value(); - break; - } - case STORE_PROCEDURE_REPLACE: { - this.dataPlaneWriterScope |= SCOPE_STORED_PROCEDURE_REPLACE.value(); - break; - } - case STORE_PROCEDURE_DELETE: { - this.dataPlaneWriterScope |= SCOPE_STORED_PROCEDURE_DELETE.value(); - break; - } case STORE_PROCEDURE_EXECUTE: { this.dataPlaneWriterScope |= SCOPE_STORED_PROCEDURE_EXECUTE.value(); break; } - // Cosmos container user defined function scope. - case USER_DEFINED_FUNCTION_READ: { - this.dataPlaneReaderScope |= SCOPE_USER_DEFINED_FUNCTION_READ.value(); - break; - } - case USER_DEFINED_FUNCTION_REPLACE: { - this.dataPlaneWriterScope |= SCOPE_USER_DEFINED_FUNCTION_REPLACE.value(); - break; - } - case USER_DEFINED_FUNCTION_DELETE: { - this.dataPlaneWriterScope |= SCOPE_USER_DEFINED_FUNCTION_DELETE.value(); - break; - } - - // Cosmos container trigger scope. - case TRIGGER_READ: { - this.dataPlaneReaderScope |= SCOPE_TRIGGER_READ.value(); - break; - } - case TRIGGER_REPLACE: { - this.dataPlaneWriterScope |= SCOPE_TRIGGER_REPLACE.value(); - break; - } - case TRIGGER_DELETE: { - this.dataPlaneWriterScope |= SCOPE_TRIGGER_DELETE.value(); - break; - } - default: throw new IllegalArgumentException("permissionKind"); } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPermissionKind.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPermissionKind.java index b1a5bbbe3dd3..8f63ef469a56 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPermissionKind.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/SasTokenPermissionKind.java @@ -19,21 +19,8 @@ public enum SasTokenPermissionKind { CONTAINER_DELETE_ITEMS, CONTAINER_EXECUTE_QUERIES, CONTAINER_READ_FEEDS, - CONTAINER_CREATE_STORE_PROCEDURES, - CONTAINER_READ_STORE_PROCEDURES, - CONTAINER_REPLACE_STORE_PROCEDURES, - CONTAINER_DELETE_STORE_PROCEDURES, - CONTAINER_CREATE_TRIGGERS, - CONTAINER_READ_TRIGGERS, - CONTAINER_REPLACE_TRIGGERS, - CONTAINER_DELETE_TRIGGERS, - CONTAINER_CREATE_USER_DEFINED_FUNCTIONS, - CONTAINER_READ_USER_DEFINED_FUNCTIONS, - CONTAINER_REPLACE_USER_DEFINED_FUNCTIONS, - CONTAINER_DELETE_USER_DEFINED_FUNCTIONS, CONTAINER_EXECUTE_STORED_PROCEDURES, - CONTAINER_READ_CONFLICTS, - CONTAINER_DELETE_CONFLICTS, + CONTAINER_MANAGE_CONFLICTS, CONTAINER_READ_ANY, CONTAINER_FULL_ACCESS, @@ -50,22 +37,5 @@ public enum SasTokenPermissionKind { /** * Cosmos DB Store Procedure scope. */ - STORE_PROCEDURE_READ, - STORE_PROCEDURE_REPLACE, - STORE_PROCEDURE_DELETE, STORE_PROCEDURE_EXECUTE, - - /** - * Cosmos DB User Defined Function scope. - */ - USER_DEFINED_FUNCTION_READ, - USER_DEFINED_FUNCTION_REPLACE, - USER_DEFINED_FUNCTION_DELETE, - - /** - * Cosmos DB Trigger scope. - */ - TRIGGER_READ, - TRIGGER_REPLACE, - TRIGGER_DELETE, } From 800d28e4142e386ed6f39682113a65289e636b0b Mon Sep 17 00:00:00 2001 From: milismsft-mac Date: Wed, 3 Feb 2021 16:50:13 -0800 Subject: [PATCH 11/11] fixes for spotbugs related build errors. --- .../BarrierRequestHelper.java | 3 -- .../sastokens/SasTokenImpl.java | 29 +++---------------- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/directconnectivity/BarrierRequestHelper.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/directconnectivity/BarrierRequestHelper.java index 4f209cfad367..e0bcf95792c5 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/directconnectivity/BarrierRequestHelper.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/directconnectivity/BarrierRequestHelper.java @@ -99,9 +99,6 @@ public static Mono createAsync( case ResourceToken: - authorizationToken = request.getHeaders().get(HttpConstants.HttpHeaders.AUTHORIZATION); - break; - case SasToken: authorizationToken = request.getHeaders().get(HttpConstants.HttpHeaders.AUTHORIZATION); break; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java index 143b49579670..218d50cc31df 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/sastokens/SasTokenImpl.java @@ -25,26 +25,14 @@ import static com.azure.cosmos.implementation.sastokens.ControlPlanePermissionScope.SCOPE_CONTAINER_READ_OFFER; import static com.azure.cosmos.implementation.sastokens.ControlPlanePermissionScope.SCOPE_CONTAINER_WRITE_ALL_ACCESS; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_CREATE_ITEMS; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_CREATE_STORED_PROCEDURES; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_CREATE_TRIGGERS; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_CREATE_USER_DEFINED_FUNCTIONS; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_DELETE_CONFLICTS; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_DELETE_ITEMS; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_DELETE_STORED_PROCEDURES; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_DELETE_TRIGGERS; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_DELETE_USER_DEFINED_FUNCTIONS; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_EXECUTE_QUERIES; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_EXECUTE_STORED_PROCEDURES; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_READ_ALL_ACCESS; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_READ_CONFLICTS; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_READ_FEEDS; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_READ_STORED_PROCEDURES; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_READ_TRIGGERS; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_READ_USER_DEFINED_FUNCTIONS; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_REPLACE_ITEMS; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_REPLACE_STORED_PROCEDURES; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_REPLACE_TRIGGERS; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_REPLACE_USER_DEFINED_FUNCTIONS; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_CONTAINER_UPSERT_ITEMS; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_ITEM_DELETE; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_ITEM_READ; @@ -52,16 +40,7 @@ import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_ITEM_REPLACE; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_ITEM_UPSERT; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_ITEM_WRITE_ALL_ACCESS; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_STORED_PROCEDURE_DELETE; import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_STORED_PROCEDURE_EXECUTE; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_STORED_PROCEDURE_READ; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_STORED_PROCEDURE_REPLACE; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_TRIGGER_DELETE; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_TRIGGER_READ; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_TRIGGER_REPLACE; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_USER_DEFINED_FUNCTION_DELETE; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_USER_DEFINED_FUNCTION_READ; -import static com.azure.cosmos.implementation.sastokens.DataPlanePermissionScope.SCOPE_USER_DEFINED_FUNCTION_REPLACE; /** * Represents the implementation of a permission configuration object to be used when creating a Cosmos shared access @@ -112,8 +91,8 @@ public SasTokenImpl() { * {@literal * user\n * userTag\n - * resourcePathPrefix\n - * partitionRangesCommaSeparated\n + * resourcePath\n + * BASE64_ENCODED_PartitionKeyValues_CommaSeparated\n * epochStartTime\n * epochExpiryTime\n * keyKind\n @@ -194,7 +173,7 @@ private String generatePayload() { StringBuilder payload = new StringBuilder(this.user).append("\n") .append(this.userTag).append("\n") - .append(resourcePrefixPath).append("\n") + .append(this.resourcePath).append("\n") .append(partitionRanges).append("\n") .append(String.format("%X", this.startTime.getEpochSecond())).append("\n") .append(String.format("%X", this.expiryTime.getEpochSecond())).append("\n") @@ -209,7 +188,7 @@ private String generatePayload() { private String getSasTokenWithHMACSHA256(String key) { byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); - byte[] keyDecodedBytes = Utils.Base64Decoder.decode(key); + byte[] keyDecodedBytes = Utils.Base64Decoder.decode(keyBytes); SecretKey signingKey = new SecretKeySpec(keyDecodedBytes, "HMACSHA256"); try { Mac macInstance = Mac.getInstance("HMACSHA256");