basyx commited on
Commit
ef1511e
·
verified ·
1 Parent(s): 6443d7d

Ensure workspace roles retain baseline read scopes

Browse files
Files changed (1) hide show
  1. app/security/studio_identity.py +28 -1
app/security/studio_identity.py CHANGED
@@ -10,6 +10,32 @@ from app.security.context import AuthContext
10
  from app.security.scopes import DEFAULT_ROLE_SCOPES
11
  from app.security.tenancy import TenantPrincipal
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  _HEX_SHA256 = re.compile(r"^[a-f0-9]{64}$")
14
 
15
 
@@ -79,10 +105,11 @@ def constrain_context_to_membership(
79
  effective_role = role_map.get(raw_role)
80
  if effective_role is None:
81
  raise ValueError("Unsupported workspace membership role.")
 
82
  return replace(
83
  context,
84
  role=effective_role,
85
- scopes=frozenset(context.scopes.intersection(DEFAULT_ROLE_SCOPES[effective_role])),
86
  workspace_id=principal.workspace_id,
87
  user_id=principal.user_id,
88
  membership_id=principal.membership_id,
 
10
  from app.security.scopes import DEFAULT_ROLE_SCOPES
11
  from app.security.tenancy import TenantPrincipal
12
 
13
+ # Every workspace role is allowed the same baseline read capabilities. Keep this
14
+ # explicit so new UI read endpoints do not fail closed merely because a role's
15
+ # mutation scopes differ.
16
+ STUDIO_BASELINE_READ_SCOPES = frozenset({
17
+ "templates:read",
18
+ "operations:read",
19
+ "jobs:read",
20
+ "assets:read",
21
+ "mcp:read",
22
+ "system:read",
23
+ "social:accounts:read",
24
+ "social:posts:read",
25
+ "social:schedules:read",
26
+ "social:analytics:read",
27
+ "analytics:read",
28
+ "generation:providers:read",
29
+ "generation:requests:read",
30
+ "ai:read",
31
+ "copilot:read",
32
+ "projects:read",
33
+ "members:read",
34
+ "teams:read",
35
+ "comments:read",
36
+ "approvals:read",
37
+ })
38
+
39
  _HEX_SHA256 = re.compile(r"^[a-f0-9]{64}$")
40
 
41
 
 
105
  effective_role = role_map.get(raw_role)
106
  if effective_role is None:
107
  raise ValueError("Unsupported workspace membership role.")
108
+ role_scopes = DEFAULT_ROLE_SCOPES[effective_role] | STUDIO_BASELINE_READ_SCOPES
109
  return replace(
110
  context,
111
  role=effective_role,
112
+ scopes=frozenset(context.scopes.intersection(role_scopes)),
113
  workspace_id=principal.workspace_id,
114
  user_id=principal.user_id,
115
  membership_id=principal.membership_id,