I need to know how to go about implementing general security for a C# application. What options do I have in this regard? I would prefer to use an existing framework if it meets my needs - I don't want to re-invent the wheel.
My requirements are as follows:
I am looking for a free / open-source framework/library that has been time-tesed and used by the .Net community.
My application takes a client/server approach, with the server running as a windows service, connecting to a SQL Server database. Communication between client and server will be through WCF.
One other thing that is important is that I need to be able to assign specific users or roles permissions to View/Update/Delete a specific entity, whether it be a Customer, or Product etc. For e.g. Jack can view a certain 3 of 10 customers, but only update the details of customers Microsoft, Yahoo and Google, and can only delete Yahoo.
For coarse-grained security, you might find the inbuilt principal code useful; the user object (and their roles) are controlled in .NET by the "principal", but usefully the runtime itself can enforce this.
The implementation of a principal can be implementation-defined, and you can usually inject your own; for example in WCF.
To see the runtime enforcing coarse access (i.e. which functionality can be accessed, but not limited to which specific data):
static class Roles {
public const string Administrator = "ADMIN";
}
static class Program {
static void Main() {
Thread.CurrentPrincipal = new GenericPrincipal(
new GenericIdentity("Fred"), new string[] { Roles.Administrator });
DeleteDatabase(); // fine
Thread.CurrentPrincipal = new GenericPrincipal(
new GenericIdentity("Barney"), new string[] { });
DeleteDatabase(); // boom
}
[PrincipalPermission(SecurityAction.Demand, Role = Roles.Administrator)]
public static void DeleteDatabase()
{
Console.WriteLine(
Thread.CurrentPrincipal.Identity.Name + " has deleted the database...");
}
}
However, this doesn't help with the fine-grained access (i.e. "Fred can access customer A but not customer B").
Additional; Of course, for fine-grained, you can simply check the required roles at runtime, by checking IsInRole
on the principal:
static void EnforceRole(string role)
{
if (string.IsNullOrEmpty(role)) { return; } // assume anon OK
IPrincipal principal = Thread.CurrentPrincipal;
if (principal == null || !principal.IsInRole(role))
{
throw new SecurityException("Access denied to role: " + role);
}
}
public static User GetUser(string id)
{
User user = Repository.GetUser(id);
EnforceRole(user.AccessRole);
return user;
}
You can also write your own principal / identity objects that do lazy tests / caching of the roles, rather than having to know them all up-front:
class CustomPrincipal : IPrincipal, IIdentity
{
private string cn;
public CustomPrincipal(string cn)
{
if (string.IsNullOrEmpty(cn)) throw new ArgumentNullException("cn");
this.cn = cn;
}
// perhaps not ideal, but serves as an example
readonly Dictionary<string, bool> roleCache =
new Dictionary<string, bool>();
public override string ToString() { return cn; }
bool IIdentity.IsAuthenticated { get { return true; } }
string IIdentity.AuthenticationType { get { return "iris scan"; } }
string IIdentity.Name { get { return cn; } }
IIdentity IPrincipal.Identity { get { return this; } }
bool IPrincipal.IsInRole(string role)
{
if (string.IsNullOrEmpty(role)) return true; // assume anon OK
lock (roleCache)
{
bool value;
if (!roleCache.TryGetValue(role, out value)) {
value = RoleHasAccess(cn, role);
roleCache.Add(role, value);
}
return value;
}
}
private static bool RoleHasAccess(string cn, string role)
{
//TODO: talk to your own security store
}
}