views:

220

answers:

6

This is a moot question as I'm not on this project any more, but it continues to bug me. I wonder if anyone has a better idea for future reference and general good programming practices.

The textbook approach to security is "role-based security". Every screen, report, or other task is attached to one or more roles; every user is assigned to one or more roles; and then each user can exercise the screens, etc. that match his roles and nothing else. Right?

A few years ago I led a team developing a system to manage military technical manuals. Each manual had a "technical content manager", the person responsible for writing or editing it; a "stock manager", responsible for keeping track of copies and getting them shipped out; and an "administrative manager", responsible for the budget and who therefore decided how often the book would be revised, how many copies would be printed, and so on. Of course every book had a bunch of people who would order copies and read it. (As this was the military, you had to be authorized to get your hands on a book, security clearances and all that.) We didn't normally worry about the actual readers, but rather about the people at each base who managed the libraries, but that's not really relevant here.

So ... these are obvious "roles", but a role was tied to a particular book. One person might be the technical content manager for book A, the administrative manager for book B, and a reader of 50 other books. So we couldn't really say that a user had "a role". Each user had different roles for each book.

In addition to this there were more routine system-level privileges: We had a couple of system administrators authorized to update anything in the systeem, help desk people who could see almost any data but not update, etc.

I ended up creating a database like this. (To avoid getting into some of our strange terminology I'll change some field and table names here, the idea is the same.)

Person (person_id, name, etc)

Technical_Manual (manual_id, title, admin_manager_person_id, stock_manager_person_id, content_manager_person_id, etc)

Authorized_Reader (manual_id, person_id, etc)

User (user_id, admin_role, etc)

I was not really happy with this scheme, as it meant that security was split across three tables: the technical_manual table, authorized_reader table, and the user table. But ... was there a cleaner way we could have done it? Any better ideas?

+3  A: 

The way I've recently done something vaguely similar to this winds up looking like:

Person (person_id, name, etc)

Role (role_id, name [admin manager, stock manager, content manager, authorized reader, etc])

Technical_Manual (manual_id, title, etc)

Technical_Manual_Role (manual_id, person_id, role_id)

Additionally, in my system, roles are mostly just default permission bundles, and user permissions for specific actions (Read, Edit, Move, Delete, etc) can be made to vary up or down from the baseline for their role.

chaos
This is probably the simplest form for this sort of security to take. It's easy to follow, and allows for a person to have different roles for different books or multiple roles for the same book.
David
Hmm, this has potential. I don't have room for my comment as a comment, see below as a new post.
Jay
A: 

I know this may seem a bit kludgy, BUT you COULD do something like this:

Roles(role_id, etc)

Technical_Manual(manual_id, acceptable_roles, etc) where acceptable_roles is a delimited list

Then in your program, split the delimited list. I'm not saying this is the BEST way, but it would WORK. Although, I don't know if it would be the best for a military application :)

Jason
ha... even with all the disclaimers, i still get a downvote.
Jason
+1 for the down vote . . . it WOULD work, just not the best way to implement.
andrewWinn
+1  A: 

The problem with force-fitting everything into the pattern of "roles" are the logistics/volumes/workload to keep maintaining the complete set of security rules in the case where those rules are very "fine-grained".

By fine-grained, I mean the case where there are a lot of potential discriminating factors in any authorazation decision, and for each discriminating factor (say, "amount of credit applied for by the customer"), there is a potentially large range of "values" (say, there are 25 distinct ranges of credit-amount-applied-for).

Say there are three such discriminating factors, each with a range of seven possible values (7 distinct ranges of credit amount). Then you would have to define 7*7*7 = 343 roles. Then for each individual user of your system, you would have to assign the complete subset of all the roles that that user can perform. If a user is authorized to decide on a credit application of 50.000.000, then it is quite likely (but then again, not absolutely certain !) that he is also authorized to decide on a credit application of 5.000.000.

That is why in my project, the security-related facilites are limited to identification (userid) and authentication (usercertificate). There are no provisions whatsoever for authorization. Those must be addressed through user-defined constraints.

I basically agree. I'm a firm believer in the principle of using tools when they are useful, but not declaring that because a certain tool was useful in solving the last problem that therefore it is the only tool that anyone should ever use for the rest of time. That said, role-based security is a good model, generally flexible and easy to use, so if I can make it work with little tweaking, I'd like to. If you can't make your app work without creating thousands of roles, then maybe it's not the right solution for that app.
Jay
A: 

You could take a more claims-based approach to the authorization.

there could be some general permissions that each user may or may not have (i.e. admin) that could be directly attached to a role. and we would use your table to match a particular user to which publications they have advanced rights to, and create claims for those entries.

so when a user's authorization is being requested, you get a collection of claims, some high level ones derived from roles, and some publication specific ones, derived from your tables.

Mike Jacobs
That's basically what we did. The problem is just that we ended up with authorization information being in three places -- the role table for system level stuff, the tech_manual table for "manual manager" stuff, and the subscription table for "manual reader" stuff. It seemed cumbersome.
Jay
A: 

Comment on Chaos's response:

It occurs to me that the "system level" roles could fit the same scheme if manual_id for such things could be set to null or some magic value. Yes, I know, some people have a strong aversion to nulls, but it works here. Then there would be a single "security stuff" table, and it's all clean.

I did run into a lot of trouble with the three manager fields. We had a number of queries like, "What books is Bob responsible for?", that required a query with a big OR to go against all three fields. Which meant we needed three indexes. I realized later that we would have been better to break that out into a separate table. But throwing the authorized readers into that same table ... a lot of things clean up. Throw system level stuff in also and ... I like it. It becomes easy to ask "What rights does Mary Jones have?" as well as "Who has rights to the F-15 avionics manual?", "Who are all of our technical content managers?" etc.

Jay
+1  A: 

Just wanted to add more from concept perspective. Even though RBAC (Role Based access control) seems to be in vogue, there are a lot of models like DAC, MAC which have been available to solve the Access Control problem for a longer time (RBAC actually has been formalized sometime around 1995 while other model have been around for much longer and used by military). The way you have explained the requirements I see multiple models in use

  1. RBAC - being used in case of "system-roles" that you talked about.
  2. Attribute/Policy based access control - being used for all the manual related pieces.
  3. MAC - being used to control access to manuals at base i.e. each manual and user has sensitivity level associated and they need to match based on specific criteria to be able to access.

These models can be expressed using standards like XACML (and evaluated at runtime using policy engine implementations) which can describe policy. For example in your case the policy would look something along line of

(Attribute based)

Allow "everybody" to "edit" "manual" if userId = manual.technical_content_manager

Allow "everybody" to "ship" "manual" if userId = manual.stock_manager

(RBAC)

Allow "HelpDesk" to "view" "manual info", "manual"

Allow "Administrator" to "view", "edit", "ship" "manual"

(MAC)

Allow "everybody" to "view" "manual" if userId.level >= manual.level

Based on the policy model above, it is clear that you need to track user-role mapping which can be done using table and retrieved at runtime to feed to policy engine at runtime.

jhash