Project

General

Profile

Authzdesign » History » Version 7

Shuvam Misra, 22/06/2018 06:23 PM

1 3 Shuvam Misra
# Authorization module: core design
2 1 Shuvam Misra
3
The authorization module uses a set of rules in a rulebase to decide who can perform what action on what object. "Who" here is a human user, "what action" is an operation performed on a computer system like a voucher entry, a report viewing, or the adding of a new task in a task tracker. "What object" here means an information entity like a salary record, a voucher, a task list, *etc*.
4
5
{{>toc}}
6
7 2 Shuvam Misra
This page describes a design which we can use for any traditional business application.
8 1 Shuvam Misra
9 2 Shuvam Misra
## Features
10
11
* *Application independent*: can be used with any business application
12 5 Shuvam Misra
13 6 Shuvam Misra
* *Useful for front and back*: it is useful for web service calls to decide whether the curent caller can be permitted to execute the call, and is also useful for Javascript code in the front-end to decide whether to display certain buttons or sections of screens at run-time, depending on a common set of access rules. The set of access rules which apply to a user session are described in-memory in a JSON tree data structure.
14 5 Shuvam Misra
15 2 Shuvam Misra
* *Supports access per object instance*: it supports access controls for specific operations on specific object instances too, *e.g.* can User `rohit` update rates and terms on a specific Purchase Order? This is very powerful for large, critical and long-lasting object instances like tender bids, project plans for large projects, *etc*
16 5 Shuvam Misra
17 4 Shuvam Misra
* *Associate an access rule with a part of an object*: an access rule can carry additional information which associates a part of an object with a permission, *e.g.* this edit permission applies to Section Two of a specific bid document.
18 5 Shuvam Misra
19 2 Shuvam Misra
* *Language independent*: may be implemented in any programming language
20 5 Shuvam Misra
21 2 Shuvam Misra
* *High performance*: requires some data structure processing at user login time, but can then be accessed for very fast go-no-go lookups with each operation or web service call
22 1 Shuvam Misra
23
## The basic entities
24
25
Any authorisation system deals with the following entities:
26
27
* **user**: the human user
28
* **data objects**: the item, or set of items, or class of items, on which operations are being performed
29
* **operations**: the operations being attempted
30
* **permissions**: rules which specify which combinations of the above three entities are permitted. All else are blocked.
31
32
Additional derived entities, defined for convenience, are
33
34
* a group of users, called **user groups**
35
* a group of permissions, sometimes called **roles**
36
37
This design assumes that there is a user master table, a group master table, and a groupusersmap table which maps users to groups of which they are members.
38
39
## The structure of an access rule
40
41
We need to understand the demands on the access rule.
42
43
In its simplest form, a rule will just have three parts:
44
45
* which user the rule applies to: *e.g.* "user `sanjeev`"
46
* which entity the rule applies to: *e.g.* "vouchers" (a class of objects, not just a single object)
47
* which operation is being permitted by the rule: *e.g.* "create"
48
49
This three-part rule says "User `sanjeev` can create vouchers".
50
51
For simple designs, this can even be combined to a two-part rule:
52
53
* the user (`sanjeev`)
54
* the privilege (`vouchercreate`)
55
56
where you combine the class of objects with the operation name and call this combined thing the privilege being given.
57
58
We need more sophistication than this for more complex business applications.
59
60
* **Access to a specific object**: we may want to give User `sanjeev` access to modify a specific indent or purchase order, but not give him the right to do the same with all indents or purchase orders. In our language we are giving a user *ad hoc* access to a specific *object instance*, not a *class of objects*.
61 7 Shuvam Misra
62 1 Shuvam Misra
* **Access to a sub-object**: we may want to give User `sanjeev` access to the Vendor Details section of a specific purchase order, not all sections. We may want to give User `galahad` access to Tax Computations section of the same purchase order. This requires our access rule to have the ability to refer to a sub-object.
63
64
Note that the complexity of a sub-object reference only comes when we need to give a user rights to a specific sub-object of a *specific object instance*. If we had to give the user rights to a sub-object of a whole class of objects, then we would just have re-defined our object definition to refer to a specific section of the object, not the whole object. For instance, if we needed to give `sanjeev` access to the Vendor Details section of *all Purchase Orders*, we would simply have re-defined our object to refer to "`povendorsection`" instead of the entire "`po`". And then we would have given `sanjeev` the access to all `povendorsection`. The real complexity comes when we want to give `sanjeev` the access to the Vendor Details section of *a specific Purchase Order*. In that case, we need to add a new attribute to the access rule.
65
66
So, the final structure of an access rule can be as follows:
67
68
* **ID**: a mandatory, unique ID, the primary key for database access and cross-reference purposes.
69
* **usertype**: one char, with "`U`" for user and "`G`" for user-group. Mandatory, non-NULL
70
* **who**: string, will contain a username or a groupname. Mandatory non-NULL. A "`*`" here means that this rule applies to all users without exception.
71
* **resource**: string, will specify the resource on which the access control will apply. This may have a path notation to identify a module or sub-module in a hierarchy. This allows us to specify an access control at any level of a hierarchy. Possible values could be "`ui/fa`" meaning the UI components of the Financial Accounting system, or "`ws/fa`" meaning the web services of the FA system, or "`ws/fa/vouchers`" to indicate web services of voucher management within the FA system, and so on. Mandatory, non-NULL.
72
* **instance**: string, may contain the ID of a specific resource instance of the type or class indicated by **`resource`**. For instances, if the **`resource`** fields specified "`ws/fa/vouchers`" and the **`instance`** has the value "`20a00bce`", then this is the unique ID of a specific voucher on which the access control is being applied. Optional, may be empty.
73
* **part**: a value indicating a section or part of the object instance on which the access control is being applied. The object instance itself is identified in the **`instance`** attribute above. Examples could be "`vendordetails`", "`candidate[02]`" (for the second candidate in a list of candidates), and so on. Optional, may be NULL.
74
* **action**: a value indicating the operation being permitted, from the enumerated set of all possible operations. For UI related operations, values could be `show`, `edit`, `delete`, *etc*. For web services, operations could be `get`, `update`, `create`, *etc*. There could be any number of possible operations, and a complete set can only be defined in the context of the application and the business operations it supports.
75
76
That's it. Six useful fields (excluding the ID) can capture details of all possible access rules.
77
78
If the system permits it, the **`action`** attribute may be multi-valued, and can carry a list of operations which are permitted. If the system does not support multi-valued attributes, the entire rule will have to be repeated in the database table for each valiue of **`action`**.
79
80
## Supporting the idea of roles
81
82
A role is a collection of access rules. It's a convenience. If all Sales Executives need to be given a set of 23 access permissions, it's nice to be able to group them into something called a "role", and then assign permission to users based on such roles.
83
84
A Roles master can contain the following useful fields:
85
86
* **ID**: the mandatory unique ID column
87
* **descr**: a string description of the role, for human consumption
88
89
The access rules table's **`who`** attribute, which is already capable of holding usernames or user-group names, can now be extended to hold role IDs too, and the **usertype** attribute will then contain "`R`". This means that
90
91
* an ordinary user can be linked to an access rule,
92
* a user-group can be associated with the rule, or
93
* a role can be associated with it
94
95
In addition to this, a roles mapping table is needed, to map a role to either individual users or user-groups. This table will have just three columns:
96
97
* **role**: the ID of the role record in the Roles master
98
* **usertype**: a single-character field, holding "`U`" or "`G`"
99
* **who**: the username of a user, or the groupname of a group
100
101
All columns here are mandatory non-NULL, and the uniqueness criterion will apply to the entire 3-tuple.
102
103
## Summary of tables
104
105
* **`accessrules`**
106
  * **`ID`**
