AccessControlList permission systems restrict access based on verifying membership in static permission lists. For instance, a process in Unix is assigned a user and a group, and is able to manipulate a file if that user or group has permission to do so. Even where a process goes outside the system, it is common to control that through similar access checks. For instance, the SuDo utility allows you to run commands as a different user with different permissions through a more detailed access list, often after you verify that you are who you say you are.
Is it possible for either or both of you to create a separate page to discuss pros and cons at length, and make reference to these here so the content here does not change rapidly? I do not know ACL, like to see what are the arguments, and value both of this viewpoints. DeleteWhenCooked
Possibly use CapabilitySecurityDiscussion for this.
It should be explained that external access is rarely controlled by ACLs but almost universally with capabilities. Because after all is said and done, a password is a capability. The real-world analogue to ACLs aren't passwords but biometrics.
Most commercial operating systems with security models base their permissioning on an AccessControlList model of some type. Details differ widely, but generally a process is identified as having some set of permissions, and its actions are constantly checked against what it can do. It is unlikely that this situation will change for the foreseeable future.
There are many advantages to this kind of system. It is easy to explain. It maps well to our organizational structures. It is familiar.
There are also many disadvantages. Miss a check anywhere, and the security system can be broken. Manage to break into any program with superuser permission, and you have complete control of the system. You cannot give out a precise permission without reworking your security model to make that precise permission easy to state, so people give out broad permissions. You suffer from the ConfusedDeputyProblem.
Essentially, an AccessControlList approach to security makes access dependent on global variables. This suffers from the usual defects of global data in any programming system. It makes the system hard to verify, and distributes widely the areas that have to be coded perfectly to avoid horrible mistakes. As a result, SecurityIsHard (if not impossible).
Sounds like an anti-database viewpoint to me. Smells like a HolyWar coming.
Another disadvantage, the dual to the ConfusedDeputyProblem, is that ACLs promise a type of security which is provably impossible. ACLs promise to restrict access to a resources to a person A, and away from everyone else, even when that person actively wants to share access to the resource.
I see nothing in ACL's that forces such a limit. ACL-based Roles and/or groups can be used for such, or simply use additive assignments. They are as general or specific as you want them to be. LifeIsaBigMessyGraph and so are security assignments. It is a matter of managing privileges and roles to the level or granularity desired.
This security promise is impossible to fulfill without literally putting person A inside of a black box. Without recourse to such draconian and authoritarian measures, it is always the case that person A can set up a proxy to pass along any requests to the system and the results of such requests back to those who actually requested them. IOW, ACLs are a fascist's wet dream, and they provably can't work.
Prove away, hopefully using semi-realistic scenarios.
Compare and contrast to the CapabilitySecurityModel.
Please mark and label the alleged proofs.
Here is one possible relational layout for AccessControlList. Note that Groups is just a many-to-many table here rather than defining specifically named groups. This allows a "group" to become an individual and visa versa easier without adding and deleting. Some will complain about this approach, but it is superior IMO.
Table: ACL --------------- userRef // foreign key to Users table resourceRef accessType // "read-only", etc. Could be made fancier than one field Table: Users ---------------- userID password description isGroupOnly // if cannot be an individual (no password) Table: Groups ----------------- userRef1 // foreign key to Users table userRef2 // foreign key to Users tableNote that there is a "subtractive" access and "additive" access implementation choice. Subtractive assumes the least amount of security when multiple matches apply to a resource, while Additive assumes the most access when multiple matches apply. Additive is easier for users IMO.
All of the ACLs I've used let me grant or deny. They support both additive and subtractive. Add a boolean to the ACL table.
I assume that "deny" trumped "grant".
The ACL administrator adds new entries. Each entry grants or denies an entity access to a resource. As long as the precedence rules are established (deny overrides grant in every system I've used) it's much simpler than restricting the administrator to only grant or deny. If all they can do is grant access then you have to subdivide a group just to deny access to one member of that group. This is supported by all ACLs I've managed (VMS, Windows, J2EE).
I'd add it in addition to accessType. Then you can grant read, grant write, deny read, deny write, etc. Like I said before, add a boolean to the ACL table.
Do you mean like:
Table: ACL --------------- userRef // foreign key to Users table resourceRef // foreign key to Resources table grantRead // Boolean... denyRead grantChange denyChange grantInsert denyInsert grantDelete denyDeleteSimplified variation:
Table: ACL --------------- userRef // foreign key to Users table resourceRef // foreign key to Resources table canRead // blank = ignore, G = grant, D = deny canChange canInsert canDeleteA blank code means that this record will not be used in making a security determination. If there are no others, then deny is assumed.
I assume that in the case of multiple matches (such as belonging to multiple groups that may overlap), a single deny trumps all grants.
Another more generic way to do the above is to have another table with "Access type" so that new access categories, such as "Admin" may be added later on just by adding a new row. But, that may be generic-happy overkill. It might look something like:
Table: ACL --------------- userRef // foreign key to Users table resourceRef // foreign key to Resources table accessRef // access kind. Example: "read", "delete", etc. isGrant // True(1)=grant, False(0)=deny Table: accessKind // optional because a simple name alone may be enough. ----------------- accessID accessDescriptIn this case we don't need an "ignore" code because one would simply delete the record. A character code may be more readable than a Boolean "isGrant", by the way. Thus, we may want to call it something like "grantDeny" or "grantCode" instead.
Another issue that may have to be addressed is whether users want groupings of access kinds. For example, often one will want to grant or revoke read, write, insert, and delete all at once. One approach is have a button or operation that selects multiple grant kinds and changes them as a unit. Another is to create grant kinds that cover multiple kinds. For example, have an "edit" category that grants or denies three categories (write, insert, delete). Which approach is taken depends on the requirements or needs of the customers. In large companies, each department may want their own custom groups that fit their department processes. In that case the organization of grant types starts to resemble that of managing the user side. Perhaps the same logic can be shared between user group management, and "access kinds". However, this is probably overkill in the vast majority of cases. -- top
Access Levels
Many companies prefer "levels" of access kinds. Although this is somewhat limiting in my opinion, it does usually make training and the UI simpler. A typical set of levels that cover most situations would resemble:
0 - (none) 1 - read 2 - read,update 3 - read,update,insert 4 - read,update,insert,delete 5 - read,update,insert,delete,admin > 5 - customIf the company later needs special "levels", then integers above 5 can be used. However, the list after 5 may no longer be "progressive". We can't do "level math" on it. But, at least more can be added without affecting existing level-code handling. This is the nice thing about it. It makes the roughly 90% that fit the normal levels simple to understand and calculate, yet allows for custom access codes for special cases that need custom codes.
An alternative to inventing new level values to create separate features. Zero would mean no access and anything higher would mean access, although for consistency, perhaps use "1". In the deletion example, create a feature called something like "Deletion Screen". Such an approach can handle just about anything, but could get tedious in some situations. Thus, a system might have two kinds of interfaces options for a given item: either the 6 levels (0-to-5), or a Boolean per-feature selection (0=none, 1=has-feature-access). These two variations should cover a vast majority, if not all situations without problems or confusion. An example is given below.
If you want to go full-out "meta" rather than hard-wire the above two systems, then make the level systems be configurable. A schema system for such may resemble:
table: levelSystem ------------------- levelSysID (primary key, could be a number or a name) levelSysDescript maxLevels table: levelSystemItem ------------------- levelSysRef (reference to above table) levelValue (integer level number) levelItemDescript (examples: "no access", "read-only", etc.)This approach is probably overkill in most cases, however.
Trumping
If a user belongs to multiple groups, some algorithm needs to be in place to determine which number is used. One approach is to have groups or settings with a "trump" option. If this option appears, then that setting "trumps" non-trump settings. If there are multiple trump results, then the lowest level is taken. This is sometimes called "additive" and "subtractive", where subtractive is the "trump". Example:
1 3 4In this case, 4 is the final access used.
1 3 Trump 4Here the final access is 3 because of a trump result.
1 Trump 2 Trump 4Here, 1 is the highest since we take the lowest trump.
2 Trump 3 TrumpHere, zero (0) is the highest score. Trump only sets the upper limit, not lower limit.
The formula for selection goes something like this:
min(min(trump_list, 5), max(non_trump_list, 0))(Warning - I have not thoroughly tested this formula as written. Also note that those greater than 5 are not to be included here. Those may require special handling where they have meaning.)
User Interface
The user interface to assign access levels or specific features may resemble this:
User: Martha Stewart ....................|...........Access Levels Feature.............|..0....1....2....3....4....5 -------------------------------------------------- Car parts.............(_)..(X)..(_)..(_)..(_)..(_) Car models............(X)..(_)..(_)..(_)..(_)..(_) Car parts-per-model...(_)..(_)..(_)..(X)..(_)..(_) Dealers...............(_)..(_)..(X)..(_)..(_)..(_) Dealers-per-model.....(_)..(_)..(_)..(_)..(_)..(X) Can reassign sales.........[X] (Boolean check-box, see above) Can approve loans..........[_] Can cancel sales...........[X] ...etc...(Dots to prevent TabMunging)
Putting descriptions ("read, change, insert", etc.) in place of or in addition to the numbers may be helpful. Or, at least include a mouse-rollover description or the like. Some other approach may be needed if a feature has "special" levels. An alternative to the above is a drop-down list. Although more compact, it is less visual in my opinion. But, drop-downs do handle special levels more consistently. Thus, if you have a lot of custom levels, then perhaps go with the drop-down. However, lots of custom levels is perhaps a sign that "levels" are not the right access sub-paradigm.
If "trump" is used (not shown), it could be a check-box on the far right. Note that the above are radio buttons, not check-boxes. Thus, "trump" would be independent of them.
Roles
In some cases, "roles" are used instead of users. A user is assigned a role, and roles are assigned specific features based on an interface panel similar to above. It allows multiple users to share a single security profile without CopyAndPaste of the details. It gets sticky if users are allowed to be in multiple roles. It gets confusing for users or operators to fully understand and audit the impact of having multiple roles. It is probably simpler to create a custom role for a user with special or odd needs rather than manage multiple roles per user.
The drawback of the single role approach is that it may increase the quantity of roles and result in a longer security assignment update process. If multiple roles are allowed per user to alleviate this problem, then make sure the people who manage them are well versed on how to manage and audit such a system. Multi-roles makes the system more powerful, but also increases the skills requirements to use it. If there is a high turn-over rate for the security operator/manager position(s), then the single-role approach is probably warrented.
-- top
Note that one can add categories, groups, taxonomy trees etc. to the access items. It does not restrict nor impose access item CategorizationModels. The security system would deal with items at the ACL table level, but categories can be introduced around that without interfering.
Typical API Usage
In a procedural module, an ACL call typically looks something like:
status = security("resource-name", userID) if not status['allowed'] { message('Permission denied for resource ' . status['resourceDescript']); return; }Or even shortened to:
security("resource-name", stopOnDenial=yes)This assumes that the security function/module can determine the userID by itself, shortening the call. Shortening security-related code is important because it may be used in many places, and thus can be a big source of code bloat if done the long-winded way. One can even do this:
security("resource-name")In this case, the default for "stopOnError" is already "yes". We would pass an explicit "no" if we wanted to handle it in the calling routine for specific situations. Thus, we only need one simple function call in each guarded thing (module, block, function, etc.). However, in practice we don't want to just stop, but exit the specific unit. Thus, it would tend to resemble:
if (security("resource-name")) { do_regular_stuff }We don't need an Else here because the "deny" message is implemented inside of the security routine by default.
If "levels" are used (see above), then the code may resemble:
if (security("resource-name") >= 3) { do_regular_stuff; }Variation:
if (! security("resource-name", "medium")) { return; }-- top