Dynamics 365 V9 — Verify Logged In User Security Role using TypeScript

We get requirements where we need to perform operations based on the user having a particular security role or System Administrator role.

Consider the following requirement:

I want to check if the logged in user is Admin or has “XXX” role and show/hide custom ribbon button.

Common solution:

For the above requirement, this is how a developer would proceed:

  1. Create Enable Rules for the ribbon button and implement a JavaScript method to return true/false based on the users security roles.
  2. The key here is to implement the Script method for role check.
  3. Get the GUID of System Administrator and “XXX” role.
  4. Get all the roles of the logged in user.
  5. Iterate through all the roles and check if any of the roles match, then returns true otherwise false.

Code looks like below:

export function IsUserHasRoles(): boolean {
// GUID of role to check
var roleId = “XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX”;
// Get all the roles of the Logged in User.
var currentUserRoles = Xrm.Utility.getGlobalContext().userSettings.securityRoles;
for (var i = 0; i < currentUserRoles.length; i++) {
var userRoleId = currentUserRoles[i];
if (userRoleId == roleId) {
// Return true if the Role matches
return true;
}
}
return false;}
Xrm.Page.context.getUserRoles has been deprecated and replaced with Xrm.Utility.getGlobalContext().userSettings.securityRoles.

To know more details on deprecated client API read the documentations.

Problem with the above solution:

  1. Hardcoding the role GUID only works if the CRM organization has only root Business Unit and no child Business Units. If the logged in user is in any of the Child Business Unit, the above checks will fail. We can’t really change the code specific to user security setup in each instance and this is not an acceptable or suggested solution.
  2. If the requirement change to enable/disable custom ribbon button if the user has role “YYY” instead of “XXX” which needs code change.

Alternative Solution:

  1. Move the security roles from one instance to another instance using configuration migration tool to keep the same GUID.
  2. Store the User GUID in the config setting. May be comma (,) separated GUID if we need to verify more than one roles with the logged in user roles.
  3. Get the GUID from config settings and implement logic same as common solution.
  4. This solution works OK if we are dealing with only custom security roles. We have a problem here if we need check the logged in user roles with any out -of-box security roles(System Administrator). The GUID of out-of-box roles are different in different instance.

Better solution:

Since the issue is with using the role Id. The security role name can be used to verify the user security role. So even though the role ids will not be same, the name of the security roles may not change, if at all change it should be easy enough just to update some configuration but not the code.

The role names are more readable whereas role ids are not and not easy to find. For role name config value, admin can easily update config on change of requirement.

Key things to consider before implement the code.

  1. Reuse of code.
  2. Change of requirement should not involve code change, config change should do the job.
  3. Eliminate multiple server call.

I have created helper methods and a config parameter in custom entity to store the comma (,) separated role name to verify user roles.

Implemented code using xrm type definition script. it’s a way of writing code in the next generation of JavaScript. TypeScript is superset of JavaScript that have strong type checking through the type interface. If you’re not familiar with form scripting using XrmDefinitelyTyped then start reading documentation.

The sample project contains both TypeScript and JavaScript files with all the required configs. The structure of the project looks like below.

You can check and download this sample TypeScript project from my GitHub

Ribbon Button Enable Rules TypeScript Function:

/**
* Returns true if user has System Administrator OR Schedule Manager role, otherwise false
*/
export function ClcikMeEnableRules() {
// Get roles name from the config entity, hard coded here for testing.
var configValue = “System Administrator,Schedule Manager”;
return KARLAB.Utility.Common.IsUserHasRoles(configValue.split(“,”));
}

Reusable TypeScript Functions:

IsUserHasRoles:

/**
* Return true if loggedin user role match with one of given roles name
* @param {rolesToVerify} Array that contains the user roles name to be verify with the user’s security roles
*/
export function IsUserHasRoles(rolesToVerify): boolean {
var matchingRoles = [];
if (rolesToVerify.length == 0)
return false;
var currentUserRoles = Xrm.Utility.getGlobalContext().userSettings.securityRoles;
var userRolesName = KARLAB.Utility.Common.GetRolesName(currentUserRoles);
if (userRolesName.length == 0)
return false;
rolesToVerify.forEach((e1) => userRolesName.forEach((e2) => {
if (e1 == e2) {
matchingRoles.push(e1);
}}));
return matchingRoles.length > 0
}

GetRolesName:

/**
* Return collection of role name for the given role ids
* @param {currentUserRolesId} Array that contains the user role ids to be applied on this query to search roles
*/
export function GetRolesName(currentUserRolesId): any[] {
var rolesName = [];
var filters = “&$filter”;
for (var i = 0; i < currentUserRolesId.length; i++) {
i == 0 ? filters += “=” : filters += “ or “;
filters += “roleid eq “ + currentUserRolesId[i];
}
var queryStrg = KARLAB.Utility.WEBApi.GetApiPath() + “roles?$select=name” + filters;
var results = KARLAB.Utility.WEBApi.RetrieveRecordsSync(queryStrg);
if (results.value != null && results.value.length > 0) {
for (var i = 0; i < results.value.length; i++) {
rolesName.push(results.value[i][“name”]);
}
}
return rolesName;
}

Hope this helps! please comment and share your thoughts if you have implemented this in a better way.