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.:)

43 comments:

Regan said...

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() +
" <soap:Body...

Jim Wang said...

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

venu said...

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

Thanks in advance

Anonymous said...

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

cfellers said...

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?

Hoho said...

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.

Anonymous said...

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

Anonymous said...

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

Jim Wang said...

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

Ben said...

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

Thanks in advance

Aline said...

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

Ian said...

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

Simon said...

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.

BigCalm said...

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"

BigCalm said...

Got it - in the original post the RetrieveMultiple line ends in

"%22%3e"

This is wrong, it should end

\" >"

Anonymous said...

So how do you call the function?

IanR said...

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,

IanR said...

also...
I inserted

alert (oXml.xml)

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

help???

thanks,

Anonymous said...

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

Anonymous said...

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?

Anonymous said...

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

Simon said...

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

---> "Internal Server Error"

---> 0x80040203

---> false

Any suggestion?

Anonymous said...

It fixed my problem as BigCalm said above.

Thanks

Priscilla said...

This was a lifesaver! Kudos.

Sam said...

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

A. Chiesa said...

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!

hero said...

Thanks a ton Jim, you saved a day for me

Howard said...

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.

Anonymous said...

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

Anonymous said...

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

Mark Chaffee said...

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

Mark Chaffee said...

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.

Anonymous said...

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

. said...

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

AsturRED said...

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!!

G.Alvarez said...

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

DigiOz Multimedia said...

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

Hojabri, Omid said...

Great Code!!!!
you solved my problem

咪摩兔 said...

八大娛樂全套-指油壓半套

高雄汽車借款.高雄當鋪.順利當鋪
高雄當鋪.高雄汽車借款.華旗當鋪
台中當鋪.台中汽車借款.全國當鋪
台中汽車借款.台中當鋪.利源當鋪

Jose said...

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.

Jose said...

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.

myfootprint said...
This comment has been removed by the author.
myfootprint said...

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