Skip to content

Commit 8cdf8fc

Browse files
devGregAclaude
andcommitted
feat(authorization): introduce Action enum and permission_to_action mapper
Add a string Action enum that names the legacy permission vocabulary (view / edit / add / delete / import / staff_only / superuser_only) and a suffix-driven permission_to_action() helper that flattens any of the existing Permissions enum members down to one of those actions. The fine-grained Permissions enum is preserved so existing call sites (`@user_is_authorized(Permissions.X, ...)`) keep compiling. Track B step DefectDojo#13 will simplify the call sites to action strings; until then this gives authorization.py a single dispatch surface that accepts both the legacy Permissions enum members AND the new action strings. Roles enum preserved purely as a label set — legacy authorization no longer branches on roles. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5708ae1 commit 8cdf8fc

1 file changed

Lines changed: 79 additions & 1 deletion

File tree

dojo/authorization/roles_permissions.py

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,47 @@
1-
from enum import IntEnum
1+
from enum import IntEnum, StrEnum
2+
3+
4+
class Action(StrEnum):
5+
6+
"""
7+
Legacy permission actions. The fine-grained Permissions enum below is
8+
preserved so existing call sites (`@user_is_authorized(Permissions.X, …)`)
9+
keep compiling, but every check now flattens to one of these intents:
10+
11+
* View — read-only access to an object (membership in
12+
authorized_users, or staff/superuser bypass)
13+
* Edit / Add — mutating an existing object or creating one
14+
(membership in authorized_users + staff bypass)
15+
* Delete — destroying an object (staff/superuser only)
16+
* Import — bulk ingest of scan results (staff bypass + per-product
17+
membership)
18+
* StaffOnly — administrative actions like member management or
19+
configuration changes
20+
* SuperuserOnly — system-wide changes that legacy never delegated
21+
22+
The role hierarchy (Reader / Writer / Maintainer / Owner) does not exist
23+
in this model; per-product distinctions collapse to membership.
24+
"""
25+
26+
View = "view"
27+
Add = "add"
28+
Edit = "edit"
29+
Delete = "delete"
30+
Import = "import"
31+
StaffOnly = "staff_only"
32+
SuperuserOnly = "superuser_only"
233

334

435
class Roles(IntEnum):
36+
37+
"""
38+
Preserved for backward compatibility. Legacy authorization no longer
39+
branches on roles — these values now act as labels only. The membership
40+
tables (Product_Member, Product_Type_Member, Global_Role) exist as inert
41+
data tables that the dojo-pro plugin can adopt; nothing in dojo/ reads
42+
role assignments after the legacy rewrite.
43+
"""
44+
545
Reader = 5
646
API_Importer = 1
747
Writer = 2
@@ -522,3 +562,41 @@ def get_global_roles_with_permissions():
522562
Roles.Maintainer: {Permissions.Product_Type_Add},
523563
Roles.Owner: {Permissions.Product_Type_Add},
524564
}
565+
566+
567+
def permission_to_action(permission):
568+
"""
569+
Map a fine-grained Permissions enum member (or an Action / action string)
570+
to a legacy Action.
571+
572+
The mapping is suffix-based: every Permissions name is of the form
573+
``<Noun>_<Verb>``. Verbs map to actions; the noun is irrelevant because
574+
legacy authorization is not noun-aware (the object passed at check time
575+
determines the membership scope).
576+
"""
577+
if isinstance(permission, Action):
578+
return permission
579+
if isinstance(permission, str):
580+
try:
581+
return Action(permission)
582+
except ValueError:
583+
return Action.View
584+
585+
name = getattr(permission, "name", "") or str(permission)
586+
587+
if name == "Risk_Acceptance":
588+
return Action.Edit
589+
if name == "Import_Scan_Result":
590+
return Action.Import
591+
if name.endswith(("_View", "_View_History")):
592+
return Action.View
593+
if name.endswith(("_Edit", "_Configure_Notifications")):
594+
return Action.Edit
595+
if name.endswith("_Delete"):
596+
return Action.Delete
597+
if name.endswith(("_Add_Product", "_Add")):
598+
return Action.Add
599+
if "_Manage_" in name or name.endswith("_Add_Owner"):
600+
return Action.StaffOnly
601+
602+
return Action.View

0 commit comments

Comments
 (0)