19 January 2008

CRM 4.0 : Check current user's security role using JavaScript

It's a common question about how to show/hide fields based on user's security roles.
Ronald Lemmen had a very popular post on his blog about how to use 'RemoteCommand' to achieve that in CRM 3.0. Because 'RemoteCommand' is for internal use and unsupported, it doesn't work in CRM 4.0.
Michael H?hne also had a great post about how to access web service using client-side JavaScript. Since CRM 4.0 web service EndPoint changed, some people get 401 authorization error.

Here's code which works great in CRM 4.0, the function UserHasRole("ROLE_NAME") returns true if the current has the role, returns false if it doesn't. GetCurrentUserRoles() function generated by Michael H?hne's tool with some changes. Thanks for Regan who point out that using GenerateAuthenticationHeader() instead of hard-coding the Organization's Name.



//check if the current user has the 'System Administrator' role
alert(UserHasRole("System Administrator"));

function UserHasRole(roleName)
{
//get Current User Roles, oXml is an object
var oXml = GetCurrentUserRoles();
if(oXml != null)
{
//select the node text
var roles = oXml.selectNodes("//BusinessEntity/q1:name");
if(roles != null)
{
for( i = 0; i < roles.length; i++)
{
if(roles[i].text == roleName)
{
//return true if user has this role
return true;
}
}
}
}
//otherwise return false
return false;
}

