Add a design doc for the new permission model
[bugdar.git] / docs / bugdar2_permissions.md
1 # Bugdar 2.0, Take 3 — Permissions Model
2
3 ## Current Situation
4
5 Bugdar v1 has a multi-tierd permission model. Users have a primary group and multiple secondary groups. Since group permissions are OR'd together, they are cumulative rather than subtractive: once a permission bit is ON, it can't be OFF (see `FetchUserPermissions`). A secondary unit by which permissions are partitioned is products. Products also have usergroup-level permissions that can override other group-level permissions if set explicitly to NO, unlike multiple usergroup permissions, which are OR'd together (see `fetch_on_bits()`).
6
7 A separate permissions matrix exists for custom fields. Custom fields have usergroup-level permissions for no access, view rights, and modification rights.
8
9 The basic algorithm for applying these permissions is:
10
11 bool HasPermission($user, $permission, $bug) {
12 $group_mask = $user->primary_group & $permission;
13
14 $has_product_mask = FALSE;
15 $product_mask = 0;
16
17 for each $group in $user->groups {
18 $group_mask |= $group->permissions & $permission;
19
20 if $bug->product in $group->product_permissions {
21 $has_product_mask = TRUE;
22 $product_mask |= $group->product_permissions[$bug->product] & $permission;
23 }
24 }
25
26 return $has_product_mask ? $product_mask : $group_mask;
27 }
28
29 Thus it's clear that any product permission will override the set group mask. Yet in each of the two tiers of permissions that `HasPermission` evaluates, the individual permission masks are OR'd together.
30
31 ### Database Tables
32 * `bugfield`
33 * Configuration data for custom fields.
34 * `bugfieldpermission`
35 * Controls custom permissions (none, can view, can edit) for custom fields.
36 * Links to `bugfield`.
37 * `permission`
38 * Controls product-specific permissions that cascade on top of the base `usergroup.permissions` field.
39 * `usergroup`
40 * Controls the base permsission of a user, OR'd together in `FetchUserPermissions`.
41
42 ## Version 2
43
44 In version 2, all attributes (product, severity, priority) are all going to be implemented using a "custom field" system. This means that product-level permissions and custom field permissions are no longer applicable. Since products will now be expressed as just ordinary fields, the second tier of permissions needs to be rethought.
45
46 Expressing the v1 relationship is done like such:
47
48 Tuple<Product, Usergroup> = Mask
49
50 Expressing this in v2 with fields yields:
51
52 Tuple<Field, Value, Usergroup> = Mask
53
54 ### Recreating v1
55
56 This sets a Mask when a Field is set to Value and the user is in Usergroup. Product-level permissions can thus be reproduced:
57
58 Tuple<"Product", "Example Product", "Registered Users"> = NewMask
59
60 But it also allows new permission expressions:
61
62 Tuple<"Lock", "Comments", "Registered Users"> = Inherited_Permissions & ~CAN_COMMENT
63
64 Where Inherited_Permissions is the set permissions at the time of Tuple creation, this would allow one to apply security access labels to bugs that alter the effective permissions. This preserves the two-tierd system in place in v1, with the second tier permissions, if set, overriding the first tier.
65
66 ### Extending
67
68 Rather than having the `Tuple<Field, Value, Usergroup>` map to a `Mask`, it instead could map to another tuple:
69
70 Tuple<Field, Value, Usergroup> = Tuple<Allow, Deny>
71
72 Where the first item in the RHS is a bitwise OR'd mask of permissions being allowed, and the second item is a bitwise OR'd mask of permissions being explicitly denied. All other permissions would be inherited. This would create an effective permission mask of:
73
74 EffectiveMask = ((Primary_Group | Secondary_Group...) | Rule_Permissions[Allow]) & ~Rule_Permissions[Deny]
75
76 This would give Deny permissions highest precedence.