Monday, May 23, 2011

Grant Access To a User or Team to an Entity using jscript or .NET in Microsoft Dynamics CRM 2011 Using GrantAccessRequest

This illustration shows you how to grant access to a user (systemuser) or a team to an entity in Microsoft Dynamics CRM 2011 using the GrantAccessRequest.   This example will be given in SOAP (JScript) and in C# (.NET).

The different levels for the AccessMask are AppendAccess, AppendToAccess, AssignAccess, CreateAccess, DeleteAccess, None, ReadAccess, ShareAccess, and WriteAccess.

Ok, here is what the code look like!
First in C#:

GrantAccessRequest grantAccessRequest = new GrantAccessRequest
{
    PrincipalAccess = new PrincipalAccess
    {
        Principal = new EntityReference(SystemUser.EntityLogicalName, new Guid("05B84284-0872-E011-8947-1CC1DEE8DAD5")),

        //Using this line instead of the one above you could just as easily be granting access to a user
        //Principal = new EntityReference(Team.EntityLogicalName, new Guid("{team guid here}")),
        

        AccessMask = AccessRights.ReadAccess
        | AccessRights.AppendToAccess
    },
    Target = new EntityReference(Account.EntityLogicalName, new Guid("B0951D47-FB71-E011-882E-1CC1DEF17774"))
};

service.Execute(grantAccessRequest);

If you need help instantiating a service object in .NET within a plugin check out this post:
http://mileyja.blogspot.com/2011/04/instantiating-service-object-within.html

Now here is the Jscript nicely formatted by the CRM 2011 SOAP formatter. Available at: http://crm2011soap.codeplex.com/

Now in Jscript:

