Getting logged in user roles in client side in Dynamics V9.0

You may be asking? Why this mundane post? After all we have been here close to 8 years since 2011 released and we have millions of time retrieved it using Xrm.Page.context.getUserRoles().

So what’s the fuss in it?

So if you are working on CRM version below 9.0, then it’s no fuss. But if you are working on V9.0 and above, may be this is an interesting read for you.

Xrm.Page is deprecated. So you should no longer be using Xrm.Page.context.getUserRoles()

So what’s the other way. Well you need to do it the V9.0 way. Below is the  code to do the same.

var userRoles = Xrm.Utility.getGlobalContext().userSettings.securityRoles;

And the userRoles variable here contains the list of GUID’s of the roles the user have. Wasn’t that easy?

Also now with the unified interface coming in, you should be very careful about every piece of Javascript you write. There are some properties which works only in UCI and some in Web

For e.g – not only you can get the security roles but also the privilege id’s for all the security roles associated with the user.

var userRolesPrivileges = Xrm.Utility.getGlobalContext().userSettings.securityRolePrivileges;

Unfortunately, the above line only works in UCI and not on the web version.

Debajit Dutta
(Dynamics MVP)
For consultation/ training visit http://www.xrmforyou.com or reach out to us at info@xrmforyou.com

Advertisements

{Solved} Call Web API from within a plugin in Dynamics 365 online

Well, this is one question I cam across countless number of times. And in a recently conducted training boot camp, i was asked the same question. To be honest after understanding OAuth and how various types of grants work in Azure Active Directory, I was thinking this should not be a very tough problem.

To my surprise, when I started searching the community I came across many links which suggested it can’t be done and many links which suggested ways but no complete guidance on how to do it.

So decided to challenge myself in it for my blog readers and came out pretty quick within couple of hours. So here we start out journey.

Before we go ahead and discuss on how to do this, let’s understand the problem faced.

The first thing that we need to connect to Web API instance is an access token. And getting an access token now is just piece of cake. You reference Active Directory Authentication Library (you can easily get this from Nuget with the keyword : ADAL) in your project and then use some 4-5 pre-defined lines to get the token.

So what’s the problem with that? The problem is when we try to run the same code in our plugins and try to access the token, we get a security exception. The problem is our online plugins runs in sandbox mode.

So the solution to this would be not to use ADAL. So what are the other options? Off-course we can use plain simple HttpRequest to get the token right? After all internally ADAL might be doing the same right? Not sure about ADAL but certainly this is harder than you think.

The first point to consider here is when the plugin is executing, we do not have the password for the user.

In my blog link – https://debajmecrm.com/2018/04/30/headless-authentication-with-dynamics-crm-online-and-external-web-app-which-requires-client-secret/ I have explained in detail on how to get the access token without using ADAL. The problem here is, I am  using the password here to get the token. In plugins I will not have that.

So how to tackle this.

If you observe carefully, there are various types of grants in Oauth and one of them “grant_type=password” I have used in the above link. Well since I can’t use that, there is another type called grant_type=client_credentials”. And this is what we are going to use here.

So let’s now jump to the solution.

Step 1:

Register an application of type WebApp/Api in your Azure active directory with proper permission to Dynamics Application and then create an application user in Dynamics with the Application ID of your registered App. Give access to your application user with proper security roles to gain necessary access to entities.

If you are not aware of registering App or the concept of Application User in Dynamics, it’s explained in great detail in the below link

https://debajmecrm.com/2018/08/16/step-by-step-guide-query-dynamics-crm-web-api-using-server-to-server-authentication-with-application-user/

Just set-up the APP and the application user. Don’t try to use the code since it will not work in plugins.

Step 2:

Plugin code

public class WebApiSample : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            // getting the pipeline context.
             var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            var ent = (Entity)context.InputParameters["Target"];

            var accessToken = GetTokenWithoutADAL().GetAwaiter().GetResult();
             var accountJson = RetrieveAccounts(accessToken).GetAwaiter().GetResult();

            ent["vvbv_description"] = accountJson;
        }

        private async Task<string> GetTokenWithoutADAL()
        {
            string azureAdDomain = "xrmforyou62.onmicrosoft.com";
            string loginUrl = $"
https://login.microsoftonline.com/{azureAdDomain}/oauth2/token";
             string resource = "https://xrmforyou62.crm.dynamics.com";
              string clientId = "9a3590d8-c89e-4de5-bb50-8dfa3d9796a9";
            string clientSecret = "<put your client secret here>";

            HttpClient client = new HttpClient();
            var postData = $"client_id={clientId}&client_secret={clientSecret}&resource={resource}&grant_type=client_credentials";

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, loginUrl);
            request.Content = new StringContent(postData, Encoding.UTF8);
            request.Content.Headers.Remove("Content-Type");
            request.Content.Headers.TryAddWithoutValidation("Content-Type", $"application/x-www-form-urlencoded");

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            var responseMessage = await client.SendAsync(request);
            var jsonResponseString = await responseMessage.Content.ReadAsStringAsync();

           var jsonContent = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonResponseString);

            return jsonContent["access_token"];
        }

        private async Task<string> RetrieveAccounts(string token)
        {
             string webApiUrl = "
https://xrmforyou62.api.crm.dynamics.com/api/data/v9.1";
             string url = $"{webApiUrl}/accounts?$select=name";

            HttpClient client = new HttpClient();
            client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
           
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            HttpResponseMessage response = await client.GetAsync(url);

            string jsonContent = await response.Content.ReadAsStringAsync();

            return jsonContent;
        }
    }

You can do a better job at handling the client_id and client_secret. But this is just sample code and hence I am using this directly.

