Changes between Version 9 and Version 10 of doc/developer/UserRolesArchitecture

Show
Ignore:
Timestamp:
07/12/07 14:22:21 (13 years ago)
Author:
benoitg
Comment:

Update to match actual code

Legend:

Unmodified
Added
Removed
Modified
  • doc/developer/UserRolesArchitecture

    v9 v10  
    11[[PageOutline]] 
    2 Original author: Benoit Grégoire, last modified: 2006-12-05 
     2Original author: Benoit Grégoire, last modified: 2007-07-12 
    33= New user permission architecture, General model = 
    44== Why our own rights and permissions framework == 
     
    1616 
    1717== Permissions == 
    18 Permissions are defined in the code, all in a big PHP lookup table to allow easy translations. 
    19 {{{ 
    20 $PERMISSIONS['PERMISSION_ID'] = array(_("permission description"), STAKEHOLDER_TYPE); 
    21 }}} 
     18Permissions are defined in the code, all in a big PHP lookup table to allow easy translations.  An example of permission would be NODE_PERM_EDIT_METADATA.  Being a Node permission type, the user who get's this permission (a node stakeholder) gets to be allowed to edit the metadata of the specific node instance that this permission applies to.  Permissions are defined in Permission::getPermissionArray().   
    2219 
    23 These roles id's must also be listed in a database table for relationships.  The database table will be generated by a synchronization script to keep it in sync.  An elegant to make sure it stays in sync is to run the script inside the catch block of an exception thrown when there is a mismatch, easing the maintenance burden (only code needs to be modified when a permission type is added or deleted) at no performance cost. 
     20'''$PERMISSIONS['PERMISSION_ID'] = array(''' 
     21'''_("permission description"),''' // The description of the permission 
    2422 
    25 An example of permission would be NODE_PERM_EDIT_METADATA.  Being a Node permission type, the user who get's this permission (a node stakeholder) gets to be allowed to edit the metadata of the specific node instance that this permission applies to. 
     23'''Stakeholder::Node,''' // The permission type 
     24 
     25'''true'''  // Wether or not all EXISTING roles of that type should have that permission granted.This is only used when syncing the permissions. It is NOT if logically all roles should have that permission by default.  It is meant to maintain prior functionnality of a server untill the administrators can review the different roles.  In general, when adding new permission types to restrict existing functionnality this should be set to true. When adding brand new functionnality, this should be set to whatever is logical as default behaviour. 
     26 
     27''');''' 
     28 
     29=== Adding or removing permissions === 
     30 * You do NOT need to update the database schema when adding or removing permissions.  The database is kept in sync by a script (Permission::syncPermissions()) that is run everytime an exception is caucht when instanciating a permission.  This way, there is no performance cost, and the maintenance burden is considerably reduced:  Just define your permission and use it, and the database relationships will automagically be updated. 
     31 
     32 * Permission granularity:  One of the main point of the role system is to have much more granular permission, and then manage this forest of potential permission type using the role system.  While more granular permission won't be added overnight, when you do add more granular permission, PLEASE delete the old, larger permission. 
     33 * Where to put permission checks:  In general, in the interface methods of objects:  getAdminUI(), processAdminUI() and delete().  Do not put them in setter or getters, as other parts of the code may need to use them. 
    2634 
    2735== Roles == 
     
    3038If we then assign a user the "Tech support" role for a specific node, that user becomes a "Tech support stakeholder" for that node. 
    3139 
    32 === User types (system roles) === 
    33 While administrators can define new roles, there is a need for a few SYSTEM defined roles that are always present.   
     40Node (ex: Node tech support, Node owner, etc.) 
     41 
     42Network (ex: User allowed to login multiple times, Content curator (can edit other people content(, Content editor, etc.) 
     43Server 
     44Content 
     45=== User types (system roles) (NOT YET IMPLEMENTED) === 
     46While administrators can define new roles, there is a need for a few SYSTEM defined roles that are ALWAYS present.   
    3447 * Anyone (anonymous)  
    3548  * maybe just special-case an ID, like SPLASH_ONLY_USER? 
     
    5467It would be extremely useful to allow an administrator to masquerade as another user for testing and tech support purposes.  Logging out would then get him back to his original user.  As user handling is centralized, this should be reasonably easy to implement. 
    5568 
    56 = Handling insufficient permission = 
    57 In the common case, missing permissions should be handled through exceptions. 
     69= Handling of insufficient permission = 
     70In the common case, missing permissions should be handled through exceptions  (using the Security::require*() methods). 
    5871This will solve two annoying problems, while getting rid of just about all current permission error handling code: 
    59  * If the user is not logged in (because of a timeout, or never logged-in), allow him to login. 
     72 * If the user is not logged in (because of a timeout, or never logged-in), it allows him to login again. 
    6073 * If the user doesn't have the necessary permission(s), inform him (telling which permission is missing) and allow to login as another user. 
    6174In both cases, if the user successfully logs-in (with the rights permission), the get and post parameters are regenerated and the original operation is attempted again. 
     75 
    6276== Exception Implementation == 
    63 In think the simplest and cleanest way is to use set_exception_handler() to handle all exceptions outside MainUI, by calling a new MainUI with the prettily formatted error message and the request-repetition code.   
    64  * The downside is that all access control exceptions are now fatal.   
    65  * The upside is that it's really easy not to get stuck in the middle of a half-rendered page, and that we don't have to wrap all the top level scripts in try-catch blocks. 
     77There is now a global exception handler defined in MainUI.php outside MainUI.  When an exception is thrown, it will call a new MainUI with the prettily formatted error message and the request-repetition code.  Now it's really easy to not get stuck in the middle of a half-rendered page, and we don't have to wrap all the top level scripts in try-catch blocks. 
    6678 
    6779= API = 
    68 Probably only two classes:  Roles and Security 
    69 These function are to be prefered to the next ones, as they will throw exceptions to allow the user to login (or login as another user) to retry the operation: 
     80To most developers, only two classes matter:  Permission (to define permissions) and Security (to use them) 
     81 
     82These function are to be prefered in most cases for permission checks in processAdminUI, as they will throw exceptions to allow the user to login (or login as another user) to retry the operation: 
    7083{{{ 
    71 Security::requirePermission($permission, $target_object, $user);   
    72 Security::requireEitherPermissions(array $permission, $target_object, $user); 
    73 Security::requireAllPermissions(array $permission, $target_object, $user); 
     84Security::requirePermission(Permission $permission, $target_object, $user=null);   
     85Security::requireAnyPermissions(Array $permissionsArray, $target_object, $user=null); 
     86Security::requireAllPermissions(Array $permissionsArray, $user=null); 
     87}}} 
     88 
     89 
     90Mirror functions exist to simply check if the user has the permissions.  Generally used in displayAdminUI (if specific options are unavailable, but the user should still be able to edit other parts of the object), when building menu, etc. 
     91{{{ 
     92Security::hasPermission(Permission $permission, $target_object, $user=null); 
     93Security::hasAnyPermissions(Array $permissionsArray, $target_object, $user=null); 
     94Security::hasAllPermissions(Array $permissionsArray, $user=null); 
    7495}}} 
    7596 
    7697Internal implementation, and corner cases (gateway interaction maybe) 
    7798{{{ 
    78 Security::hasPermission($permission, $targetObject, $user);  //user is optional, if unspecified, the current user is used.  User can also be null, meaning that there is no user currently logged-in 
     99Security::getObjectsWithPermission(Permission $permission, $user=null);  //TO find an object on which the user has the permissions.  Especially usefull to build lists in menus and select boxes. 
    79100Security::hasRole($role, $targetObject, $user);  //user is optional, if unspecified, the current user is used.  User can also be null, meaning that there is no user currently logged-in 
    80 Security::hasEitherPermissions(array $permission, $targetObject, $user); 
    81 Security::hasAllPermissions(array $permission, $targetObject, $user); 
    82101}}} 
    83102 
    84 Mostly for reporting 
     103Mostly for reporting (NOT YET IMPLEMENTED) 
    85104{{{ 
    86105Security::getUsersWithPermission($permission, $targetObject); //Return list of users with this permission 
     
    88107Security::getPermissions($user);  //returns array of PERMISSION constants for this user. 
    89108}}} 
     109 
    90110= Data model = 
    91 permission_type table 
    92  * permission_id 
     111stakeholder_type table 
     112 * stakeholder_type_id 
    93113 
    94114permission table 
    95115 * permission_id REFERENCES permission_type 
    96  * stakeholder_table (Implicitely gives the object type)  
     116 * stakeholder_type_id REFERENCES stakeholder_types 
    97117 
    98118roles table 
    99  * role_id 
    100  * role_name (langstring_id ?) 
     119 * role_id text NOT NULL, 
     120 * role_description_content_id text, 
     121 * is_system_role bool NOT NULL DEFAULT false, 
     122 * stakeholder_type_id text NOT NULL REFERENCES stakeholder_types, 
     123 * role_creation_date 
    101124 
    102125role_has_permissions 
    103126 * role_id REFERENCES roles 
    104  * permission_id REFERENCES role 
    105 There is a check constraint that verifies that the role and permission type matches. 
     127 * permission_id REFERENCES permissions 
    106128 
    107 One table per stakeholder type 
    108 Node (ex: Node tech support, Node owner, etc.) 
    109 Network (ex: User allowed to login multiple times, Content curator (can edit other people content(, Content editor, etc.) 
    110 Server 
    111 Content 
     129One table per stakeholder type, that all inherit from the stakeholders table: 
    112130 
    113 (object_type)_stakeholders 
    114  * object_id 
    115  * user_id 
    116  * role_id 
     131stakeholders 
     132 * user_id text NOT NULL 
     133 * role_id text NOT NULL 
     134 * object_id text NOT NULL 
     135 
     136The tables are named from the stakeholder types.  For example, for nodes, the table is called node_stakeholders: 
     137 
    117138Note that the stakeholder tables list user-role pairs (and NOT user-permission pairs) 
    118139