if (typeof (SDK) == "undefined")
   { SDK = { __namespace: true }; }
       //This will establish a more unique namespace for functions in this library. This will reduce the 
       // potential for functions to be overwritten due to a duplicate name when the library is loaded.
       SDK.SAMPLES = {
           _getServerUrl: function () {
               ///<summary>
               /// Returns the URL for the SOAP endpoint using the context information available in the form
               /// or HTML Web resource.
               ///</summary>
               var OrgServicePath = "/XRMServices/2011/Organization.svc/web";
               var serverUrl = "";
               if (typeof GetGlobalContext == "function") {
                   var context = GetGlobalContext();
                   serverUrl = context.getServerUrl();
               }
               else {
                   if (typeof Xrm.Page.context == "object") {
                         serverUrl = Xrm.Page.context.getServerUrl();
                   }
                   else
                   { throw new Error("Unable to access the server URL"); }
                   }
                  if (serverUrl.match(/\/$/)) {
                       serverUrl = serverUrl.substring(0, serverUrl.length - 1);
                   } 
                   return serverUrl + OrgServicePath;
               }, 
           GrantAccessRequest: function () {
               var requestMain = ""
               requestMain += "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
               requestMain += "  <s:Body>";
               requestMain += "    <Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
               requestMain += "      <request i:type=\"b:GrantAccessRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:b=\"http://schemas.microsoft.com/crm/2011/Contracts\">";
               requestMain += "        <a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
               requestMain += "          <a:KeyValuePairOfstringanyType>";
               requestMain += "            <c:key>Target</c:key>";
               requestMain += "            <c:value i:type=\"a:EntityReference\">";
               requestMain += "              <a:Id>b0951d47-fb71-e011-882e-1cc1def17774</a:Id>";
               requestMain += "              <a:LogicalName>account</a:LogicalName>";
               requestMain += "              <a:Name i:nil=\"true\" />";
               requestMain += "            </c:value>";
               requestMain += "          </a:KeyValuePairOfstringanyType>";
               requestMain += "          <a:KeyValuePairOfstringanyType>";
               requestMain += "            <c:key>PrincipalAccess</c:key>";
               requestMain += "            <c:value i:type=\"b:PrincipalAccess\">";
               requestMain += "              <b:AccessMask>ReadAccess AppendToAccess</b:AccessMask>";
               requestMain += "              <b:Principal>";
               requestMain += "                <a:Id>05b84284-0872-e011-8947-1cc1dee8dad5</a:Id>";
               requestMain += "                <a:LogicalName>systemuser</a:LogicalName>";
               requestMain += "                <a:Name i:nil=\"true\" />";
               requestMain += "              </b:Principal>";
               requestMain += "            </c:value>";
               requestMain += "          </a:KeyValuePairOfstringanyType>";
               requestMain += "        </a:Parameters>";
               requestMain += "        <a:RequestId i:nil=\"true\" />";
               requestMain += "        <a:RequestName>GrantAccess</a:RequestName>";
               requestMain += "      </request>";
               requestMain += "    </Execute>";
               requestMain += "  </s:Body>";
               requestMain += "</s:Envelope>";
               var req = new XMLHttpRequest();
               req.open("POST", SDK.SAMPLES._getServerUrl(), true)
               // Responses will return XML. It isn't possible to return JSON.
               req.setRequestHeader("Accept", "application/xml, text/xml, */*");
               req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
               req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
               var successCallback = null;
               var errorCallback = null;
               req.onreadystatechange = function () { SDK.SAMPLES.GrantAccessResponse(req, successCallback, errorCallback); };
               req.send(requestMain);
           },
       GrantAccessResponse: function (req, successCallback, errorCallback) {
               ///<summary>
               /// Recieves the assign response
               ///</summary>
               ///<param name="req" Type="XMLHttpRequest">
               /// The XMLHttpRequest response
               ///</param>
               ///<param name="successCallback" Type="Function">
               /// The function to perform when an successfult response is returned.
               /// For this message no data is returned so a success callback is not really necessary.
               ///</param>
               ///<param name="errorCallback" Type="Function">
               /// The function to perform when an error is returned.
               /// This function accepts a JScript error returned by the _getError function
               ///</param>
               if (req.readyState == 4) {
               if (req.status == 200) {
               //if (successCallback != null)
               //{ successCallback(); }
               alert("worked!");
               }
               else {
                   errorCallback(SDK.SAMPLES._getError(req.responseXML));
               }
           }
       },
       _getError: function (faultXml) {
           ///<summary>
           /// Parses the WCF fault returned in the event of an error.
           ///</summary>
           ///<param name="faultXml" Type="XML">
           /// The responseXML property of the XMLHttpRequest response.
           ///</param>
           var errorMessage = "Unknown Error (Unable to parse the fault)";
           if (typeof faultXml == "object") {
               try {
                   var bodyNode = faultXml.firstChild.firstChild;
                   //Retrieve the fault node
                   for (var i = 0; i < bodyNode.childNodes.length; i++) {
                       var node = bodyNode.childNodes[i];
                       //NOTE: This comparison does not handle the case where the XML namespace changes
                       if ("s:Fault" == node.nodeName) {
                       for (var j = 0; j < node.childNodes.length; j++) {
                           var faultStringNode = node.childNodes[j];
                           if ("faultstring" == faultStringNode.nodeName) {
                               errorMessage = faultStringNode.text;
                               break;
                           }
                       }
                       break;
                   }
               }
           }
           catch (e) { };
        }
        return new Error(errorMessage);
     },
 __namespace: true
};




To understand how to parse the response please review my post on using the DOM parser.
Now you can call the SDK.SAMPLES.GrantAccessRequest function from your form jscript handler.
Thats all there is to it!

I hope this helps!

5 comments:

  1. I get a 405 http error, method not allowed. Am I doing something wrong? I am doing exactly the same.

    ReplyDelete
  2. This could be an IIS setting causing your issue.

    ReplyDelete
  3. Hi Jamie.

    I've tried implementing your code, but am having trouble getting it to work. The response code comes back as 200, but I'm getting an HTML page back which just says "An error occurred, please contact your system Administrator.", and the entity is not shared.

    It's the same page that sometimes appears when an error does occur.

    I'm accessing Dynamics CRM Online.

    I'm an administrator, so I have all the permissions necessary to do the sharing (since I can do it via the app).

    Any help you can share?

    Thanks,
    Paulo

    ReplyDelete
    Replies
    1. I tested the code so I am guessing something isn't quite right. I am very busy this week so I would post your code and question here and then send me the link to the question int he forums. That way if I don't have time the next couple days someone else might be able to help you.
      http://mileyja.blogspot.com/2012/02/you-got-questions-we-got-answers.html

      Delete
    2. Hi Jamie. Thanks, unfortunately I NEEDED to get this working on Monday. Since I didn't manage, I implemented a plugin instead. I suspect it might actually be something simple (and stupid) like permissions on the User/Team since the team had 0 security roles, so basically had access to nothing.

      As soon as I have time I'll try it again, but I used your C# code instead of JScript :)

      Delete