I have never been a huge fan of how CSLA handles security. One drawback I have found is that you cannot secure a BusinessListBase without first wrapping it with a BusinessBase. I happen to think it is a perfectly valid use case for someone to call Save() on a root collection. I also think it is perfectly valid for someone to want to secure the CRUD operations at this level. So how can you do it?
First some design choices need to be made. For instance, can a user who is not authorized to call save on a root Customer object call save on a root CustomerCollection object? Or are these two independent user stories that should have separate security contexts? If you think like me you answer yes to the first question and no to the last. That is to say, if a user can’t save a root object the user should not be able to save a root collection of those same objects.
If you have already created you own set of CSLA base classes the solution to this is very easy. By overriding the void DataPortal_OnDataPortalInvoke(DataPortalEventArgs e) method on BusinessListBase you get a framework hook to do anything you so desire before the DataPortal operation is executed. I use it to check the AuthorizationRules! And since I have already stated that if a user can’t save a root object the user should not be able to save a root collection of those same objects, I can delegate the security checks to the actual business object. See my code below.
[Serializable]
public class MyBusinessListBase<T, C> : BusinessListBase<T, C>
where T : BusinessListBase<T, C>
where C : Csla.Core.IEditableBusinessObject
{
#region BusinessListBase overrides
/// <summary>
/// Called by the server-side DataPortal prior to calling the
/// requested DataPortal_xyz method.
/// </summary>
/// <param name="e">The DataPortalContext object passed to the DataPortal.</param>
protected override void DataPortal_OnDataPortalInvoke(DataPortalEventArgs e)
{
base.DataPortal_OnDataPortalInvoke(e);
CheckCollectionAuthorization(e);
}
#endregion
#region Private Methods
/// <summary>
/// Checks the collection authorization.
/// </summary>
private static void CheckCollectionAuthorization(DataPortalEventArgs e)
{
if (e.Operation == DataPortalOperations.Fetch)
{
if (!Csla.Security.AuthorizationRules.CanGetObject(typeof(C)))
{
throw new System.Security.SecurityException(string.Format("User not authorized to {0} object type {1}",
"get",
typeof(C).Name));
}
}
else if (e.Operation == DataPortalOperations.Create)
{
if (!Csla.Security.AuthorizationRules.CanCreateObject(typeof(C)))
{
throw new System.Security.SecurityException(string.Format("User not authorized to {0} object type {1}",
"create",
typeof(C).Name));
}
}
else if (e.Operation == DataPortalOperations.Update)
{
if (!Csla.Security.AuthorizationRules.CanEditObject(typeof(C)))
{
throw new System.Security.SecurityException(string.Format("User not authorized to {0} object type {1}",
"Update",
typeof(C).Name));
}
}
}
#endregion
}