Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Passing a list of permissions to the DocumentClient constructor doesn't work, but passing a single resource token does #472

Open
thomaslevesque opened this issue Apr 6, 2018 · 14 comments

Comments

@thomaslevesque
Copy link

I have a database with a collection that is partitioned, and each user can only access one partition. I create the users and permissions like this:

public static async Task<User> CreateUserAndPermissionAsync(this DocumentClient client, string userId)
{
    var dbUri = UriFactory.CreateDatabaseUri(Constants.DatabaseId);
    var user = await client.CreateUserAsync(dbUri, new User { Id = userId });
    var collectionUri = UriFactory.CreateDocumentCollectionUri(Constants.DatabaseId, Constants.CollectionId);
    var permission = await client.CreatePermissionAsync(
        user.Resource.SelfLink,
        new Permission
        {
            Id = "MyPermission",
            PermissionMode = PermissionMode.All,
            ResourceLink = collectionUri.ToString(),
            ResourcePartitionKey = new PartitionKey(userId)
        });
    return user.Resource;
}

If I create a DocumentClient for a user by passing the list of permissions like this:

public static async Task<DocumentClient> GetClientForUserAsync(this DocumentClient client, string userId)
{
     var userUri = UriFactory.CreateUserUri(Constants.DatabaseId, userId).ToString();
     var permissionsUri = $"{userUri}/permissions";
     var permissions = (await client.ReadPermissionFeedAsync(permissionsUri)).ToList();
     return new DocumentClient(
         client.ServiceEndpoint,
         permissions,
         client.ConnectionPolicy);
}

Then querying the collection fails with this error:

UnauthorizedException: The client does not have any valid token for the requested resource dbs/MyDatabase/colls/MyCollection

But I know that it's not true; the list of permissions I passed to the DocumentClient constructor has exactly one permission, whose ResourceLink is dbs/MyDatabase/colls/MyCollection.

On the other hand, if I create the DocumentClient by passing a single resource token, it works fine:

public static async Task<DocumentClient> GetClientForUserAsync(this DocumentClient client, string userId)
{
     var userUri = UriFactory.CreateUserUri(Constants.DatabaseId, userId).ToString();
     var permissionsUri = $"{userUri}/permissions";
     var permissions = (await client.ReadPermissionFeedAsync(permissionsUri)).ToList();
     return new DocumentClient(
         client.ServiceEndpoint,
         permissions[0].Token,
         client.ConnectionPolicy);
}

It looks like a bug, but this sample does the same thing and works fine. As far as I can tell, the only difference between my code and the sample is that I use CreateDocumentQuery (with the PartitionKey set in the FeedOptions) whereas the sample uses ReadDocumentFeedAsync.

@thomaslevesque
Copy link
Author

As far as I can tell, the only difference between my code and the sample is that I use CreateDocumentQuery (with the PartitionKey set in the FeedOptions) whereas the sample uses ReadDocumentFeedAsync.

I changed my code to use ReadDocumentFeedAsync, and I'm getting the same error.

Why doesn't DocumentClient find the appropriate token when it's clearly there?

@thomaslevesque
Copy link
Author

Hello? Is anyone following this repo?

@kirankumarkolli
Copy link
Member

@thomaslevesque what are the expectation of query with a partition key limiter? Do you expect the query to filter based on partition key if so could you please try adding it as explicit filter in the query.

@thomaslevesque
Copy link
Author

@thomaslevesque what are the expectation of query with a partition key limiter? Do you expect the query to filter based on partition key if so could you please try adding it as explicit filter in the que

Yes, I expect it to filter the query based on the partition key (which works, if I pass a single resource token to the DocumentClient constructor). I tried to add an explicit filter on the partition key in the query, it doesn't change anything.

In short:

  • using the DocumentClient(Uri serviceEndpoint, string authKeyOrResourceToken) constructor (with a resource token as the authKeyOrResourceToken argument) always works as expected
  • using the DocumentClient(Uri serviceEndpoint, IList<Permission> permissionFeed) constructor never works

@kirankumarkolli
Copy link
Member

@thomaslevesque am assuming that you are unblocked on the query part with an explicit token.

On list of permissions issue: Are there any overlapping permissions (Same Resource with read/write permissions) which can cause conflict?

