3
votes

My DocumentDb app has pretty much a multitenant architecture. I was planning on using a partition key right at the outset to make sure that the whole thing doesn't have to get redesigned if it becomes popular. The current architecture calls for a single heterogeneous collection. A collection per tenant architecture isn't going to work.

All access would include the tenant id so as to prevent leakage. That's not what I'm concerned about.

I need client side read-only access so I was planning on having my server issue resource tokens based on permissions for each user.

But I noticed in the documentation that you can only have one permission per user per resource. I don't want to create permissions for each document. Basically I want a permission for each partition key. That way, the tenant id becomes the constraining factor on client side reads.

Basically, the client device has read only access to all the documents in the collection that share its partition key.

Should the resource token be compromised, it only lasts an hour and it wouldn't give anyone access to the entire collection, just that tenant's data.

Can I have a single DocumentDb database user with multiple read only permissions on the same collection if each permission has a different partition key?

TIA

3

3 Answers

4
votes

It is supported to create multiple permissions per resource (for each partition key). Here's an example:

User user = await client.CreateUserAsync(UriFactory.CreateDatabaseUri("SampleDatabase"), new User { Id = "NewUser" });
Permission permission = await client.CreatePermissionAsync(
    user.SelfLink, 
    new Permission
    {
        Id = "ReadA",
        PermissionMode = PermissionMode.Read,
        ResourcePartitionKey = new PartitionKey("Andersen"),
        ResourceLink = collection.SelfLink
    });

Permission permission2 = await client.CreatePermissionAsync(
    user.SelfLink,
    new Permission
    {
        Id = "ReadW",
        PermissionMode = PermissionMode.Read,
        ResourcePartitionKey = new PartitionKey("Wakefield"),
        ResourceLink = collection.SelfLink
    });

The permissions apply to all documents that have the same partition key. So when you access a document with the partition key with permissions, DocumentDB returns the document successfully, but with another partition key, DocumentDB returns an authorization error:

DocumentClient restrictedClient1 = new DocumentClient(
   new Uri("https://FILLME:443/"), 
   permission.Token);

// Succeeds
await restrictedClient1.ReadDocumentAsync(
    UriFactory.CreateDocumentUri("SampleDatabase", "SampleCollection", "AndersenFamily"),
    new RequestOptions { PartitionKey = new PartitionKey("Andersen") });

// Fails
await restrictedClient1.ReadDocumentAsync(
    UriFactory.CreateDocumentUri("SampleDatabase", "SampleCollection", "WakefieldFamily"),
    new RequestOptions { PartitionKey = new PartitionKey("Wakefield") });
1
votes

This answer helped me:

Cosmos DB partitioned access to a database

Using the code provided by the poster, I was able to get partitioned permissions to work.

0
votes

Finally just wrote a bunch of tests to see if I could create two permissions, each with a different name and different partition key, but for the same user and the same READ permission on the same collection. No go. The second one resulted in a Conflict result.

So, trying to protect a tenant's data by issuing only resource tokens for their partition in a single collection doesn't work.

:(

The only alternative would be to create two different collections, duplicating data when necessary. At this point, I'm not sure how paranoid I am, or should be, about security.

I'm going to punt and run it all through a backend. No one gets any keys.