Microsoft.Azure.Cosmos.Samples/Usage/UserManagement/UserManagementProgram.cs (221 lines of code) (raw):

namespace Cosmos.Samples.Shared { using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Azure.Cosmos; using Microsoft.Extensions.Configuration; using Newtonsoft.Json; internal class UserManagementProgram { // Async main requires c# 7.1 which is set in the csproj with the LangVersion attribute // <Main> public static async Task Main(string[] args) { try { IConfigurationRoot configuration = new ConfigurationBuilder() .AddJsonFile("appSettings.json") .Build(); string endpoint = configuration["EndPointUrl"]; if (string.IsNullOrEmpty(endpoint)) { throw new ArgumentNullException("Please specify a valid endpoint in the appSettings.json"); } string authKey = configuration["AuthorizationKey"]; if (string.IsNullOrEmpty(authKey) || string.Equals(authKey, "Super secret key")) { throw new ArgumentException("Please specify a valid AuthorizationKey in the appSettings.json"); } //Read the Cosmos endpointUrl and authorisationKeys from configuration //These values are available from the Azure Management Portal on the Cosmos Account Blade under "Keys" //NB > Keep these values in a safe & secure location. Together they provide Administrative access to your Cosmos account using (CosmosClient client = new CosmosClient(endpoint, authKey)) { Database database = null; try { using (await client.GetDatabase("UserManagementDemoDb").DeleteStreamAsync()) { } // Get, or Create, the Database database = await client.CreateDatabaseIfNotExistsAsync("UserManagementDemoDb"); await UserManagementProgram.RunDemoAsync( client, database); } finally { if (database != null) { await database.DeleteStreamAsync(); } } } } catch (CosmosException cre) { Console.WriteLine(cre.ToString()); } catch (Exception e) { Exception baseException = e.GetBaseException(); Console.WriteLine("Error: {0}, Message: {1}", e.Message, baseException.Message); } finally { Console.WriteLine("End of demo, press any key to exit."); Console.ReadKey(); } } // </Main> // <RunDemoAsync> private static async Task RunDemoAsync( CosmosClient client, Database database) { //-------------------------------------------------------------------------------------------------- // We need Two Containers, Two Users, and some permissions for this sample, // So let's go ahead and set these up initially //-------------------------------------------------------------------------------------------------- // Get, or Create, two separate Containers Container container1 = await database.CreateContainerAsync( id: "Container1", partitionKeyPath: "/AccountNumber"); Container container2 = await database.CreateContainerAsync( id: "Container2", partitionKeyPath: "/AccountNumber"); // Insert two documents in to col1 SalesOrder salesOrder1 = new SalesOrder() { Id = "order1", AccountNumber = "partitionKey1" }; await container1.CreateItemAsync<SalesOrder>( salesOrder1, new PartitionKey(salesOrder1.AccountNumber)); SalesOrder salesOrder2 = new SalesOrder() { Id = "order2", AccountNumber = "pk2" }; await container1.CreateItemAsync<SalesOrder>( salesOrder2, new PartitionKey(salesOrder2.AccountNumber)); // Create a user User user1 = await database.CreateUserAsync("Thomas Andersen"); // Get an existing user and permission. // This is a client side reference and does no verification against Cosmos DB. user1 = database.GetUser("Thomas Andersen"); // Verify the user exists UserProperties userProperties = await user1.ReadAsync(); //Add the read permission to the user and validate the user can //read only the container it has access to await ValidateReadPermissions( client.Endpoint.OriginalString, database.Id, container1, user1); // Insert one item in to container 2 SalesOrder salesOrder3 = new SalesOrder() { Id = "doc3", AccountNumber = "partitionKey" }; await container2.CreateItemAsync<SalesOrder>( salesOrder3, new PartitionKey(salesOrder3.AccountNumber)); // Create a new user User user2 = await database.CreateUserAsync("Robin Wakefield"); //Add the all permission to the user for a single item and validate the user can //only access the single item await ValidateAllPermissionsForItem( client.Endpoint.OriginalString, database.Id, container2, user2, salesOrder3); // Add read permission to user1 on container 2 so query has multiple results PermissionResponse permissionUser1Container2Response = await user1.CreatePermissionAsync( new PermissionProperties( id: "permissionUser1Container2", permissionMode: PermissionMode.Read, container: container2)); Permission permissionUser1Container2 = permissionUser1Container2Response; PermissionProperties user1Container2Properties = permissionUser1Container2Response; Console.WriteLine(); Console.WriteLine($"Created {permissionUser1Container2.Id} with resource URI: {user1Container2Properties.ResourceUri}"); // Get an existing permission and token permissionUser1Container2 = user1.GetPermission("permissionUser1Container2"); // Get an existing permission properties user1Container2Properties = await permissionUser1Container2.ReadAsync(); Console.WriteLine($"Read existing {permissionUser1Container2.Id} with resource URI: {user1Container2Properties.ResourceUri}"); // All user1's permissions in a List List<PermissionProperties> user1Permissions = new List<PermissionProperties>(); using (FeedIterator<PermissionProperties> feedIterator = user1.GetPermissionQueryIterator<PermissionProperties>()) { while (feedIterator.HasMoreResults) { FeedResponse<PermissionProperties> permissions = await feedIterator.ReadNextAsync(); user1Permissions.AddRange(permissions); } } } // </RunDemoAsync> // <ValidateAllPermissionsForItem> private static async Task ValidateAllPermissionsForItem( string endpoint, string databaseName, Container container, User user, SalesOrder salesOrder) { // All Permissions on Doc1 for user1 PermissionProperties allPermissionForItem = new PermissionProperties( id: "permissionUserSaleOrder", permissionMode: PermissionMode.All, container: container, resourcePartitionKey: new PartitionKey(salesOrder.AccountNumber)); PermissionProperties allItemPermission = await user.CreatePermissionAsync(allPermissionForItem); // Create a new client with the generated token using (CosmosClient permissionClient = new CosmosClient(endpoint, allItemPermission.Token)) { Container permissionContainer = permissionClient.GetContainer(databaseName, container.Id); SalesOrder readSalesOrder = await permissionContainer.ReadItemAsync<SalesOrder>( salesOrder.Id, new PartitionKey(salesOrder.AccountNumber)); Console.WriteLine(); Console.WriteLine("Read item will all permission succeeded."); // Read sales order item readSalesOrder.OrderDate = DateTime.UtcNow; // Write sales order item await permissionContainer.UpsertItemAsync<SalesOrder>( readSalesOrder, new PartitionKey(salesOrder.AccountNumber)); Console.WriteLine("Upsert item will all permission succeeded."); //try iterate items should fail because the user only has access to single partition key //and therefore cannot access anything outside of that partition key value. try { using (FeedIterator<SalesOrder> itemIterator = permissionContainer.GetItemQueryIterator<SalesOrder>( "select * from T")) { while (itemIterator.HasMoreResults) { await itemIterator.ReadNextAsync(); throw new ApplicationException("Should never get here"); } } } catch (CosmosException ce) when (ce.StatusCode == System.Net.HttpStatusCode.Forbidden) { Console.WriteLine("Item query failed because user has access to only 1 partition"); } } } // </ValidateAllPermissionsForItem> // <ValidateReadPermissions> private static async Task ValidateReadPermissions( string endpoint, string databaseName, Container container, User user) { // Read Permission on container for the user PermissionProperties readPermission = new PermissionProperties( id: "Read", permissionMode: PermissionMode.Read, container: container); PermissionProperties readContainerPermission = await user.CreatePermissionAsync(readPermission); // Create a new client with the generated token using (CosmosClient readClient = new CosmosClient(endpoint, readContainerPermission.Token)) { Container readContainer = readClient.GetContainer(databaseName, container.Id); //try read items should succeed because user1 was granted Read permission on container1 using (FeedIterator<SalesOrder> feedIterator = readContainer.GetItemQueryIterator<SalesOrder>()) { while (feedIterator.HasMoreResults) { FeedResponse<SalesOrder> salesOrders = await feedIterator.ReadNextAsync(); foreach (SalesOrder salesOrder in salesOrders) { Console.WriteLine(JsonConvert.SerializeObject(salesOrder)); } } } //try iterate databases should fail because the user has no Admin rights //but only read access to a single container and therefore //cannot access anything outside of that container. try { using (FeedIterator<DatabaseProperties> databaseIterator = readClient.GetDatabaseQueryIterator<DatabaseProperties>("select T.* from T")) { while (databaseIterator.HasMoreResults) { await databaseIterator.ReadNextAsync(); throw new ApplicationException("Should never get here"); } } } catch (CosmosException ce) when (ce.StatusCode == System.Net.HttpStatusCode.Forbidden) { Console.WriteLine("Database query failed because user has no admin rights"); } } } // </ValidateReadPermissions> } }