Skip to content

Commit 89b8409

Browse files
csrbarberclaude
andauthored
Add authorization organization roles support (#549)
* Add authorization organization roles support Add CRUD operations for organization roles including create, list, get, update, set/add/remove permissions on the authorization module. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * format * Add authorization environment roles support (#550) * Add authorization environment roles support Add CRUD operations for environment roles including create, list, get, update, set/add permissions on the authorization module. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Use Role union type for list/get organization role endpoints The list and get organization role endpoints can return both EnvironmentRole and OrganizationRole types. This aligns the Python SDK return types with the Node SDK. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add authorization event and webhook types (#551) * Add authorization event and webhook types Add event and webhook types for organization_role (created, updated, deleted) and permission (created, updated, deleted) to support authorization-related event streaming and webhook delivery. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Distinct type for organization role events * mypy --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 30cdb9a commit 89b8409

File tree

12 files changed

+962
-1
lines changed

12 files changed

+962
-1
lines changed

tests/test_authorization.py

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from typing import Union
22

33
import pytest
4+
from tests.utils.fixtures.mock_environment_role import MockEnvironmentRole
5+
from tests.utils.fixtures.mock_organization_role import MockOrganizationRole
46
from tests.utils.fixtures.mock_permission import MockPermission
57
from tests.utils.list_resource import list_response_of
68
from tests.utils.syncify import syncify
@@ -170,3 +172,278 @@ def test_delete_permission(self, capture_and_mock_http_client_request):
170172
assert request_kwargs["url"].endswith(
171173
"/authorization/permissions/documents:read"
172174
)
175+
176+
# --- Organization Role fixtures ---
177+
178+
@pytest.fixture
179+
def mock_organization_role(self):
180+
return MockOrganizationRole(id="role_01ABC").dict()
181+
182+
@pytest.fixture
183+
def mock_organization_roles(self):
184+
return {
185+
"data": [MockOrganizationRole(id=f"role_{i}").dict() for i in range(5)],
186+
"object": "list",
187+
}
188+
189+
# --- Organization Role tests ---
190+
191+
def test_create_organization_role(
192+
self, mock_organization_role, capture_and_mock_http_client_request
193+
):
194+
request_kwargs = capture_and_mock_http_client_request(
195+
self.http_client, mock_organization_role, 201
196+
)
197+
198+
role = syncify(
199+
self.authorization.create_organization_role(
200+
"org_01EHT88Z8J8795GZNQ4ZP1J81T",
201+
slug="admin",
202+
name="Admin",
203+
)
204+
)
205+
206+
assert role.id == "role_01ABC"
207+
assert role.type == "OrganizationRole"
208+
assert request_kwargs["method"] == "post"
209+
assert request_kwargs["url"].endswith(
210+
"/authorization/organizations/org_01EHT88Z8J8795GZNQ4ZP1J81T/roles"
211+
)
212+
assert request_kwargs["json"] == {"slug": "admin", "name": "Admin"}
213+
214+
def test_list_organization_roles(
215+
self, mock_organization_roles, capture_and_mock_http_client_request
216+
):
217+
request_kwargs = capture_and_mock_http_client_request(
218+
self.http_client, mock_organization_roles, 200
219+
)
220+
221+
roles_response = syncify(
222+
self.authorization.list_organization_roles("org_01EHT88Z8J8795GZNQ4ZP1J81T")
223+
)
224+
225+
assert request_kwargs["method"] == "get"
226+
assert request_kwargs["url"].endswith(
227+
"/authorization/organizations/org_01EHT88Z8J8795GZNQ4ZP1J81T/roles"
228+
)
229+
assert len(roles_response.data) == 5
230+
231+
def test_get_organization_role(
232+
self, mock_organization_role, capture_and_mock_http_client_request
233+
):
234+
request_kwargs = capture_and_mock_http_client_request(
235+
self.http_client, mock_organization_role, 200
236+
)
237+
238+
role = syncify(
239+
self.authorization.get_organization_role(
240+
"org_01EHT88Z8J8795GZNQ4ZP1J81T", "admin"
241+
)
242+
)
243+
244+
assert role.id == "role_01ABC"
245+
assert request_kwargs["method"] == "get"
246+
assert request_kwargs["url"].endswith(
247+
"/authorization/organizations/org_01EHT88Z8J8795GZNQ4ZP1J81T/roles/admin"
248+
)
249+
250+
def test_update_organization_role(
251+
self, mock_organization_role, capture_and_mock_http_client_request
252+
):
253+
request_kwargs = capture_and_mock_http_client_request(
254+
self.http_client, mock_organization_role, 200
255+
)
256+
257+
role = syncify(
258+
self.authorization.update_organization_role(
259+
"org_01EHT88Z8J8795GZNQ4ZP1J81T",
260+
"admin",
261+
name="Super Admin",
262+
)
263+
)
264+
265+
assert role.id == "role_01ABC"
266+
assert request_kwargs["method"] == "patch"
267+
assert request_kwargs["url"].endswith(
268+
"/authorization/organizations/org_01EHT88Z8J8795GZNQ4ZP1J81T/roles/admin"
269+
)
270+
assert request_kwargs["json"] == {"name": "Super Admin"}
271+
272+
def test_set_organization_role_permissions(
273+
self, mock_organization_role, capture_and_mock_http_client_request
274+
):
275+
request_kwargs = capture_and_mock_http_client_request(
276+
self.http_client, mock_organization_role, 200
277+
)
278+
279+
role = syncify(
280+
self.authorization.set_organization_role_permissions(
281+
"org_01EHT88Z8J8795GZNQ4ZP1J81T",
282+
"admin",
283+
permissions=["documents:read", "documents:write"],
284+
)
285+
)
286+
287+
assert role.id == "role_01ABC"
288+
assert request_kwargs["method"] == "put"
289+
assert request_kwargs["url"].endswith(
290+
"/authorization/organizations/org_01EHT88Z8J8795GZNQ4ZP1J81T/roles/admin/permissions"
291+
)
292+
assert request_kwargs["json"] == {
293+
"permissions": ["documents:read", "documents:write"]
294+
}
295+
296+
def test_add_organization_role_permission(
297+
self, mock_organization_role, capture_and_mock_http_client_request
298+
):
299+
request_kwargs = capture_and_mock_http_client_request(
300+
self.http_client, mock_organization_role, 200
301+
)
302+
303+
role = syncify(
304+
self.authorization.add_organization_role_permission(
305+
"org_01EHT88Z8J8795GZNQ4ZP1J81T",
306+
"admin",
307+
permission_slug="documents:read",
308+
)
309+
)
310+
311+
assert role.id == "role_01ABC"
312+
assert request_kwargs["method"] == "post"
313+
assert request_kwargs["url"].endswith(
314+
"/authorization/organizations/org_01EHT88Z8J8795GZNQ4ZP1J81T/roles/admin/permissions"
315+
)
316+
assert request_kwargs["json"] == {"slug": "documents:read"}
317+
318+
def test_remove_organization_role_permission(
319+
self, capture_and_mock_http_client_request
320+
):
321+
request_kwargs = capture_and_mock_http_client_request(
322+
self.http_client,
323+
202,
324+
headers={"content-type": "text/plain; charset=utf-8"},
325+
)
326+
327+
response = syncify(
328+
self.authorization.remove_organization_role_permission(
329+
"org_01EHT88Z8J8795GZNQ4ZP1J81T",
330+
"admin",
331+
permission_slug="documents:read",
332+
)
333+
)
334+
335+
assert response is None
336+
assert request_kwargs["method"] == "delete"
337+
assert request_kwargs["url"].endswith(
338+
"/authorization/organizations/org_01EHT88Z8J8795GZNQ4ZP1J81T/roles/admin/permissions/documents:read"
339+
)
340+
341+
# --- Environment Role fixtures ---
342+
343+
@pytest.fixture
344+
def mock_environment_role(self):
345+
return MockEnvironmentRole(id="role_01DEF").dict()
346+
347+
@pytest.fixture
348+
def mock_environment_roles(self):
349+
return {
350+
"data": [MockEnvironmentRole(id=f"role_{i}").dict() for i in range(5)],
351+
"object": "list",
352+
}
353+
354+
# --- Environment Role tests ---
355+
356+
def test_create_environment_role(
357+
self, mock_environment_role, capture_and_mock_http_client_request
358+
):
359+
request_kwargs = capture_and_mock_http_client_request(
360+
self.http_client, mock_environment_role, 201
361+
)
362+
363+
role = syncify(
364+
self.authorization.create_environment_role(slug="member", name="Member")
365+
)
366+
367+
assert role.id == "role_01DEF"
368+
assert role.type == "EnvironmentRole"
369+
assert request_kwargs["method"] == "post"
370+
assert request_kwargs["url"].endswith("/authorization/roles")
371+
assert request_kwargs["json"] == {"slug": "member", "name": "Member"}
372+
373+
def test_list_environment_roles(
374+
self, mock_environment_roles, capture_and_mock_http_client_request
375+
):
376+
request_kwargs = capture_and_mock_http_client_request(
377+
self.http_client, mock_environment_roles, 200
378+
)
379+
380+
roles_response = syncify(self.authorization.list_environment_roles())
381+
382+
assert request_kwargs["method"] == "get"
383+
assert request_kwargs["url"].endswith("/authorization/roles")
384+
assert len(roles_response.data) == 5
385+
386+
def test_get_environment_role(
387+
self, mock_environment_role, capture_and_mock_http_client_request
388+
):
389+
request_kwargs = capture_and_mock_http_client_request(
390+
self.http_client, mock_environment_role, 200
391+
)
392+
393+
role = syncify(self.authorization.get_environment_role("member"))
394+
395+
assert role.id == "role_01DEF"
396+
assert request_kwargs["method"] == "get"
397+
assert request_kwargs["url"].endswith("/authorization/roles/member")
398+
399+
def test_update_environment_role(
400+
self, mock_environment_role, capture_and_mock_http_client_request
401+
):
402+
request_kwargs = capture_and_mock_http_client_request(
403+
self.http_client, mock_environment_role, 200
404+
)
405+
406+
role = syncify(
407+
self.authorization.update_environment_role("member", name="Updated Member")
408+
)
409+
410+
assert role.id == "role_01DEF"
411+
assert request_kwargs["method"] == "patch"
412+
assert request_kwargs["url"].endswith("/authorization/roles/member")
413+
assert request_kwargs["json"] == {"name": "Updated Member"}
414+
415+
def test_set_environment_role_permissions(
416+
self, mock_environment_role, capture_and_mock_http_client_request
417+
):
418+
request_kwargs = capture_and_mock_http_client_request(
419+
self.http_client, mock_environment_role, 200
420+
)
421+
422+
role = syncify(
423+
self.authorization.set_environment_role_permissions(
424+
"member", permissions=["documents:read"]
425+
)
426+
)
427+
428+
assert role.id == "role_01DEF"
429+
assert request_kwargs["method"] == "put"
430+
assert request_kwargs["url"].endswith("/authorization/roles/member/permissions")
431+
assert request_kwargs["json"] == {"permissions": ["documents:read"]}
432+
433+
def test_add_environment_role_permission(
434+
self, mock_environment_role, capture_and_mock_http_client_request
435+
):
436+
request_kwargs = capture_and_mock_http_client_request(
437+
self.http_client, mock_environment_role, 200
438+
)
439+
440+
role = syncify(
441+
self.authorization.add_environment_role_permission(
442+
"member", permission_slug="documents:read"
443+
)
444+
)
445+
446+
assert role.id == "role_01DEF"
447+
assert request_kwargs["method"] == "post"
448+
assert request_kwargs["url"].endswith("/authorization/roles/member/permissions")
449+
assert request_kwargs["json"] == {"slug": "documents:read"}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import datetime
2+
3+
from workos.types.authorization.environment_role import EnvironmentRole
4+
5+
6+
class MockEnvironmentRole(EnvironmentRole):
7+
def __init__(self, id: str):
8+
now = datetime.datetime.now().isoformat()
9+
super().__init__(
10+
object="role",
11+
id=id,
12+
name="Member",
13+
slug="member",
14+
description="Default environment member role",
15+
permissions=["documents:read"],
16+
type="EnvironmentRole",
17+
created_at=now,
18+
updated_at=now,
19+
)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import datetime
2+
3+
from workos.types.authorization.organization_role import OrganizationRole
4+
5+
6+
class MockOrganizationRole(OrganizationRole):
7+
def __init__(
8+
self,
9+
id: str,
10+
organization_id: str = "org_01EHT88Z8J8795GZNQ4ZP1J81T",
11+
):
12+
now = datetime.datetime.now().isoformat()
13+
super().__init__(
14+
object="role",
15+
id=id,
16+
organization_id=organization_id,
17+
name="Admin",
18+
slug="admin",
19+
description="Organization admin role",
20+
permissions=["documents:read", "documents:write"],
21+
type="OrganizationRole",
22+
created_at=now,
23+
updated_at=now,
24+
)

0 commit comments

Comments
 (0)