107
  * **`usertype`**
108
  * **`who`**
109
  * **`resource`**
110
  * **`instance`**
111
  * **`part`**
112
  * **`action`**
113
114
115
* **`roles`**
116
  * **`ID`**
117
  * **`descr`**
118
119
* **`rolemembersmap`**
120
  * **`role`**
121
  * **`usertype`**
122
  * **`who`**
123
124
## Loading an access profile
125
126
When a user logs in to the application, her access profile should be loaded from database into in-core storage or a fast cache so that it can be traversed rapidly at each subsequent request or operation to check whether the user is permitted to do so.
127
128
```
129
load_access_profile(user, rulestree)
130
    groupslist = load all user-group names from groups table
131
            where user is a member
132
    roleslist = load all roles from rolemembersmap where
133
            (usertype == 'U' and who == username) OR
134
            (usertype == 'G' and who exists in groupslist)
135
136
    ruleIDlist = load the IDs of all rules from accessrules where
137
            (usertype == 'U' and who == username) OR
138
            (usertype == 'G' and who exists in groupslist) OR
139
            (usertype == 'R' and who exists in roleslist) OR
140
            (who == '*')
141
142
    // we now have the IDs of all the rules which apply to this user
143
144
    // We now convert the set of lists into a tree data structure,
145
    // based on the paths in the "resource" attributes of the rules.
146
    // Any rule with "resource" == "ui" or "ws" or just a one-part
147
    // path will be associated with a Level 1 node just below the root.
148
    // Any rule with "resource" == "ui/fa" will be associated with
149
    // a Level 2 node, labelled "fa", below the "ui" node. And so on.
150
    //
151
    // Each node in the tree has a set of one or more access rules and
152
    // their associated attributes.
153
154
    rulestree = rules_list_to_tree(ruleIDlist)
155
end procedure
156
```
157
158
This process creates an in-memory tree data structure which can be traversed very efficiently whenever a specific access is attempted.
159
160
## Checking access
161
162
Whenever any access is attempted, a function "`is_allowed()`" is called, which traverses the "`rulestree`" created in the loading step, and returns a boolean "`TRUE`" or "`FALSE`". The "`is_allowed()`" function is called with:
163
164
* `user`: the username of the user who is attempting the operation
165
* `resource`: the resource type being operated upon. May in some cases just identify a module or a resource class.
166
* `instance`: the unique ID of the specific instance of type `resource`, *e.g.* Purchase Order ID or voucher ID. May be null for certain operations, for instance an operation which impacts all or many instances of the resource type
167
* `part`: a path string indicating the sub-part of the resource which is being operated upon. This may be NULL.
168
* `operation`: an identifier indicating what is the operation being attempted.
169
170
171
```
172
boolean
173
is_allowed(user, resource, instance, part, operation)
174
    patharray[] = break resource into parts at "/"
175
    thisnode = rulestree    // initialise to the root node of tree
176
177
    for each pathstep in patharray[] do
178
        if any of the rules at thisnode matches the other parameters of the call, then
179
            return TRUE
180
        else if thisnode.childnode corresponding to pathstep exists then
181
            thisnode = thisnode.childnode
182
        else
183
            return FALSE
184
        endif
185
    endfor
186
187
    return FALSE
188
end function
189
```
190
191
This function will be called by every web service call which requires authentication, and if the `rulestree` data structure is passed to the front-end Javascript code at login time, then it can be used by the front-end code too, to decide which UI segments to display or hide.