better-notion 0.9.9__py3-none-any.whl → 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- better_notion/plugins/official/__init__.py +3 -1
- better_notion/plugins/official/agents.py +356 -0
- better_notion/utils/agents/__init__.py +65 -0
- better_notion/utils/agents/auth.py +235 -0
- better_notion/utils/agents/dependency_resolver.py +368 -0
- better_notion/utils/agents/project_context.py +232 -0
- better_notion/utils/agents/rbac.py +371 -0
- better_notion/utils/agents/schemas.py +614 -0
- better_notion/utils/agents/state_machine.py +216 -0
- better_notion/utils/agents/workspace.py +371 -0
- {better_notion-0.9.9.dist-info → better_notion-1.0.1.dist-info}/METADATA +2 -2
- {better_notion-0.9.9.dist-info → better_notion-1.0.1.dist-info}/RECORD +15 -6
- {better_notion-0.9.9.dist-info → better_notion-1.0.1.dist-info}/WHEEL +0 -0
- {better_notion-0.9.9.dist-info → better_notion-1.0.1.dist-info}/entry_points.txt +0 -0
- {better_notion-0.9.9.dist-info → better_notion-1.0.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
"""Role-based access control (RBAC) for the agents workflow system.
|
|
2
|
+
|
|
3
|
+
This module provides role-based permission management for different agent types.
|
|
4
|
+
Each role has specific permissions that control what actions they can perform.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, List, Literal, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# Define valid roles
|
|
11
|
+
RoleType = Literal[
|
|
12
|
+
"Developer",
|
|
13
|
+
"PM",
|
|
14
|
+
"Product Analyst",
|
|
15
|
+
"QA",
|
|
16
|
+
"Designer",
|
|
17
|
+
"DevOps",
|
|
18
|
+
"Admin",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Define permission structure
|
|
23
|
+
# Format: resource:action (e.g., "tasks:claim", "projects:create")
|
|
24
|
+
PermissionType = str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class RoleManager:
|
|
28
|
+
"""Manages role-based access control for agent operations.
|
|
29
|
+
|
|
30
|
+
The RoleManager defines permissions for each role and provides methods
|
|
31
|
+
to check if a role has a specific permission.
|
|
32
|
+
|
|
33
|
+
Roles:
|
|
34
|
+
- Developer: Can work on tasks, submit ideas, report issues
|
|
35
|
+
- PM: Can manage projects, tasks, review ideas, view analytics
|
|
36
|
+
- Product Analyst: Can view projects, analyze data, generate reports
|
|
37
|
+
- QA: Can review tasks, create incidents, report issues
|
|
38
|
+
- Designer: Can work on design tasks, submit ideas
|
|
39
|
+
- DevOps: Can manage deployments, infrastructure tasks
|
|
40
|
+
- Admin: Full access to all resources and actions
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
>>> # Check if a role has a permission
|
|
44
|
+
>>> RoleManager.check_permission("Developer", "tasks:claim")
|
|
45
|
+
True
|
|
46
|
+
|
|
47
|
+
>>> # Require a permission (raises PermissionError if denied)
|
|
48
|
+
>>> RoleManager.require_permission("Developer", "projects:create")
|
|
49
|
+
PermissionError: Role 'Developer' does not have permission 'projects:create'
|
|
50
|
+
|
|
51
|
+
>>> # Get all permissions for a role
|
|
52
|
+
>>> perms = RoleManager.get_permissions("PM")
|
|
53
|
+
>>> print(perms)
|
|
54
|
+
['tasks:*', 'projects:create', 'projects:update', ...]
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
# Define permissions for each role
|
|
58
|
+
PERMISSIONS: Dict[RoleType, List[PermissionType]] = {
|
|
59
|
+
"Developer": [
|
|
60
|
+
# Task operations
|
|
61
|
+
"tasks:claim",
|
|
62
|
+
"tasks:start",
|
|
63
|
+
"tasks:complete",
|
|
64
|
+
"tasks:list",
|
|
65
|
+
"tasks:view",
|
|
66
|
+
"tasks:comment",
|
|
67
|
+
# Idea operations
|
|
68
|
+
"ideas:submit",
|
|
69
|
+
"ideas:view",
|
|
70
|
+
# Issue operations
|
|
71
|
+
"issues:report",
|
|
72
|
+
"issues:view",
|
|
73
|
+
# Version operations
|
|
74
|
+
"versions:view",
|
|
75
|
+
# Basic project visibility
|
|
76
|
+
"projects:view",
|
|
77
|
+
],
|
|
78
|
+
"PM": [
|
|
79
|
+
# Full task access
|
|
80
|
+
"tasks:*",
|
|
81
|
+
# Project management
|
|
82
|
+
"projects:create",
|
|
83
|
+
"projects:update",
|
|
84
|
+
"projects:delete",
|
|
85
|
+
"projects:view",
|
|
86
|
+
"projects:list",
|
|
87
|
+
# Version management
|
|
88
|
+
"versions:create",
|
|
89
|
+
"versions:update",
|
|
90
|
+
"versions:delete",
|
|
91
|
+
"versions:view",
|
|
92
|
+
"versions:list",
|
|
93
|
+
# Idea management
|
|
94
|
+
"ideas:*",
|
|
95
|
+
# Issue management
|
|
96
|
+
"issues:*",
|
|
97
|
+
# Analytics and reporting
|
|
98
|
+
"analytics:*",
|
|
99
|
+
"report:*",
|
|
100
|
+
# Organization visibility
|
|
101
|
+
"organizations:view",
|
|
102
|
+
"organizations:list",
|
|
103
|
+
],
|
|
104
|
+
"Product Analyst": [
|
|
105
|
+
# Read-only project access
|
|
106
|
+
"projects:view",
|
|
107
|
+
"projects:list",
|
|
108
|
+
# Read-only task access
|
|
109
|
+
"tasks:view",
|
|
110
|
+
"tasks:list",
|
|
111
|
+
# Analytics and reporting
|
|
112
|
+
"analytics:view",
|
|
113
|
+
"analytics:cycle-time",
|
|
114
|
+
"analytics:completion-rate",
|
|
115
|
+
"report:view",
|
|
116
|
+
"report:generate",
|
|
117
|
+
# Version visibility
|
|
118
|
+
"versions:view",
|
|
119
|
+
"versions:list",
|
|
120
|
+
# Organization visibility
|
|
121
|
+
"organizations:view",
|
|
122
|
+
"organizations:list",
|
|
123
|
+
],
|
|
124
|
+
"QA": [
|
|
125
|
+
# Task review
|
|
126
|
+
"tasks:view",
|
|
127
|
+
"tasks:list",
|
|
128
|
+
"tasks:review",
|
|
129
|
+
"tasks:comment",
|
|
130
|
+
# Incident management
|
|
131
|
+
"incidents:create",
|
|
132
|
+
"incidents:update",
|
|
133
|
+
"incidents:resolve",
|
|
134
|
+
"incidents:view",
|
|
135
|
+
"incidents:list",
|
|
136
|
+
# Issue reporting
|
|
137
|
+
"issues:report",
|
|
138
|
+
"issues:view",
|
|
139
|
+
"issues:resolve",
|
|
140
|
+
# Project visibility
|
|
141
|
+
"projects:view",
|
|
142
|
+
"projects:list",
|
|
143
|
+
# Version visibility
|
|
144
|
+
"versions:view",
|
|
145
|
+
"versions:list",
|
|
146
|
+
],
|
|
147
|
+
"Designer": [
|
|
148
|
+
# Task operations
|
|
149
|
+
"tasks:claim",
|
|
150
|
+
"tasks:start",
|
|
151
|
+
"tasks:complete",
|
|
152
|
+
"tasks:list",
|
|
153
|
+
"tasks:view",
|
|
154
|
+
"tasks:comment",
|
|
155
|
+
# Design task management
|
|
156
|
+
"tasks:create:design",
|
|
157
|
+
# Idea operations
|
|
158
|
+
"ideas:submit",
|
|
159
|
+
"ideas:view",
|
|
160
|
+
# Issue operations
|
|
161
|
+
"issues:report",
|
|
162
|
+
"issues:view",
|
|
163
|
+
# Project visibility
|
|
164
|
+
"projects:view",
|
|
165
|
+
],
|
|
166
|
+
"DevOps": [
|
|
167
|
+
# Task operations
|
|
168
|
+
"tasks:claim",
|
|
169
|
+
"tasks:start",
|
|
170
|
+
"tasks:complete",
|
|
171
|
+
"tasks:list",
|
|
172
|
+
"tasks:view",
|
|
173
|
+
"tasks:comment",
|
|
174
|
+
# Infrastructure task management
|
|
175
|
+
"tasks:create:infrastructure",
|
|
176
|
+
"tasks:create:deployment",
|
|
177
|
+
# Incident management
|
|
178
|
+
"incidents:*",
|
|
179
|
+
# Issue operations
|
|
180
|
+
"issues:report",
|
|
181
|
+
"issues:view",
|
|
182
|
+
"issues:resolve",
|
|
183
|
+
# Version management
|
|
184
|
+
"versions:create",
|
|
185
|
+
"versions:update",
|
|
186
|
+
"versions:view",
|
|
187
|
+
# Project visibility
|
|
188
|
+
"projects:view",
|
|
189
|
+
"projects:list",
|
|
190
|
+
],
|
|
191
|
+
"Admin": [
|
|
192
|
+
# Full access to everything
|
|
193
|
+
"*",
|
|
194
|
+
],
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@classmethod
|
|
198
|
+
def check_permission(
|
|
199
|
+
cls,
|
|
200
|
+
role: str,
|
|
201
|
+
permission: str,
|
|
202
|
+
) -> bool:
|
|
203
|
+
"""Check if a role has a specific permission.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
role: Role name (e.g., "Developer", "PM")
|
|
207
|
+
permission: Permission string (e.g., "tasks:claim")
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
True if role has the permission, False otherwise
|
|
211
|
+
|
|
212
|
+
Example:
|
|
213
|
+
>>> RoleManager.check_permission("Developer", "tasks:claim")
|
|
214
|
+
True
|
|
215
|
+
|
|
216
|
+
>>> RoleManager.check_permission("Developer", "projects:create")
|
|
217
|
+
False
|
|
218
|
+
"""
|
|
219
|
+
# Admin has all permissions
|
|
220
|
+
if role == "Admin":
|
|
221
|
+
return True
|
|
222
|
+
|
|
223
|
+
# Check if role exists
|
|
224
|
+
if role not in cls.PERMISSIONS:
|
|
225
|
+
return False
|
|
226
|
+
|
|
227
|
+
permissions = cls.PERMISSIONS[role]
|
|
228
|
+
|
|
229
|
+
# Check for wildcard permission
|
|
230
|
+
if "*" in permissions:
|
|
231
|
+
return True
|
|
232
|
+
|
|
233
|
+
# Check for exact match
|
|
234
|
+
if permission in permissions:
|
|
235
|
+
return True
|
|
236
|
+
|
|
237
|
+
# Check for resource wildcard (e.g., "tasks:*" matches "tasks:claim")
|
|
238
|
+
for perm in permissions:
|
|
239
|
+
if perm.endswith(":*"):
|
|
240
|
+
resource = perm[:-2] # Remove ":*"
|
|
241
|
+
if permission.startswith(resource + ":"):
|
|
242
|
+
return True
|
|
243
|
+
|
|
244
|
+
# Check for action-specific wildcard (e.g., "tasks:create:*")
|
|
245
|
+
# This would match "tasks:create:design" but not "tasks:update"
|
|
246
|
+
if ":" in permission:
|
|
247
|
+
perm_resource, perm_action = permission.split(":", 1)
|
|
248
|
+
|
|
249
|
+
for role_perm in permissions:
|
|
250
|
+
if ":" in role_perm:
|
|
251
|
+
role_resource, role_action = role_perm.split(":", 1)
|
|
252
|
+
|
|
253
|
+
# Match resource and action
|
|
254
|
+
if role_resource == perm_resource:
|
|
255
|
+
if role_action == "*" or role_action == perm_action:
|
|
256
|
+
return True
|
|
257
|
+
|
|
258
|
+
# Check for sub-action wildcard (e.g., "create:*")
|
|
259
|
+
if role_action.endswith(":*"):
|
|
260
|
+
action_prefix = role_action[:-2]
|
|
261
|
+
if perm_action.startswith(action_prefix):
|
|
262
|
+
return True
|
|
263
|
+
|
|
264
|
+
return False
|
|
265
|
+
|
|
266
|
+
@classmethod
|
|
267
|
+
def require_permission(
|
|
268
|
+
cls,
|
|
269
|
+
role: str,
|
|
270
|
+
permission: str,
|
|
271
|
+
) -> None:
|
|
272
|
+
"""Require a permission for a role, raising PermissionError if denied.
|
|
273
|
+
|
|
274
|
+
This is a convenience method that raises an exception instead of
|
|
275
|
+
returning a boolean, making it useful for guard clauses.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
role: Role name (e.g., "Developer", "PM")
|
|
279
|
+
permission: Permission string (e.g., "tasks:claim")
|
|
280
|
+
|
|
281
|
+
Raises:
|
|
282
|
+
PermissionError: If role does not have the permission
|
|
283
|
+
|
|
284
|
+
Example:
|
|
285
|
+
>>> RoleManager.require_permission("Developer", "tasks:claim")
|
|
286
|
+
>>> # OK, no error
|
|
287
|
+
|
|
288
|
+
>>> RoleManager.require_permission("Developer", "projects:create")
|
|
289
|
+
PermissionError: Role 'Developer' does not have permission 'projects:create'
|
|
290
|
+
"""
|
|
291
|
+
if not cls.check_permission(role, permission):
|
|
292
|
+
raise PermissionError(
|
|
293
|
+
f"Role '{role}' does not have permission '{permission}'"
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
@classmethod
|
|
297
|
+
def get_permissions(cls, role: str) -> List[str]:
|
|
298
|
+
"""Get all permissions for a role.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
role: Role name
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
List of permission strings for the role
|
|
305
|
+
|
|
306
|
+
Example:
|
|
307
|
+
>>> perms = RoleManager.get_permissions("Developer")
|
|
308
|
+
>>> print(perms)
|
|
309
|
+
['tasks:claim', 'tasks:start', 'tasks:complete', ...]
|
|
310
|
+
"""
|
|
311
|
+
return cls.PERMISSIONS.get(role, [])
|
|
312
|
+
|
|
313
|
+
@classmethod
|
|
314
|
+
def get_all_roles(cls) -> List[str]:
|
|
315
|
+
"""Get list of all defined roles.
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
List of role names
|
|
319
|
+
|
|
320
|
+
Example:
|
|
321
|
+
>>> roles = RoleManager.get_all_roles()
|
|
322
|
+
>>> print(roles)
|
|
323
|
+
['Developer', 'PM', 'Product Analyst', 'QA', 'Designer', 'DevOps', 'Admin']
|
|
324
|
+
"""
|
|
325
|
+
return list(cls.PERMISSIONS.keys())
|
|
326
|
+
|
|
327
|
+
@classmethod
|
|
328
|
+
def is_valid_role(cls, role: str) -> bool:
|
|
329
|
+
"""Check if a role name is valid.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
role: Role name to validate
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
True if role is defined, False otherwise
|
|
336
|
+
|
|
337
|
+
Example:
|
|
338
|
+
>>> RoleManager.is_valid_role("Developer")
|
|
339
|
+
True
|
|
340
|
+
|
|
341
|
+
>>> RoleManager.is_valid_role("InvalidRole")
|
|
342
|
+
False
|
|
343
|
+
"""
|
|
344
|
+
return role in cls.PERMISSIONS
|
|
345
|
+
|
|
346
|
+
@classmethod
|
|
347
|
+
def get_role_description(cls, role: str) -> Optional[str]:
|
|
348
|
+
"""Get a human-readable description for a role.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
role: Role name
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
Role description or None if role doesn't exist
|
|
355
|
+
|
|
356
|
+
Example:
|
|
357
|
+
>>> desc = RoleManager.get_role_description("Developer")
|
|
358
|
+
>>> print(desc)
|
|
359
|
+
'Can work on tasks, submit ideas, report issues'
|
|
360
|
+
"""
|
|
361
|
+
descriptions = {
|
|
362
|
+
"Developer": "Can work on tasks, submit ideas, report issues",
|
|
363
|
+
"PM": "Can manage projects, tasks, review ideas, view analytics",
|
|
364
|
+
"Product Analyst": "Can view projects, analyze data, generate reports",
|
|
365
|
+
"QA": "Can review tasks, create incidents, report issues",
|
|
366
|
+
"Designer": "Can work on design tasks, submit ideas",
|
|
367
|
+
"DevOps": "Can manage deployments, infrastructure tasks",
|
|
368
|
+
"Admin": "Full access to all resources and actions",
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return descriptions.get(role)
|