fine-grained permissions; PrincipalPermission - roles seperate from permissions;
I've been using PrincipalPermission for a while in wcf services. [PrincipalPermission(SecurityAction.Demand, Role = SecurityRoles.CanManageUsers)]
Our roles are prefixed with: Can* and is how we achieve fine grained actions control with the built in asp.net membership system.
This makes it hard to k开发者_开发百科now as a business unit what fine grained roles we can give to a user.
Here is my new approach and wanted to see if anyone can provide feedback, code review before i implement my suggestion.
1) aspnet_roles - business unit role
2) Extend the asp.net membership system by creating a permission table and Role_Permission table and User_Permission table (many to many)
3) create custom CodeAccessSecurityAttribute + that looks at new tables [CustomPermissionCheck(Security.Demand, HasPermission="can*")] first iteration i'll statically new the dependent repository.. ideally i would like an aop style attribute that has repository injected IPermissionRepository.HasPermission(...);
If i approach new aop way i probably will stop inheriting from CodeAccessSecurityAttribute -- what do the security guys have to say about this?
has anyone else solved this, is there something in the framework that i've missed?
I would say that if you are in ASP.NET, then you should implement a custom RoleProvider.
In your custom RoleProvider, you would access another table which would have the business groups linked to the fine grained permissions.
Then, when you find out the user, you can find out the business group that they are in and assign all of the appropriate roles in the RoleProvider and not change any of the existing code that you have.
It also works better, because it allows you to change what groups have what permissions easily, while keeping the domain model for permissions pure on the code side.
I implemented first iteration and it is working nicely. [PermissionValidate(SecurityAction.Demand, HasPermission = CensusSchedulerRoles.CanUpdateCensusScheduler)]
public void Demand()
{
var principal = Thread.CurrentPrincipal;
if(principal == null || principal.Identity.IsAuthenticated == false)
{
throw new SecurityException("Unable to get IPrincipal.");
}
if(principal.Identity.IsAuthenticated == false)
{
throw new SecurityException("You must be authenticated.");
}
#warning this should be moved to an aop attribute that is injected by a ioc container.
using (var connection = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["......."].ConnectionString))
{
connection.Open();
using(var command = new SqlCommand(
@"
SELECT COUNT(t.name) FROM
(
SELECT p.name, u.UserName FROM
aspnet_Users as u
INNER JOIN [User_Permission] as up
ON up.user_id = u.UserId
INNER JOIN Permission as p
ON p.id = up.permission_id
UNION
SELECT p2.name, u2.UserName FROM
aspnet_Users as u2
INNER JOIN aspnet_UsersInRoles as uir
ON uir.UserId = u2.UserId
INNER JOIN aspnet_Roles as r
ON r.RoleId = uir.RoleId
INNER JOIN Role_Permission as rp
ON rp.role_id = r.RoleId
INNER JOIN Permission as p2
ON p2.id = rp.permission_id
) as t
WHERE t.UserName = @username AND t.name = @haspermission
", connection))
{
command.Parameters.Add("@username", SqlDbType.VarChar).Value = Thread.CurrentPrincipal.Identity.Name;
command.Parameters.Add("@haspermission", SqlDbType.VarChar).Value = _permissionRequested;
if( Convert.ToInt32(command.ExecuteScalar()) <=0)
{
throw new SecurityException(String.Format("User '{0}' is not assigned permission '{1}'.", principal.Identity.Name, _permissionRequested));
}
}
}
}
精彩评论