简体   繁体   中英

Azure cosmos db issues with connection by resource token

I'm trying to connect to my cosmos db database by using a Resource token instead of a account master key which I used before. Before I used the following way to create DocumentClient :

    var client = new DocumentClient(new Uri(configAccountName), configAccountKey);
    await client.OpenAsync();

This way works well, but I want to use resource token ways for that target as mentioned in these articles : link1 and link2 .

I've created a db user with All permissions by the following code:

    public static async Task<User> CreateUserAndPermissionAsync(this DocumentClient client, string userId)
    {
        var dbUri = UriFactory.CreateDatabaseUri(dataBase);
        var user = await client.CreateUserAsync(dbUri, new User { Id = userId });
        var collectionUri = UriFactory.CreateDocumentCollectionUri(dataBase, collectionName);
        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;
    }

The user is created correctly and I can retrieve him by ReadUserAsync or his permissions by ReadPermissionAsync methods. Then, I want to create new instance of DocumentClient with permissions of newly created user.

    //this is a temporally document client instance to read permissions for my user in below extensions method versions
    var client = new DocumentClient(new Uri(configAccountName), configAccountKey);

I've checked 3 ways (some of them are similar). I've tryied to call each of ways to get new DocumentClient instance( newClient variable) and then call OpenAsync method to open connection:

    var newClient = await client.GetClientForUserAsync_v###(userName);
    await newClient.OpenAsync();

All tryings were failed

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

The OpenAsync method was crashed with this error : Failed to parse the value '' as ResourceId., documentdb-dotnet-sdk/1.22.0 Host/32-bit MicrosoftWindowsNT/6.2.9200.0

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

The OpenAsync method was crashed with this error : Insufficient permissions provided in the authorization header for the corresponding request. Please retry with another authorization header. ActivityId: ##ReplacedActivityId##, Microsoft.Azure.Documents.Common/1.22.0.0, documentdb-dotnet-sdk/1.22.0 Host/32-bit MicrosoftWindowsNT/6.2.9200.0

    //version 3:
    public static async Task<DocumentClient> GetClientForUserAsync_v3(this DocumentClient client, string userId)
    {
        FeedResponse<Permission> permFeed = await client.ReadPermissionFeedAsync(UriFactory.CreateUserUri(dataBase, userId));
        List<Permission> permList = new List<Permission>();

        foreach (Permission perm in permFeed)
        {
            permList.Add(perm);
        }

        DocumentClient userClient = new DocumentClient(new Uri(client.ServiceEndpoint.AbsoluteUri), permList, new ConnectionPolicy()
        {
            //**UPDATE**: I tried all ConnectionMode values as well as without this parameter
            ConnectionMode = ConnectionMode.Gateway
        });
        return userClient;
    }     

The OpenAsync method was crashed with this error : Failed to parse the value '' as ResourceId., documentdb-dotnet-sdk/1.22.0 Host/32-bit MicrosoftWindowsNT/6.2.9200.0 .

The first and third errors are the most strange, because I've checked and I see the ResourceId value in the perm variable (as item of permList collection) as well as in permissions variable from the first version.

Could anyone help with it?

UPDATE I've checked the 4th version, which gives me the same result as 1,3 trying: Failed to parse the value '' as ResourceId., documentdb-dotnet-sdk/1.22.0 Host/32-bit MicrosoftWindowsNT/6.2.9200.0

    //version 4
    public static async Task<DocumentClient> GetClientForUserAsync_v4(this DocumentClient client, string userId)
    {
        var user = await client.ReadUserAsync(UriFactory.CreateUserUri(dataBase, userId));
        var permissions = await client.ReadPermissionFeedAsync(user.Resource.SelfLink);
        List<Permission> permList = new List<Permission>();

        foreach (Permission perm in permissions)
        {
            permList.Add(perm);
        }

        DocumentClient userClient = new DocumentClient(new Uri(client.ServiceEndpoint.AbsoluteUri), permList, new ConnectionPolicy()
        {
            ConnectionMode = ConnectionMode.Direct
        });
        return userClient;
    }

The Cosmos DB resource token will be generated when the permission been built. We can use resource token to connect to Cosmos DB like below:

var client = new DocumentClient(new Uri(endPointUri), resourceToken );

And we can get resource Token of the permission as below:

    public static async Task<Dictionary<PermissionMode, string>> GetPermissonTokens(DocumentClient client,string DatabaseId,string userId)
    {
        FeedResponse<Permission> permFeed =  await client.ReadPermissionFeedAsync(UriFactory.CreateUserUri(DatabaseId, userId));

        Dictionary<PermissionMode,string> permList = new Dictionary<PermissionMode, string>();

        foreach (Permission perm in permFeed)
        {

            permList.Add(perm.PermissionMode, perm.Token);
        }

        return permList;
    }

Here is a Complete demo for your reference:

    static void Main(string[] args)
    {
        //create DocumentClient instance with end point Uri and primary key
        DocumentClient client = new DocumentClient(new Uri("https://xxxx.xxxxx.azure.com:443/"), "ikhSrMZIGKBrF1xxxxx7iDSLBMJD37oCOlw2N24YoBtfAV7HJFfVgNbhCfQdWGAq3eZY4FyX3z6zsWoRLHQ==", new ConnectionPolicy { EnableEndpointDiscovery = false });

        Task<Dictionary<PermissionMode, string>> task = GetPermissonTokens(client, "Test2", "testUser2");
        task.Wait();
        Dictionary<PermissionMode, string> permissionTokens = task.Result;

        DocumentClient userClient = new DocumentClient(client.ReadEndpoint, permissionTokens[PermissionMode.All]);
        if (userClient != null)
        {
            //then we can do CRUD operation to Cosmos DB Here 
        }
    }


    public static async Task<Dictionary<PermissionMode, string>> GetPermissonTokens(DocumentClient client,string DatabaseId,string userId)
    {
        FeedResponse<Permission> permFeed =  await client.ReadPermissionFeedAsync(UriFactory.CreateUserUri(DatabaseId, userId));

        Dictionary<PermissionMode,string> permList = new Dictionary<PermissionMode, string>();

        foreach (Permission perm in permFeed)
        {

            permList.Add(perm.PermissionMode, perm.Token);
        }

        return permList;
    }

I have similar case where in my model I am using partitioning. So, I have developed this function to be get the user token.

    private async Task<string> GetUserToken()
    {
        User user = null;
        try
        {
            try
            {
                var uri = UriFactory.CreateUserUri(config.Database, config.PartitionKey);
                client.DeleteUserAsync(uri).Wait();

                user = await client.ReadUserAsync(UriFactory.CreateUserUri(config.Database, config.PartitionKey));

                var permission = await GetorCreatePermission(user, config.Collection, config.PartitionKey);
                return permission.Token;
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message);
            }
            if (user == null)
            {
                user = new User
                {
                    Id = config.PartitionKey
                };
                user = await client.CreateUserAsync(UriFactory.CreateDatabaseUri(config.Database), user);
                var permission = await GetorCreatePermission(user, config.Collection, config.PartitionKey);
                return permission.Token;
            }
            else
            {
                throw new Exception("");
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

After multiple attempts, the one conclusion I ended up was not to include the PartitionKey when creating permission for the collection. The below tweak did the job for me and I am able to successfully add contents to my collection using resource tokens.

    new Permission
    {
        Id = "MyPermission",
        PermissionMode = PermissionMode.All,
        ResourceLink = collectionUri.ToString()
    });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM