Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 209 additions & 0 deletions src/authorization/authorization.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import {
import { WorkOS } from '../workos';
import environmentRoleFixture from './fixtures/environment-role.json';
import listEnvironmentRolesFixture from './fixtures/list-environment-roles.json';
import organizationRoleFixture from './fixtures/organization-role.json';
import listOrganizationRolesFixture from './fixtures/list-organization-roles.json';

const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
const testOrgId = 'org_01HXYZ123ABC456DEF789ABC';

describe('Authorization', () => {
beforeEach(() => fetch.resetMocks());
Expand Down Expand Up @@ -236,4 +239,210 @@ describe('Authorization', () => {
);
});
});

describe('createOrganizationRole', () => {
it('creates an organization role', async () => {
fetchOnce(organizationRoleFixture, { status: 201 });

const role = await workos.authorization.createOrganizationRole(
testOrgId,
{
slug: 'org-admin',
name: 'Org Admin',
description: 'Organization administrator',
},
);

expect(fetchURL()).toContain(
`/authorization/organizations/${testOrgId}/roles`,
);
expect(fetchBody()).toEqual({
slug: 'org-admin',
name: 'Org Admin',
description: 'Organization administrator',
});
expect(role).toMatchObject({
object: 'role',
id: 'role_01HXYZ123ABC456DEF789ORG',
slug: 'org-admin',
name: 'Org Admin',
type: 'OrganizationRole',
});
});
});

describe('listOrganizationRoles', () => {
it('returns both environment and organization roles', async () => {
fetchOnce(listOrganizationRolesFixture);

const { data, object } =
await workos.authorization.listOrganizationRoles(testOrgId);

expect(fetchURL()).toContain(
`/authorization/organizations/${testOrgId}/roles`,
);
expect(object).toEqual('list');
expect(data).toHaveLength(3);
expect(data).toEqual(
expect.arrayContaining([
expect.objectContaining({
slug: 'admin',
type: 'EnvironmentRole',
}),
expect.objectContaining({
slug: 'org-admin',
type: 'OrganizationRole',
}),
expect.objectContaining({
slug: 'org-member',
type: 'OrganizationRole',
}),
]),
);
});

it('passes expand parameter', async () => {
fetchOnce(listOrganizationRolesFixture);

await workos.authorization.listOrganizationRoles(testOrgId, {
expand: 'permissions',
});

expect(fetchSearchParams()).toEqual({
expand: 'permissions',
});
});
});

describe('getOrganizationRole', () => {
it('gets an organization role by slug', async () => {
fetchOnce(organizationRoleFixture);

const role = await workos.authorization.getOrganizationRole(
testOrgId,
'org-admin',
);

expect(fetchURL()).toContain(
`/authorization/organizations/${testOrgId}/roles/org-admin`,
);
expect(role).toMatchObject({
object: 'role',
slug: 'org-admin',
type: 'OrganizationRole',
});
});
});

describe('updateOrganizationRole', () => {
it('updates an organization role', async () => {
const updatedRoleFixture = {
...organizationRoleFixture,
name: 'Super Org Admin',
description: 'Updated description',
};
fetchOnce(updatedRoleFixture);

const role = await workos.authorization.updateOrganizationRole(
testOrgId,
'org-admin',
{
name: 'Super Org Admin',
description: 'Updated description',
},
);

expect(fetchURL()).toContain(
`/authorization/organizations/${testOrgId}/roles/org-admin`,
);
expect(fetchBody()).toEqual({
name: 'Super Org Admin',
description: 'Updated description',
});
expect(role).toMatchObject({
name: 'Super Org Admin',
description: 'Updated description',
});
});
});

describe('deleteOrganizationRole', () => {
it('deletes an organization role', async () => {
fetchOnce({}, { status: 204 });

await workos.authorization.deleteOrganizationRole(testOrgId, 'org-admin');

expect(fetchURL()).toContain(
`/authorization/organizations/${testOrgId}/roles/org-admin`,
);
});
});

describe('setOrganizationRolePermissions', () => {
it('sets permissions for an organization role', async () => {
const updatedRoleFixture = {
...organizationRoleFixture,
permissions: ['org:read', 'org:write'],
};
fetchOnce(updatedRoleFixture);

const role = await workos.authorization.setOrganizationRolePermissions(
testOrgId,
'org-admin',
['org:read', 'org:write'],
);

expect(fetchURL()).toContain(
`/authorization/organizations/${testOrgId}/roles/org-admin/permissions`,
);
expect(fetchBody()).toEqual({
permissions: ['org:read', 'org:write'],
});
expect(role.permissions).toEqual(
expect.arrayContaining(['org:read', 'org:write']),
);
});
});

describe('addOrganizationRolePermission', () => {
it('adds a permission to an organization role', async () => {
const updatedRoleFixture = {
...organizationRoleFixture,
permissions: ['org:manage', 'members:invite', 'billing:read'],
};
fetchOnce(updatedRoleFixture);

const role = await workos.authorization.addOrganizationRolePermission(
testOrgId,
'org-admin',
'billing:read',
);

expect(fetchURL()).toContain(
`/authorization/organizations/${testOrgId}/roles/org-admin/permissions`,
);
expect(fetchBody()).toEqual({
slug: 'billing:read',
});
expect(role.permissions).toEqual(
expect.arrayContaining(['billing:read']),
);
});
});

describe('removeOrganizationRolePermission', () => {
it('removes a permission from an organization role', async () => {
fetchOnce({}, { status: 200 });

await workos.authorization.removeOrganizationRolePermission(
testOrgId,
'org-admin',
'members:invite',
);

expect(fetchURL()).toContain(
`/authorization/organizations/${testOrgId}/roles/org-admin/permissions/members:invite`,
);
});
});
});
104 changes: 104 additions & 0 deletions src/authorization/authorization.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { WorkOS } from '../workos';
import {
Role,
RoleList,
OrganizationRoleResponse,
ListOrganizationRolesResponse,
} from '../roles/interfaces';
import {
EnvironmentRole,
EnvironmentRoleResponse,
Expand All @@ -7,11 +13,19 @@ import {
CreateEnvironmentRoleOptions,
UpdateEnvironmentRoleOptions,
ListEnvironmentRolesOptions,
OrganizationRole,
CreateOrganizationRoleOptions,
UpdateOrganizationRoleOptions,
ListOrganizationRolesOptions,
} from './interfaces';
import {
deserializeEnvironmentRole,
serializeCreateEnvironmentRoleOptions,
serializeUpdateEnvironmentRoleOptions,
deserializeRole,
deserializeOrganizationRole,
serializeCreateOrganizationRoleOptions,
serializeUpdateOrganizationRoleOptions,
} from './serializers';

export class Authorization {
Expand Down Expand Up @@ -79,4 +93,94 @@ export class Authorization {
);
return deserializeEnvironmentRole(data);
}

async createOrganizationRole(
organizationId: string,
options: CreateOrganizationRoleOptions,
): Promise<OrganizationRole> {
const { data } = await this.workos.post<OrganizationRoleResponse>(
`/authorization/organizations/${organizationId}/roles`,
serializeCreateOrganizationRoleOptions(options),
);
return deserializeOrganizationRole(data);
}

async listOrganizationRoles(
organizationId: string,
options?: ListOrganizationRolesOptions,
): Promise<RoleList> {
const { data } = await this.workos.get<ListOrganizationRolesResponse>(
`/authorization/organizations/${organizationId}/roles`,
{ query: options },
);
return {
object: 'list',
data: data.data.map(deserializeRole),
};
}

async getOrganizationRole(
organizationId: string,
slug: string,
): Promise<Role> {
const { data } = await this.workos.get<OrganizationRoleResponse>(
`/authorization/organizations/${organizationId}/roles/${slug}`,
);
return deserializeRole(data);
}

async updateOrganizationRole(
organizationId: string,
slug: string,
options: UpdateOrganizationRoleOptions,
): Promise<OrganizationRole> {
const { data } = await this.workos.patch<OrganizationRoleResponse>(
`/authorization/organizations/${organizationId}/roles/${slug}`,
serializeUpdateOrganizationRoleOptions(options),
);
return deserializeOrganizationRole(data);
}

async deleteOrganizationRole(
organizationId: string,
slug: string,
Comment on lines +145 to +146
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth disambiguating all of these orgId/slug parameters with an object. Similar comment from other PR, so good with matching whatever decision you make there.

): Promise<void> {
await this.workos.delete(
`/authorization/organizations/${organizationId}/roles/${slug}`,
);
}

async setOrganizationRolePermissions(
organizationId: string,
slug: string,
permissions: string[],
): Promise<OrganizationRole> {
const { data } = await this.workos.put<OrganizationRoleResponse>(
`/authorization/organizations/${organizationId}/roles/${slug}/permissions`,
{ permissions },
);
return deserializeOrganizationRole(data);
}

async addOrganizationRolePermission(
organizationId: string,
slug: string,
permissionSlug: string,
): Promise<OrganizationRole> {
const { data } = await this.workos.post<OrganizationRoleResponse>(
`/authorization/organizations/${organizationId}/roles/${slug}/permissions`,
{ slug: permissionSlug },
);
return deserializeOrganizationRole(data);
}

async removeOrganizationRolePermission(
organizationId: string,
slug: string,
permissionSlug: string,
): Promise<void> {
await this.workos.delete(
`/authorization/organizations/${organizationId}/roles/${slug}/permissions/${permissionSlug}`,
);
}
}
38 changes: 38 additions & 0 deletions src/authorization/fixtures/list-organization-roles.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"object": "list",
"data": [
{
"object": "role",
"id": "role_01HXYZ123ABC456DEF789ENV",
"name": "Admin",
"slug": "admin",
"description": "Environment-level admin role",
"permissions": ["users:read", "users:write"],
"type": "EnvironmentRole",
"created_at": "2024-01-15T08:00:00.000Z",
"updated_at": "2024-01-15T08:00:00.000Z"
},
{
"object": "role",
"id": "role_01HXYZ123ABC456DEF789ORG",
"name": "Org Admin",
"slug": "org-admin",
"description": "Organization administrator",
"permissions": ["org:manage", "members:invite"],
"type": "OrganizationRole",
"created_at": "2024-01-15T09:30:00.000Z",
"updated_at": "2024-01-15T09:30:00.000Z"
},
{
"object": "role",
"id": "role_01HXYZ123ABC456DEF789MEM",
"name": "Org Member",
"slug": "org-member",
"description": null,
"permissions": ["projects:read"],
"type": "OrganizationRole",
"created_at": "2024-01-15T10:00:00.000Z",
"updated_at": "2024-01-15T10:00:00.000Z"
}
]
}
11 changes: 11 additions & 0 deletions src/authorization/fixtures/organization-role.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"object": "role",
"id": "role_01HXYZ123ABC456DEF789ORG",
"name": "Org Admin",
"slug": "org-admin",
"description": "Organization administrator",
"permissions": ["org:manage", "members:invite"],
"type": "OrganizationRole",
"created_at": "2024-01-15T09:30:00.000Z",
"updated_at": "2024-01-15T09:30:00.000Z"
}
Loading