function GetCurrentUserRoles()
{
var xml = "" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
GenerateAuthenticationHeader() +
" <soap:Body>" +
" <RetrieveMultiple xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" +
" <query xmlns:q1=\"http://schemas.microsoft.com/crm/2006/Query\" xsi:type=\"q1:QueryExpression\">" +
" <q1:EntityName>role</q1:EntityName>" +
" <q1:ColumnSet xsi:type=\"q1:ColumnSet\">" +
" <q1:Attributes>" +
" <q1:Attribute>name</q1:Attribute>" +
" </q1:Attributes>" +
" </q1:ColumnSet>" +
" <q1:Distinct>false</q1:Distinct>" +
" <q1:LinkEntities>" +
" <q1:LinkEntity>" +
" <q1:LinkFromAttributeName>roleid</q1:LinkFromAttributeName>" +
" <q1:LinkFromEntityName>role</q1:LinkFromEntityName>" +
" <q1:LinkToEntityName>systemuserroles</q1:LinkToEntityName>" +
" <q1:LinkToAttributeName>roleid</q1:LinkToAttributeName>" +
" <q1:JoinOperator>Inner</q1:JoinOperator>" +
" <q1:LinkEntities>" +
" <q1:LinkEntity>" +
" <q1:LinkFromAttributeName>systemuserid</q1:LinkFromAttributeName>" +
" <q1:LinkFromEntityName>systemuserroles</q1:LinkFromEntityName>" +
" <q1:LinkToEntityName>systemuser</q1:LinkToEntityName>" +
" <q1:LinkToAttributeName>systemuserid</q1:LinkToAttributeName>" +
" <q1:JoinOperator>Inner</q1:JoinOperator>" +
" <q1:LinkCriteria>" +
" <q1:FilterOperator>And</q1:FilterOperator>" +
" <q1:Conditions>" +
" <q1:Condition>" +
" <q1:AttributeName>systemuserid</q1:AttributeName>" +
" <q1:Operator>EqualUserId</q1:Operator>" +
" </q1:Condition>" +
" </q1:Conditions>" +
" </q1:LinkCriteria>" +
" </q1:LinkEntity>" +
" </q1:LinkEntities>" +
" </q1:LinkEntity>" +
" </q1:LinkEntities>" +
" </query>" +
" </RetrieveMultiple>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";

var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");

xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xmlHttpRequest.setRequestHeader("SOAPAction"," http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
xmlHttpRequest.send(xml);

var resultXml = xmlHttpRequest.responseXML;
return(resultXml);
}



Due to Bloger's format issue, I have rewrite the code, it should work for everyone now.:)

46 comments:

  1. Anonymous12:57 am

    Instead of manually piecing the authentication header together you should use the new javascript function GenerateAuthenticationHeader() which takes care of it all for you meaning you can leave out the hard-coding of the ORG_NAME stuff:)

    Fit it in as follows:

    ...xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
    GenerateAuthenticationHeader() +
    " &ltsoap:Body...

    ReplyDelete
  2. See we got something now. :)
    Thanks Regan!
    Jim

    ReplyDelete
  3. Can you please post the entire script with a sample role name?

    Thanks in advance

    ReplyDelete
  4. Anonymous8:46 pm

    Should this code work in CRM 3.0? If so what could I have messed up, because it will not work for me.

    ReplyDelete
  5. Jim,

    I copied all of the code above into the OnLoad event for my main Account form and then changed the alert to look for the "Test" security role.

    When I open an Account the alert always returns false, whether or not the current user is assigned to the "Test" role.

    Is there any part of the code that I need to alter to be specific to my CRM 4.0 installation?

    ReplyDelete
  6. Anonymous8:23 am

    I always get false as well. My alerting some of the workings, I can see roles.length is always equal to zero abnd thus no comparison to the given role is ever made.

    ReplyDelete
  7. Anonymous6:18 am

    the script return "false" for every role...
    i'm missing something?

    ReplyDelete
  8. Anonymous6:18 am

    the script return "false" for every role...
    i'm missing something?

    ReplyDelete
  9. Answer some questions here:
    1. It works for CRM 4.0 only, for CRM 3.0 security check, please check my post on the CRM Forum:

    http://forums.microsoft.com/Dynamics/ShowPost.aspx?PostID=2773685&SiteID=27


    2. For those who always get 'false' return(I known some of you didn't call the function ;)) please add a debug alert at:

    function UserHasRole(roleName)
    {
    //get Current User Roles, oXml is an object
    var oXml = GetCurrentUserRoles();
    alert(oXml.xml); // add this debug alert


    see what's you got!

    Cheers,
    Jim

    ReplyDelete
  10. Anonymous1:14 pm

    Can you please post the entire script with a sample role name?

    Thanks in advance

    ReplyDelete
  11. I've got 'false' return too. I've added debug alert like you do and recieved an empty alert message.
    If you have any idea, please post it.
    Thanks in advance

    ReplyDelete
  12. Anonymous1:46 pm

    hi all,
    i've added the alert as Aline has done and i get the same results. Any Suggestions?

    ReplyDelete
  13. I've got 'false' return too and if I show 'roles.length' using an alert comand it returns '0' elements...

    Any suggestions???

    Thanks a lot.

    ReplyDelete
  14. I'm having the same problem as everyone else - no results. After inserting this line:
    alert("" + xmlHttpRequest.statusText);

    after the
    xmlHttpRequest.send(xml);

    I'm getting "Bad request"

    ReplyDelete
  15. Got it - in the original post the RetrieveMultiple line ends in

    "%22%3e"

    This is wrong, it should end

    \" >"

    ReplyDelete
  16. Anonymous11:32 pm

    So how do you call the function?

    ReplyDelete
  17. Hi,

    I altered the RetrieveMultiple line, as per bigcalm's advice, but still got no results...
    So I added
    alert(xmlHttpRequest.statusText);

    straight after
    xmlHttpRequest.send(xml);

    (again, as per bigcalms previous post)
    and got : Internal Server Error.

    Am logged in as Administrator (am CRM System administrator)

    Anybody got similar results or advice on how to fix?
    thanks,

    ReplyDelete
  18. also...
    I inserted

    alert (oXml.xml)

    and found out that I was getting an error 0x80040203 Invalid Argument

    help???

    thanks,

    ReplyDelete
  19. Anonymous2:42 pm

    Thanks bigcalm...That fixed it for me...

    ReplyDelete
  20. Anonymous3:07 pm

    Does anyone know if there is a page on the webserver that this code can be inserted into so that these objects can be called from any onload event?

    ReplyDelete
  21. Anonymous9:21 pm

    Hi, Can you get user teams using the same code?

    ReplyDelete
  22. I have modified the RetrieveMultiple line like BigCalm has posted but I'm getting the same result:

    ---> "Internal Server Error"

    ---> 0x80040203

    ---> false

    Any suggestion?

    ReplyDelete
  23. Anonymous10:29 pm

    It fixed my problem as BigCalm said above.

    Thanks

    ReplyDelete
  24. Anonymous10:32 pm

    This was a lifesaver! Kudos.

    ReplyDelete
  25. Anonymous5:14 pm

    How would you rewrite this code to include an if statement to check for user role instead of passing one in?

    ReplyDelete
  26. Anonymous3:31 pm

    It's so smooth and clean!

    Thanks man!

    BTW: depending on your requests, you could need to cache the roles xml, in case you have to check for multiple roles.

    I did this:

    /////////////
    var userRoles = null;

    function UserHasRole(roleName)
    {
    if (!userRoles)
    {
    userRoles = GetCurrentUserRoles();
    }
    var oXml = userRoles;
    if(oXml != null)
    .....
    .....
    .....

    This keeps you from double requesting user roles.

    Keep up with the good work!

    ReplyDelete
  27. Thanks a ton Jim, you saved a day for me

    ReplyDelete
  28. Anonymous10:12 pm

    Hi Jim,
    Thanks for the updated code, work great. I do have one question though, do you know why when I load the page it asks me to log in even though I already log into the system? It only asks to log in once though so if I close and open the screen again, it won't ask and know what my role is. Anyway to stop this log in screen from come up at all?

    Thanks.

    ReplyDelete
  29. Anonymous4:21 pm

    What does this look like in c# in plugin development. I want to do this on the server side.

    ReplyDelete
  30. Anonymous4:09 am

    I just posted how to do get the same security roles via C# for CRM server-side development. This was not well documented due to the many-to-many relationship.

    http://jamiemiley.com/wordpress/?p=26

    ReplyDelete
  31. Great Code. I been looking for this high and low for 6 hours now.

    ReplyDelete
  32. Venu.

    Try this.

    //check if the current user has the '_AFM Acct. Rating Priv.' role
    //alert(UserHasRole("_AFM Acct. Rating Priv."));

    var oARC = crmForm.all.accountratingcode;

    if(UserHasRole("_AFM Acct. Rating Priv."))
    {
    oARC.Disabled = false;
    }
    else
    {
    oARC.Disabled = true;
    }

    Put that before or after the main function.

    ReplyDelete
  33. Anonymous7:22 pm

    thanks! just what i needed! you deserve to be MVP

    ReplyDelete
  34. Anonymous10:08 pm

    酒店經紀PRETTY GIRL 台北酒店經紀人 ,禮服店 酒店兼差PRETTY GIRL酒店公關 酒店小姐 彩色爆米花酒店兼職,酒店工作 彩色爆米花酒店經紀, 酒店上班,酒店工作 PRETTY GIRL酒店喝酒酒店上班 彩色爆米花台北酒店酒店小姐 PRETTY GIRL酒店上班酒店打工PRETTY GIRL酒店打工酒店經紀 彩色爆米花

    ReplyDelete
  35. At first, sorry my English is no very good.

    The first method, no is good for me. For me only run this.!!


    alert(UserHasRole("Administrador del sistema"));


    function UserHasRole(roleName)
    {

    var oXml = GetCurrentUserRoles();
    alert(oXml.xml);
    if(oXml != null)
    {
    alert(oXml .text);
    var roles = oXml.selectNodes("//soap:Envelope/soap:Body/RetrieveMultipleResult/BusinessEntities/BusinessEntity") ;
    var rol;
    var valores;
    if(roles != null)
    {

    alert(roles.length);
    for( i = 0; i < roles.length; i++)
    {
    rol=roles[i];
    valores=rol.selectSingleNode("q1:name");
    if(valores.text == roleName)
    {
    return true;
    }
    }
    }
    }
    return false;

    }



    But the second function, no run.
    "Client server was unable to process request. Error 0x80040216"

    Help me please, I dont undertand!!

    ReplyDelete
  36. here is the solution.

    http://www.crowehorwath.com/cs/blogs/crm/archive/2008/05/08/hide-show-fields-in-crm-4-0-based-on-security-role.aspx

    ReplyDelete
  37. Thank you very much for the code Jim. I was looking for those exact two functions, so you saved me a lot of work. Much appreciated.

    Pete

    ReplyDelete
  38. Great Code!!!!
    you solved my problem

    ReplyDelete
  39. Hello, I am trying to do this on the orderprdouct page. I want check all itmems in orderproduct page if a value is checked. If one of the values are checked update the order form by updated a picklist field to a specific value.

    ReplyDelete
  40. Hello, I am trying to do this on the orderprdouct page. I want check all itmems in orderproduct page if a value is checked. If one of the values are checked update the order form by updated a picklist field to a specific value.

    ReplyDelete
  41. This comment has been removed by the author.

    ReplyDelete
  42. Hi,

    Can use this script for hide/show tab by check security role. Such as
    - System Administrator Show Tab
    - Other role hide tab

    Please help and thanks in advance

    ReplyDelete
  43. Hi can i use for CRM2011

    ReplyDelete
  44. HI,
    How can i use it for CRM 2011

    ReplyDelete
  45. Works great. Thank you.

    ReplyDelete