I've been using PrincipalPermission for a while in wcf services. [PrincipalPermission(SecurityAction.Demand, Role = SecurityRoles.CanManageUsers)]
although now i have a requirement to simplify roles by business unit. - currently aspnet_roles has fine grained can* permissions.
Here is my approach and wanted to see if anyone can provide feedback, code review before i implement my suggestion.
1) aspnet_roles - business unit role
2) create 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 implemented first iteration and it is working nicely. [PermissionValidate(SecurityAction.Demand, HasPermission = CensusSchedulerRoles.CanUpdateCensusScheduler)]
The only thing i do not like is the IPermissionRepository cannot be injected .. anyone have an example using policy block in entlib or using postsharp?
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));
}
}
}
}