@thomaslevesque
Copy link
Author

@thomaslevesque am assuming that you are unblocked on the query part with an explicit token.

Well, in my current scenario I only need one token, so I'm not blocked, but it might change in the future.

On list of permissions issue: Are there any overlapping permissions (Same Resource with read/write permissions) which can cause conflict?

No, the list contains a single permission.

@YouNeedTea
Copy link

Seems like it has to do with how the library selects the token to use. When you're using the user-friendly names for db/collection you'll run into problems, but when using the db/collection IDs you'll be fine.
I ran into the same issue, and managed to work around it by using:
_documentClient.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri("T8MYAA==", "T8MYAIBacAA="));
instead of
_documentClient.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri("testdb", "testcollection"));

@thomaslevesque
Copy link
Author

@YouNeedTea are you saying I should use the collection's SelfLink? It doesn't seem to work for me.

@kirankumarkolli, how does DocumentClient select the proper token?

@YouNeedTea
Copy link

YouNeedTea commented May 17, 2018

@thomaslevesque I don't have any internal knowledge, but by examining the behaviour of DocumentClient, I believe it looks for a permission with a 'ResourceLink' property that is a prefix of the resource you're trying to access.
E.g. if you're trying to fetch a document with URI "dbs/mydb/colls/mycoll/docs/mydoc" and you have a permission with ResourceLink "dbs/mydb/colls/mycoll", it'll be picked.
If your permission had ResourceLink set to "dbs/AQIDBAU=/colls/BQIEBAU=", it wouldn't be picked.

A successful workaround for me was to insert two permissions into the database - once with ResourceLink set to the opaque self-link, and once with ResourceLink set to the friendly link.

@thomaslevesque
Copy link
Author

@YouNeedTea I see what you mean, and it makes sense, but:

  • my permissions have the "readable" resource link, and I also use the readable link for accessing resources, so it should work
  • using the opaque link instead doesn't work for me, and anyway, how would I get the opaque link for a resource if I can't access it in the first place?

@hkrotsik
Copy link

hkrotsik commented Jun 7, 2018

I've faced same issue during implementation of the permission-based access to data stored in CosmosDB.
Are there other options except explicitly adding partition key to the filter?
Unfortunately, this workaround doesn't work for me because I need to have multiple permission per user to support resource sharing.

@thomaslevesque
Copy link
Author

Are there other options except explicitly adding partition key to the filter?

As far as I can tell, adding the partition key to the filter doesn't make any difference. But you should definitely set RequestOptions.PartitionKey or FeedOptions.PartitionKey to the appropriate value.

From my experience, using URIs created by UriFactory simply never works when using resource tokens; you should always use the SelfLink, not the AltLink.

I tried all combinations, and only one works:

  1. permission created with AltLink, request made with AltLink : fails
  2. permission created with AltLink, request made with via SelfLink : fails
  3. permission created with SelfLink, request made with via AltLink : fails
  4. permission created with SelfLink, request made with via SelfLink : succeeds

I can understand why 2 and 3 would fail, but 1 should definitely work. Especially since there's no way to get the collection's SelfLink in the first place since you can't read the collection using the AltLink..

(in fact, there's a way, but it's ugly: read the ResourceLink from the permission)

@danutzplusplus
Copy link

I've run into something very similar to this, but a bit simpler.

In my situation I've granted the user read access to the collection selflink, and when i try to read a particular document from that collection (not the feed) I get the error that no token was provided.
Reading the feed however does work.
Also, passing the token, and not the permission that owns the token, also works. I was dumbfounded as to why.
As a last resort I tried what @YouNeedTea said, and that fixed the issue. So it seems that permissions are not granted across both the human-readable and "hashed" resourceids?

So, unless someone else has a better idea, seems manually granting the permission across both human readable and hashed resourceurls is the safest bet. Unless someone sees something wrong with this approach?

@ealsur
Copy link
Member

ealsur commented Jan 15, 2020

Seems like the approach is:

If you are going to use UriFactory in your queries/document operations, then the Permission needs to be created with a ResourceLink constructed with UriFactory (name based routing).

If you are going to use ResourceIds in your queries/document operations, then the Permission needs to be created with a ResourceLink constructed with the SelfLink of the collection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants