English 中文(简体)
Is this a good data model to implement strongly-typed roles?
原标题:

Yes, this has been asked before here and here. I m curious as to whether the approach I m considering is architecturally sound.

Let me start off by trying to describe what I d like to be able to do with my object model:

class Person {
  ISet<Roles> Roles { get; set; }
}

class RoleDefinition {
  string Name { get; set; }
}

class RoleAssignment {
  RoleDefinition Definition { get; set; }
  Person Person { get; set; }
}

class UserRole : RoleAssignment {
  public virtual string Login { get; set; }
  public virtual string Password { get; set; }
}

With the intent being to be able to work with roles in the following manner:

// Find all "users" with a matching login
from user in userRolesRepository.FindAll(u => u.Login.StartsWith("abc")) 
select user.Person;

To do this, I m considering the following data model

Person table (Id, Name)
RoleDefinition table (Id, Name)
RoleAssignment table (Id, DefId, PersonId)
UserRole table (RoleAssignmentId, Login, Password)
AdminRole table (RoleAssignmentId, ...)

I ll map UserRole and AdminRole as joined-sublass to RoleAssignment in NHibernate.

So, that s a 1:1 between Person and UserRole and AdminRole, a 1:1 between UserRole and RoleAssignment, and an n:1 between RoleAssignment and RoleDefinition.

My question is this: Is this really a good model?

Are there better ways to model this without losing the ability for each role to have strongly typed, queryable properties? How well will it scale, considering I will be adding even more roles to the system as we move along?

问题回答

At first glance, I think it s a bit odd for a single user to have multiple logins and passwords, one for each role, unless you will assume that a user always belongs to a single role. For example, if I had both belonged to roles named Accountant and Salesperson, such as might happen in a small business, it would seem by the definition above that I would have two RoleDefinitions and, as such, two logins and passwords.

Aside from that, in the past, I have mapped this similarly. There is a User class, which is essentially a user profile and has properties such as string UserName, string HashedPassword, TimeZoneInfo TimeZonePreference, ISet<Role> Roles, etc, as well as a LogOn(string password) method.

The LogOn() method of my User class does things like update the FailedLogonsCount property or TemporaryLockoutLiftedAtUtc property and so forth depending on whether or not hashing the passed in password succeeds against the one stored, or it returns a non-persisted object that implements IPrincipal, which is a standard .NET interface.

In this sense, I distinguish between the user s profile (the User class) and their authentication/authorization tokens (non-persisted classes that implement IPrincipal and IIdentity so that they can participate in the various [Authorize] and Thread.CurrentPrincipal schemes used throughout the .NET framework). When the User instance creates the object that implements IPrincipal, it just passes a copy of the user s roles as an array of strings so that the IPrincipal s IsInRole() method will work.

This means that my role names are essentially magic, well-known strings, in effect being a unique key in a Roles database table. But I don t think there s much of a way around that. Indeed, my Role class looks like this:

class Role {
int? Identity { get; }   // database identifier that I never really look at
string RoleEnum { get; } // the "enumeration" that is 
                         //   the well-known string, used in IsInRole()
string RoleName { get; } // a human-friendly, perhaps 
                         //   localizable name of the role
}

I don t have separate subclasses for each role type. Do UserRole and AdminRole as classes really have separate behavior intrinsically? I would submit that they are merely different data points of a generic Role class, so you don t need separate subclasses for each of them.

When you add a role to your system, you are either going to have re-compile the whole shebang with updated checks for that role (this is what I do, I don t expect to add a role frequently), or, if you really wanted to get fancy, the Role class could have a set of Permission objects or some such within them, and your code could just ask the role if role.AllowsUserToDoThis() and have all the permissions checking in one place. Configuring the role s set of Permissions would, therefore, allow you to edit fine-grained access control as the system is running, but you would lose the simplicity of the .NET-provided role-based security model.

There is, as you might have guessed, a million ways to do it, but hopefully this is some helpful feedback. Good luck!





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签