2
votes

I am having trouble retrieving access rights to a particular record in CRM.

I am using the RetrievePrincipalAccess Web API call as suggested here:

https://msdn.microsoft.com/en-us/library/mt683539.aspx

However, the AccessRights field is always null.

enter image description here

This occurs whether the user is a system admin or any security role.

Here is my code:

var crmTesting = {
tests: {
    getUserPrivilegesForRecord: function () {
        var recordId = Xrm.Page.data.entity.getId().replace('{', '').replace('}', '');
        var userId = Xrm.Page.context.getUserId().replace('{', '').replace('}', '');
        console.log(recordId);
        console.log(userId);
        crmTesting.api.retrievePrincipalAccess(userId, 'accounts', recordId, function (r) {
            console.log(r);
        });
    }
},
api: {
    retrievePrincipalAccess: function (userId, entityPluralName, entityId, callback) {
        if (!this.context.loaded) {
            this.context.init();
        }
        var url = 'systemusers(' + userId 
            + ")/Microsoft.Dynamics.CRM.RetrievePrincipalAccess(Target=@tid)?@tid={'@odata.id':'" 
            + entityPluralName + '(' + entityId + ")'}";
        this.get(url, callback);
    },
    get: function (url, callback, error) {
        if (!this.context.loaded) {
            this.context.init();
        }
        var xhr = new XMLHttpRequest();
        xhr.open('GET', this.context.clientUrl + '/api/data/v8.1/' + url);
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.setRequestHeader('Prefer', 'odata.include-annotation=OData.Community.Display.V1.FormattedValue');
        xhr.setRequestHeader('OData-MaxVersion', '4.0');
        xhr.setRequestHeader('OData-Version', '4.0');
        xhr.onload = function () {
            if (xhr.status == 200) {
                if (typeof callback != 'undefined') {
                    var result = JSON.parse(xhr.responseText);
                    callback(result);
                }
            }
            else if (xhr.status == 204) {
                if (typeof callback != 'undefined') {
                    callback();
                }
            }
            else {
                console.error(xhr.responseText);
                if (typeof error != 'undefined') {
                    error(xhr.responseText);
                }
                return;
            }
        };
        xhr.send();
    },
    post: function (url, data, callback, error) {
        if (!this.context.loaded) {
            this.context.init();
        }
        var xhr = new XMLHttpRequest();
        xhr.open('POST', this.context.clientUrl + '/api/data/v8.1/' + url);
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.setRequestHeader('Context-Type', 'application/json; charset=utf-8');
        xhr.setRequestHeader('OData-MaxVersion', '4.0');
        xhr.setRequestHeader('OData-Version', '4.0');
        xhr.onload = function () {
            if (xhr.status == 200 || xhr.status == 204) {
                try {
                    var response = JSON.parse(xhr.responseText);
                    if (typeof callback != 'undefined') {
                        callback(response);
                    }
                }
                catch (e) {
                    console.log('Request successful. No response.');
                }
                return;
            }
            else {
                console.error(xhr.responseText);
                if (typeof error != 'undefined') {
                    error(xhr.responseText);
                }
            }
        };
        if (typeof data == 'string') {
            xhr.send(data);
        }
        else {
            xhr.send(JSON.stringify(data));
        }
    },
    context: {
        loaded: false,
        context: {},
        clientUrl: '',
        init: function () {
            this.context = this.getContext();
            this.clientUrl = this.context.getClientUrl();
            this.loaded = true;
        },
        getContext: function () {
            if (typeof GetGlobalContext != 'undefined') {
                return GetGlobalContext();
            }
            else if (typeof Xrm != 'undefined') {
                return Xrm.Page.context;
            }
            else {
                throw new Error('Context is not available.');
            }
        }
    }   
}
};

Any help as to why this is happening would be much appreciated.

Not sure if it is a Microsoft bug or if I am doing something wrong so wanted to validate it here.

My request headers:

Accept:application/json
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Cookie:<cookie>
Host:<domainurl>
OData-MaxVersion:4.0
OData-Version:4.0
Pragma:no-cache
Prefer:odata.include-annotation=OData.Community.Display.V1.FormattedValue
Referer:<crmorgurl>/form/ClientApiWrapper.aspx?ver=1916985412
User-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36

And my response headers:

Cache-Control:no-cache
Content-Encoding:gzip
Content-Length:169
Content-Type:application/json; odata.metadata=minimal
Date:Wed, 24 May 2017 10:03:51 GMT
Expires:-1
OData-Version:4.0
Pragma:no-cache
REQ_ID:e5b24ef1-4531-4492-a251-dc9e7f91875e
Server:Microsoft-IIS/8.5
Vary:Accept-Encoding
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET

Query string parameters:

@tid:{'@odata.id':'accounts(C0988888-6EF4-E611-80FD-005056AC5406)'}

I tried using the Xrm-WebApi-Client library from DigitalFlow and got the same thing.

Request headers:

GET /<orgname>/api/data/v8.0/systemusers(F52C4FBB-E653-E611-80FB-005056AC5406)/Microsoft.Dynamics.CRM.RetrievePrincipalAccess(Target=@p1)?@p1={%[email protected]%22:%22accounts(06918888-6EF4-E611-80FD-005056AC5406)%22} HTTP/1.1
Host: <crmdomain>
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
OData-Version: 4.0
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Content-Type: application/json; charset=utf-8
Accept: application/json
If-None-Match: null
OData-MaxVersion: 4.0
Referer: <crmorgurl>/form/ClientApiWrapper.aspx?ver=1916985412
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-US,en;q=0.8
Cookie: <cookie>

