@@ -32,10 +32,16 @@ class AuthZData:
3232 Attributes:
3333 NAMESPACE: The namespace prefix for the data type (e.g., 'user', 'role').
3434 SEPARATOR: The separator between the namespace and the identifier (e.g., ':', '@').
35+
36+ Subclasses are automatically registered by their NAMESPACE for factory pattern.
3537 """
3638
3739 SEPARATOR : str = "@"
38- NAMESPACE : str = None # To be defined in subclasses
40+ NAMESPACE : str = None
41+
42+ # TODO: Implement factory method to return correct subclass based on NAMESPACE prefix.
43+ # This would allow initializing with either subject or scope, etc. and returning the correct subclass.
44+ # So we don't have to manage each subclass separately or hardcoded anywhere.
3945
4046
4147@define
@@ -45,21 +51,25 @@ class ScopeData(AuthZData):
4551 Attributes:
4652 scope_id: The scope identifier (e.g., 'org@Demo').
4753
48- This class assumes that the scope is already namespaced appropriately
49- before being passed in, as scopes can vary widely (e.g., courses, organizations).
54+ Acts as a factory: automatically returns the correct subclass based on the scope_id prefix.
5055 """
5156
52- NAMESPACE : str = "sc" # Generic scope namespace, should be overridden by specific scope types
57+ NAMESPACE : str = "sc"
5358 scope_id : str = ""
54- name : str = "" # Optional human-readable name
59+ name : str = ""
5560
5661 def __attrs_post_init__ (self ):
5762 """Ensure scope ID has appropriate namespace prefix."""
5863 if not self .scope_id :
5964 self .scope_id = f"{ self .NAMESPACE } { self .SEPARATOR } { self .name } " .lower ()
6065
6166 # Allow reverse lookup of name from scope_id
62- if not self .name and self .scope_id and self .NAMESPACE and self .scope_id .startswith (f"{ self .NAMESPACE } { self .SEPARATOR } " ):
67+ if (
68+ not self .name
69+ and self .scope_id
70+ and self .NAMESPACE
71+ and self .scope_id .startswith (f"{ self .NAMESPACE } { self .SEPARATOR } " )
72+ ):
6373 self .name = self .scope_id .split (self .SEPARATOR , 1 )[1 ].lower ()
6474
6575
@@ -81,7 +91,9 @@ def __attrs_post_init__(self):
8191 self .scope_id = f"{ self .NAMESPACE } { self .SEPARATOR } { self .library_id } " .lower ()
8292
8393 # Allow reverse lookup of library_id from scope_id
84- if not self .library_id and self .scope_id .startswith (f"{ self .NAMESPACE } { self .SEPARATOR } " ):
94+ if not self .library_id and self .scope_id .startswith (
95+ f"{ self .NAMESPACE } { self .SEPARATOR } "
96+ ):
8597 self .library_id = self .scope_id .split (self .SEPARATOR , 1 )[1 ].lower ()
8698
8799
@@ -92,27 +104,22 @@ class SubjectData(AuthZData):
92104 Attributes:
93105 subject_id: The subject identifier namespaced (e.g., 'user@john_doe').
94106
95- This class assumes that the subject was already namespaced by their own
96- type (e.g., 'user@', 'group@') before being passed in since subjects can be
97- users, groups, or other entities.
107+ Acts as a factory: automatically returns the correct subclass based on the subject_id prefix.
98108 """
99109
100- NAMESPACE : str = (
101- "sub" # Generic subject namespace, should be overridden by specific subject types
102- )
110+ NAMESPACE : str = "sub"
103111 subject_id : str = ""
104- name : str = "" # Optional human-readable name
112+ name : str = ""
105113
106114 def __attrs_post_init__ (self ):
107- """Ensure subject ID has appropriate namespace prefix.
108-
109- This allows initialization with either name= or subject_id= parameter.
110- """
115+ """Ensure subject ID has appropriate namespace prefix."""
111116 if not self .subject_id :
112117 self .subject_id = f"{ self .NAMESPACE } { self .SEPARATOR } { self .name } " .lower ()
113118
114119 # Allow reverse lookup of name from subject_id
115- if not self .name and self .subject_id .startswith (f"{ self .NAMESPACE } { self .SEPARATOR } " ):
120+ if not self .name and self .subject_id .startswith (
121+ f"{ self .NAMESPACE } { self .SEPARATOR } "
122+ ):
116123 self .name = self .subject_id .split (self .SEPARATOR , 1 )[1 ].lower ()
117124
118125
@@ -140,7 +147,9 @@ def __attrs_post_init__(self):
140147 self .subject_id = f"{ self .NAMESPACE } { self .SEPARATOR } { self .username } " .lower ()
141148
142149 # Allow reverse lookup of username from subject_id
143- if not self .username and self .subject_id .startswith (f"{ self .NAMESPACE } { self .SEPARATOR } " ):
150+ if not self .username and self .subject_id .startswith (
151+ f"{ self .NAMESPACE } { self .SEPARATOR } "
152+ ):
144153 self .username = self .subject_id .split (self .SEPARATOR , 1 )[1 ].lower ()
145154
146155
@@ -165,7 +174,9 @@ def __attrs_post_init__(self):
165174 self .action_id = f"{ self .NAMESPACE } { self .SEPARATOR } { self .name } " .lower ()
166175
167176 # Allow reverse lookup of name from action_id
168- if not self .name and self .action_id .startswith (f"{ self .NAMESPACE } { self .SEPARATOR } " ):
177+ if not self .name and self .action_id .startswith (
178+ f"{ self .NAMESPACE } { self .SEPARATOR } "
179+ ):
169180 self .name = self .action_id .split (self .SEPARATOR , 1 )[1 ].lower ()
170181
171182
@@ -219,13 +230,18 @@ def __attrs_post_init__(self):
219230
220231 This allows initialization with either name= or role_id= parameter.
221232 """
222- if not self .role_id or not self .role_id .startswith (f"{ self .NAMESPACE } { self .SEPARATOR } " ):
233+ if not self .role_id or not self .role_id .startswith (
234+ f"{ self .NAMESPACE } { self .SEPARATOR } "
235+ ):
223236 self .role_id = f"{ self .NAMESPACE } { self .SEPARATOR } { self .name } " .lower ()
224237
225238 # Allow reverse lookup of name from role_id
226- if not self .name and self .role_id .startswith (f"{ self .NAMESPACE } { self .SEPARATOR } " ):
239+ if not self .name and self .role_id .startswith (
240+ f"{ self .NAMESPACE } { self .SEPARATOR } "
241+ ):
227242 self .name = self .role_id .split (self .SEPARATOR , 1 )[1 ].lower ()
228243
244+
229245@define
230246class RoleAssignmentData (AuthZData ):
231247 """A role assignment is the assignment of a role to a subject in a specific scope.
0 commit comments