mirror of
https://github.com/graphql-hive/console
synced 2026-05-23 00:58:36 +00:00
210 lines
6.9 KiB
TypeScript
210 lines
6.9 KiB
TypeScript
import { OrganizationAccessScope, ProjectAccessScope, TargetAccessScope } from '@app/gql/graphql';
|
|
import { createOrganization, inviteToOrganization, joinOrganization, updateMemberAccess } from '../../../testkit/flow';
|
|
import { authenticate } from '../../../testkit/auth';
|
|
import { history } from '../../../testkit/emails';
|
|
|
|
test('owner of an organization should have all scopes', async () => {
|
|
const { access_token } = await authenticate('main');
|
|
const result = await createOrganization(
|
|
{
|
|
name: 'foo',
|
|
},
|
|
access_token
|
|
);
|
|
|
|
expect(result.body.errors).not.toBeDefined();
|
|
|
|
const owner = result.body.data!.createOrganization.ok!.createdOrganizationPayload.organization.owner;
|
|
|
|
Object.values(OrganizationAccessScope).forEach(scope => {
|
|
expect(owner.organizationAccessScopes).toContain(scope);
|
|
});
|
|
|
|
Object.values(ProjectAccessScope).forEach(scope => {
|
|
expect(owner.projectAccessScopes).toContain(scope);
|
|
});
|
|
|
|
Object.values(TargetAccessScope).forEach(scope => {
|
|
expect(owner.targetAccessScopes).toContain(scope);
|
|
});
|
|
});
|
|
|
|
test('regular member of an organization should have basic scopes', async () => {
|
|
const { access_token: owner_access_token } = await authenticate('main');
|
|
const orgResult = await createOrganization(
|
|
{
|
|
name: 'foo',
|
|
},
|
|
owner_access_token
|
|
);
|
|
|
|
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
|
|
|
// Invite
|
|
const invitationResult = await inviteToOrganization(
|
|
{
|
|
email: 'some@email.com',
|
|
organization: org.cleanId,
|
|
},
|
|
owner_access_token
|
|
);
|
|
|
|
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
|
expect(inviteCode).toBeDefined();
|
|
|
|
// Join
|
|
const { access_token: member_access_token } = await authenticate('extra');
|
|
const joinResult = await joinOrganization(inviteCode!, member_access_token);
|
|
|
|
expect(joinResult.body.errors).not.toBeDefined();
|
|
expect(joinResult.body.data?.joinOrganization.__typename).toBe('OrganizationPayload');
|
|
|
|
if (joinResult.body.data!.joinOrganization.__typename !== 'OrganizationPayload') {
|
|
throw new Error('Join failed');
|
|
}
|
|
|
|
const member = joinResult.body.data!.joinOrganization.organization.me;
|
|
|
|
// Should have only organization:read access
|
|
expect(member.organizationAccessScopes).toContainEqual(OrganizationAccessScope.Read);
|
|
// Nothing more
|
|
expect(member.organizationAccessScopes).toHaveLength(1);
|
|
|
|
// Should have only project:read and project:operations-store:read access
|
|
expect(member.projectAccessScopes).toContainEqual(ProjectAccessScope.Read);
|
|
expect(member.projectAccessScopes).toContainEqual(ProjectAccessScope.OperationsStoreRead);
|
|
// Nothing more
|
|
expect(member.projectAccessScopes).toHaveLength(2);
|
|
|
|
// Should have only target:read and target:registry:read access
|
|
expect(member.targetAccessScopes).toContainEqual(TargetAccessScope.Read);
|
|
expect(member.targetAccessScopes).toContainEqual(TargetAccessScope.RegistryRead);
|
|
// Nothing more
|
|
expect(member.targetAccessScopes).toHaveLength(2);
|
|
});
|
|
|
|
test('cannot grant an access scope to another user if user has no access to that scope', async () => {
|
|
const { access_token: owner_access_token } = await authenticate('main');
|
|
const orgResult = await createOrganization(
|
|
{
|
|
name: 'foo',
|
|
},
|
|
owner_access_token
|
|
);
|
|
|
|
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
|
|
|
// Join
|
|
const invitationResult = await inviteToOrganization(
|
|
{
|
|
email: 'some@email.com',
|
|
organization: org.cleanId,
|
|
},
|
|
owner_access_token
|
|
);
|
|
|
|
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
|
expect(inviteCode).toBeDefined();
|
|
|
|
const { access_token: member_access_token } = await authenticate('extra');
|
|
|
|
const joinResult = await joinOrganization(inviteCode!, member_access_token);
|
|
|
|
if (joinResult.body.data!.joinOrganization.__typename !== 'OrganizationPayload') {
|
|
throw new Error(`Join failed: ${joinResult.body.data!.joinOrganization.message}`);
|
|
}
|
|
|
|
const member = joinResult.body.data!.joinOrganization.organization.me;
|
|
|
|
// Grant organization:members access
|
|
await updateMemberAccess(
|
|
{
|
|
organization: org.cleanId,
|
|
organizationScopes: [OrganizationAccessScope.Members],
|
|
projectScopes: [],
|
|
targetScopes: [],
|
|
user: member.id,
|
|
},
|
|
owner_access_token
|
|
);
|
|
|
|
// Grant access to target:tokens:write
|
|
const accessResult = await updateMemberAccess(
|
|
{
|
|
organization: org.cleanId,
|
|
organizationScopes: [],
|
|
projectScopes: [],
|
|
targetScopes: [TargetAccessScope.TokensWrite],
|
|
user: member.id,
|
|
},
|
|
member_access_token
|
|
);
|
|
|
|
expect(accessResult.body.errors).toHaveLength(1);
|
|
expect(accessResult.body.errors![0].message).toMatch('target:tokens:write');
|
|
});
|
|
|
|
test('email invitation', async () => {
|
|
const { access_token: owner_access_token } = await authenticate('main');
|
|
const createOrgResult = await createOrganization(
|
|
{
|
|
name: 'foo',
|
|
},
|
|
owner_access_token
|
|
);
|
|
const org = createOrgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
|
|
|
// Invite
|
|
const email = 'invited@invited.com';
|
|
const invitationResult = await inviteToOrganization(
|
|
{
|
|
email,
|
|
organization: org.cleanId,
|
|
},
|
|
owner_access_token
|
|
);
|
|
|
|
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
|
expect(inviteCode).toBeDefined();
|
|
|
|
const sentEmails = await history();
|
|
expect(sentEmails).toContainEqual(expect.objectContaining({ to: email }));
|
|
});
|
|
|
|
test('cannot join organization twice using the same invitation code', async () => {
|
|
const { access_token: owner_access_token } = await authenticate('main');
|
|
const createOrgResult = await createOrganization(
|
|
{
|
|
name: 'foo',
|
|
},
|
|
owner_access_token
|
|
);
|
|
const org = createOrgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
|
|
|
// Invite
|
|
const invitationResult = await inviteToOrganization(
|
|
{
|
|
email: 'some@email.com',
|
|
organization: org.cleanId,
|
|
},
|
|
owner_access_token
|
|
);
|
|
|
|
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
|
expect(inviteCode).toBeDefined();
|
|
|
|
// Join
|
|
const { access_token: member_access_token } = await authenticate('extra');
|
|
const joinResult = await joinOrganization(inviteCode!, member_access_token);
|
|
|
|
expect(joinResult.body.errors).not.toBeDefined();
|
|
expect(joinResult.body.data?.joinOrganization.__typename).toBe('OrganizationPayload');
|
|
|
|
if (joinResult.body.data!.joinOrganization.__typename !== 'OrganizationPayload') {
|
|
throw new Error('Join failed');
|
|
}
|
|
|
|
const { access_token: another_access_token } = await authenticate('admin');
|
|
const secondJoinResult = await joinOrganization(inviteCode!, another_access_token);
|
|
expect(secondJoinResult.body.data?.joinOrganization.__typename).toBe('OrganizationInvitationError');
|
|
});
|