Response Headers:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; odata.metadata=minimal
Content-Encoding: gzip
Expires: -1
Vary: Accept-Encoding
Server: Microsoft-IIS/8.5
REQ_ID: 6981cbe8-1f66-42af-8227-bcd855c90ec7
OData-Version: 4.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 31 May 2017 11:57:20 GMT
Content-Length: 170
1
I tried to recreate the issue with no luck (I always get an error or the expected response). In the MSDN the AccessRights property is not nullable so it's a little bit weird. Could you please include the request and response? Thanks!Federico Jousset
Thanks for taking a look! I have added my request and response headers. At first, I thought maybe this request was only available with the 8.2 version, but having a look through release notes I think it was 8.0/8.1. Very strange.Asher
Yes, yesterday I tried it using 8.0/8.1/8.2 and it worked in all scenarios. I'll take another look and I'll let you know if I find something.Federico Jousset
Thanks. I am using On-Premise. Is there some kind of configuration in the setup to enable it?Asher
hello @Asher. we are experiencing the same issue in 3 dynamics 2016 on-premise installations. Did you found a solution?Suxsem

1 Answers

2
votes

I confirm that this is a bug in Dynamics CRM 2016 on-premise (tested on 8.1.1.1005).

I ended up creating a custom workflow activity and an action.

Custom workflow activity

Create and register a new custom workflow activity

using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Workflow;
using System;
using System.Activities;

namespace ****.WF.CheckRecordPermissions
{
    public class CheckRecordPermissions : CodeActivity
    {

        [Input("Entity logical name (if null use context)")]
        public InArgument<string> entityP { get; set; }

        [Input("Entity GUID (if null use context)")]
        public InArgument<string> guidP { get; set; }

        [Input("User to check (if null use context)")]
        [ReferenceTarget("systemuser")]
        public InArgument<EntityReference> userP { get; set; }

        [Output("Create permission")]
        public OutArgument<bool> cpP { get; set; }

        [Output("Read permission")]
        public OutArgument<bool> rpP { get; set; }

        [Output("Write permission")]
        public OutArgument<bool> wpP { get; set; }

        [Output("Delete permission")]
        public OutArgument<bool> dpP { get; set; }

        [Output("Append permission")]
        public OutArgument<bool> apP { get; set; }

        [Output("AppendTo permission")]
        public OutArgument<bool> atpP { get; set; }

        [Output("Assing permission")]
        public OutArgument<bool> aspP { get; set; }

        [Output("Share permission")]
        public OutArgument<bool> shpP { get; set; }

        protected override void Execute(CodeActivityContext executionContext)
        {
            ITracingService tracer = executionContext.GetExtension<ITracingService>();
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
            IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService service = serviceFactory.CreateOrganizationService(null);

            EntityReference target;

            String entity = entityP.Get(executionContext);
            String guid = guidP.Get(executionContext);

            if (!String.IsNullOrEmpty(entity) && !String.IsNullOrEmpty(guid))
                target = new EntityReference(entity, new Guid(guid));
            else
                target = new EntityReference(context.PrimaryEntityName, context.PrimaryEntityId);

            EntityReference user = userP.Get(executionContext);
            if (user == null)
                user = new EntityReference("systemuser", context.InitiatingUserId);

            RetrievePrincipalAccessRequest req = new RetrievePrincipalAccessRequest();
            req.Principal = user;
            req.Target = target;
            RetrievePrincipalAccessResponse resp = (RetrievePrincipalAccessResponse)service.Execute(req);

            cpP.Set(executionContext, resp.AccessRights.HasFlag(AccessRights.CreateAccess));
            rpP.Set(executionContext, resp.AccessRights.HasFlag(AccessRights.ReadAccess));
            wpP.Set(executionContext, resp.AccessRights.HasFlag(AccessRights.WriteAccess));
            dpP.Set(executionContext, resp.AccessRights.HasFlag(AccessRights.DeleteAccess));
            apP.Set(executionContext, resp.AccessRights.HasFlag(AccessRights.AppendAccess));
            atpP.Set(executionContext, resp.AccessRights.HasFlag(AccessRights.AppendToAccess));
            aspP.Set(executionContext, resp.AccessRights.HasFlag(AccessRights.AssignAccess));
            shpP.Set(executionContext, resp.AccessRights.HasFlag(AccessRights.ShareAccess));
        }
    }
}

Custom action

Create a custom action on the SystemUser entity. Add input and output parameters as follows (sorry for not using english in screenshot, we're italian):

enter image description here

enter image description here

enter image description here

Invoking the action

Finally you can invoke the action through javascript:

var readOnly;
var params = {
    "entita": Xrm.Page.data.entity.getEntityName(),
    "guid": Xrm.Page.data.entity.getId().slice(1, -1)
};
$.ajax({
    url: Xrm.Page.context.getClientUrl() + "/api/data/v8.1/systemusers(" + Xrm.Page.context.getUserId().slice(1, -1) + ")/Microsoft.Dynamics.CRM.new_controllaprivilegi",
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    },
    data: JSON.stringify(params),
    type: "post",
    dataType: "json",
    async: false,
    success: function (_responseOfferta) {
        readOnly = !_responseOfferta["Write"];
    }
});

Hope it helps!