Hi Guys
I just spent the last 2 days working out how to use the Security Application Block with Nettiers and I thourght Id document it here so others didnt have to spend so much time.
First off, each method in the service layer classes (eg: CustomerServiceBase.Generated.cs) has a check to see if the current user has permission to run the method which looks like this:
// throws security exception if not authorized
SecurityContext.IsAuthorized("GetAll");
This makes a call to the generic SecurityContext class's IsAuthorized who asks the security application block if the current user has access to the provided rule. The rule is the name of the entity plus the name of the function, so in this case it would be looking for a rule called Customer.GetAll; so if the user belongs to the roles/users who are listed in the expression for this rule in your security application block xml then they get access, if not IsAuthorized throws an exception.
To get this all working you need to add the Microsoft.Practices.EnterpriseLibrary.Security.Cache.CachingStore.CachingStoreProvider.dll file to your References folder (located in the folder with your solution file), and then add this reference to your Website project (or Winforms if you are not using ASP.Net).
Now open your web.config and find your <namespace>.Data section and then open your provider tag and change the attribute enableMethodAuthorization to true.
If you run your website now you will find that whenever you try to do anything it will crash with an exception like 'Authorization rule Customer.GetAll not found in configuration'. To get this working we will have to add rules to our security application block's xml, and you need one for each of the service operations. You can do this with the Enterprise Library Configurator, but because we will be adding *lots* of rules you may prefer to do this is VS2005. So open entlib.config and find the section containing this:
<rules>
<add expression="I:? OR R:Guest" name="AnonymousRule" />
</rules>
We will want to add our rules to this lis. Add the following for each of your entities, remembering to change the word Customer to the name of your entity:
<add name="Customer.GetByCustomerId" expression="R:Readers" />
<add name="Customer.GetAll" expression="R:Readers" />
<add name="Customer.GetPaged" expression="R:Readers" />
<add name="Customer.Find" expression="R:Readers" />
<add name="Customer.DeepLoadByCustomerId" expression="R:Readers" />
<add name="Customer.DeepLoad" expression="R:Readers" />
<add name="Customer.DeepSave" expression="R:Writers" />
<add name="Customer.Insert" expression="R:Writers" />
<add name="Customer.Update" expression="R:Writers" />
<add name="Customer.Save" expression="R:Writers" />
<add name="Customer.Delete" expression="R:Writers" />
In this example members of the Readers role can perform any of the 'Get' operations, and members of the Writers role can create and update records. If you are using Windows Authentication these roles are simply Active Directory Groups.
Now that we have that in place we wont get that exception anymore, but Nettiers version 2.2.0.559 that I am using needs a change before it will finally work: change the IsAuthorized method in the SecurityContext class (in the Services project) to the following:
/// <summary>
/// Throws a security exception if the user is not authorized.
/// </summary>
public void IsAuthorized(string ruleToCheck)
{
if (ConnectionScope.Current.DataProvider.EnableMethodAuthorization)
{
if (!RuleProvider.Authorize(Principal, string.Format("{0}.{1}", typeof(Entity).Name, ruleToCheck)))
throw new SecurityException(string.Format("User '{0}' does not have permission to perform rule '{1}'",
Principal.Identity.Name, string.Format("{0}.{1}", typeof(Entity).Name, ruleToCheck)));
}
}
You may also like to update the template file so this change remains when you regen next, and this is done in Components/SecurityContext.cst in your Nettiers folder.
Now when a user trys to do something that they dont have permissions to do (as specified in the rules) a security exception will be thrown, which you can catch in your global.asax file and show the user a nice error message.
I dont think I've missed anything, but feel free to let me know if Ive done anything wrong.
Hope someone saves some time now :)
XOR