Also to parse the JSON, I have used the Newtonsoft.json dll which you need to ilmerge with the plugin assembly before uploading to the plugin registration tool. Check for the highlighted code.

And I have registered this plugin on the Pre-create of a test entity. I set the description of the field in this plugin to the account JSON and voila it works!

image

Eureka! I can see the account JSON in the description field after I create the record.

Isn’t it great guys.

Hope this helps. And hope you just had something interesting to read in CRM world today.

Debajit Dutta

(Dynamics MVP)

For consultation/ training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Xrm.Page is deprecated with Version 9.0. How do I get the Clienturl and form data in Ribbon Actions?

Simple isn’t it? After all it was so easy

All I needed to write to get the ClientUrl

var clientUrl = Xrm.Page.context.getClientUrl();

And to get value of the field on the form

var fieldValue = Xrm.Page.getAttribute(“<fieldname>”).getValue()

So what’s the fuss? Well, if your CRM version is 8.2 and below, no worries. It it perfect.

However with version 9.0 and above, the Xrm.Page has been deprecated. So it means you can use them no longer.

So how do I get the Client Url? The below code does it for you.

var globalContext = Xrm.Utility.getGlobalContext();

var clientUrl = globalContext.getClientUrl();

And how about getting the value of a field on the form?

With version 9.0 and above, you should try to access the form data using the formContext. But again the problem is, to get the formContext, you need to use ExecutionContext. So the next question lies on how to pass executionContext parameter to your Ribbon actions.

For that you need to pass the CrmParameter – Primary control to your ribbon action. Below is the code to get the data using formContext.

image

function ribbonHandler(e) {
    var formContext = e.getFormContext();

    var recordId = formContext.data.entity.getId();
    var fieldValue = formContext.getAttribute("<field_name>").getValue();
}

Just another day as a consultant and I hope this blog just adds a grain to your CRM knowledge heap.

Debajit Dutta
(Dynamics MVP)
For consultation/ corporate training visit http://www.xrmforyou.com or reach out to us at info@xrmforyou.com

Perform Retrieve using Alternate Key and SDK in Dynamics 365

Alternate keys have been implemented some time back and by now I think most of the consultants working with Microsoft Dynamics are familiar with the concept of alternate keys.

In case you are new to alternate key, you can follow the below documentation here.

https://docs.microsoft.com/en-us/dynamics365/customer-engagement/developer/define-alternate-keys-entity

Recently I got a request to retrieve using an alternate key. Strangely enough, there are many examples of Update/ Upsert using alternate key but not much with the retrieve using SDK.

Moreover developers coming in new gets confused whenever they try to retrieve using OrganzationService.Retrieve because the method does not have any parameter to take in an alternate key

Well the alternative to that is using the RetrieveRequest. Below is the sample code to retrieve a contact record with alternate key. The alternate key is set up on the Email (emailaddress1) field of the contact entity.

string entName = "contact";
            string alternateKeyfield = "emailaddress1";

            RetrieveRequest retrieveRequest = new RetrieveRequest();
            retrieveRequest.ColumnSet = new ColumnSet(new string[2] { "firstname", "lastname" });
            retrieveRequest.Target = new EntityReference(entName, alternateKeyfield, "test@123.com");

            RetrieveResponse resp = (RetrieveResponse)proxy.Execute(retrieveRequest);

            if(resp.Entity != null)
            {
                Console.WriteLine($"First Name: {resp.Entity.GetAttributeValue<string>("firstname")}");
                Console.WriteLine($"Last Name: {resp.Entity.GetAttributeValue<string>("lastname")}");
             }

Strangely enough the RetrieveRequest is hardly being used. The highlighted one is the line where we are trying to retrieve using Alternate key which is set on emailaddress1 field.

Please note if there is no record with the specified email address here (test@123.com), you will get an error. Your code should have appropriate try catch block to handle the same

Hope this helps next time when you try to retrieve a record using alternate key.

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

{Knowhow} Delete field from managed solution in Target environment when deleted from source environment–Dynamics 365

Dynamics CRM 2016 introduced the concept of solution segmentation, solution patching and cloning and indeed it has come a long way over the last 2-3 years. We are now exposed to CDM, flows, powerapps and what not. But CRM has some old tricks up it’s sleeve which makes you go back to the basics sometimes and I am going to pen down one such scenario here. Even experienced consultants in CRM are sometimes bamboozled by this problem and I hope this is going to benefit my blog readers.

So here is the problem

  • Source Environment (Env X) has a custom field of type Optionset – “new_FieldToBeDeleted” for Entity – Account (named it explicitly for this blog. Don’t follow such convention for stupid names Smile) . The field is Local Optionset.
  • The field is placed on the form of the account.
  • The solution from Env X is exported as managed and deployed to target environment (Env Y).
  • Because of change of requirement, the local optionset now deleted from the source and replace with a global optionset.

The question now arises. How to delete the field from the target environment? It is a managed solution. Hence if you try to delete the field, you will receive an error. Also importing the new managed solution from source won’t help in this case.

Fortunately there is one way to do so with the help of Clone Solution feature.

  • In the solution in the source environment, do all the necessary changes like removing dependencies, deleting the local optionset field, creating a global optionset and creating a new field in Account Entity to map to the global optionset.
  • Select the solution and click on Clone Solution.

image

  • Put a version number as asked for.
  • Export the solution as managed.
  • While importing the solution in the target environment, check the box – “Stage for Upgrade” and select the option – “Maintain customizations”.

image

  • Once the solution is imported click on Apply Solution Upgrade button. The field in the target would be deleted as well and you will see the new field in your target environment.

          image

Hope this helps next time you have trouble with your solution management.

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com