I've been maintaining a Rbac
helper for some time now. My goal is to provide an easy way to tell the following:
- When does an user have a role
- What can a user do (with those roles)
- Which users have which role in which resources
And I'm pretty happy with my result so far, I can do the following:
var rbac = new RbacSession();
rbac.AddPermission("owner", "Delete");
rbac.UserIsInRoleIf("mantainer", u => u.Identity.Name == "Bob");
rbac.AddUserRoleForTypeIf<string>("Teacher", (user, resource) => resource == "Hello world");
I have been told to get rid of my fluent interface because all of it was wrong.
And my thought was, well yeah... But hey, wouldn't it be really cool if I could support properties to check if a user
has a role? If I did I could also support easy binding to WPF
Applications. Here follows the implementation
public class RbacPrincipal : IPrincipal, IIdentity
{
[Browsable(false)]
public class CanDoAction
{
private readonly RbacPrincipal _principal;
public CanDoAction(RbacPrincipal principal)
{
_principal = principal;
}
public bool this[string action]
{
get
{
return _principal._session.Query.IsUserAbleTo(_principal, action);
}
}
public bool this[string action, object resource]
{
get
{
return _principal._session.Query.IsUserAbleTo(_principal, action, resource);
}
}
}
[Browsable(false)]
public class IsUserInRole
{
private readonly RbacPrincipal _principal;
public IsUserInRole(RbacPrincipal principal)
{
_principal = principal;
}
public bool this[string role]
{
get
{
return _principal._session.Query.IsUserInRole(_principal, role);
}
}
public bool this[string role, object resource]
{
get
{
return _principal._session.Query.IsUserInRole(_principal, role, resource);
}
}
}
private readonly IPrincipal _principal;
private readonly IRbacSession _session;
public RbacPrincipal(IPrincipal principal, IRbacSession session)
{
_principal = principal;
_session = session;
CanDo = new CanDoAction(this);
Is = new IsUserInRole(this);
}
public bool IsInRole(string role)
{
//check role on the decorated principal se we avoid stackoverflow
return _session.Query.IsUserInRole(_principal, role);
}
public CanDoAction CanDo { get; private set; }
public IsUserInRole Is { get; private set; }
IIdentity IPrincipal.Identity { get { return _principal.Identity; } }
public string Name { get { return _principal.Identity.Name; } }
public string AuthenticationType { get { return _principal.Identity.AuthenticationType; } }
public bool IsAuthenticated { get { return _principal.Identity.IsAuthenticated; } }
}
And an example of the usage of it
var owner = new RbacPrincipal(new Principal
{
Roles = { "owner", "member", "user" }
}, rbac);
Assert.IsTrue(owner.Is["owner"]);
Assert.IsTrue(owner.Is["member"]);
Assert.IsTrue(owner.CanDo["Delete"]);
I leave the link to full test class on repo here
Now I have one limitation if I use bindings with this. The resources cannot be anything but strings, but at least, and most important I can let you check a role of a user with a binding Is[owner]
.
Is this it? Did I finally reach the "yayness"?
owner.Is["owner"]
instead ofowner.Is("owner")
or maybe even betterowner.IsInRole("owner")
? \$\endgroup\$owner.IsInRole("owner")
\$\endgroup\$