mirror of
https://github.com/graphql-hive/console
synced 2026-04-29 02:17:17 +00:00
489 lines
14 KiB
TypeScript
489 lines
14 KiB
TypeScript
import { graphql } from '../../../testkit/gql';
|
|
import * as GraphQLSchema from '../../../testkit/gql/graphql';
|
|
import { execute } from '../../../testkit/graphql';
|
|
import { initSeed } from '../../../testkit/seed';
|
|
import { deleteAccessToken, fetchPermissions } from './shared';
|
|
|
|
const CreateProjectAccessTokenMutation = graphql(`
|
|
mutation CreateProjectAccessTokenMutation($input: CreateProjectAccessTokenInput!) {
|
|
createProjectAccessToken(input: $input) {
|
|
ok {
|
|
privateAccessKey
|
|
createdProjectAccessToken {
|
|
id
|
|
title
|
|
description
|
|
createdAt
|
|
}
|
|
}
|
|
error {
|
|
message
|
|
details {
|
|
title
|
|
description
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`);
|
|
|
|
const SimpleProjectQuery = graphql(`
|
|
query ProjectAccessTokensProjectTestQuery($projectId: ID!) {
|
|
project(reference: { byId: $projectId }) {
|
|
id
|
|
slug
|
|
}
|
|
}
|
|
`);
|
|
|
|
test.concurrent('create: success with admin session', async ({ expect }) => {
|
|
const { createOrg, ownerToken } = await initSeed().createOwner();
|
|
const org = await createOrg();
|
|
const { project } = await org.createProject(GraphQLSchema.ProjectType.Federation);
|
|
|
|
const result = await execute({
|
|
document: CreateProjectAccessTokenMutation,
|
|
variables: {
|
|
input: {
|
|
project: {
|
|
byId: project.id,
|
|
},
|
|
title: 'an access token',
|
|
description: 'Some description',
|
|
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
|
|
permissions: [],
|
|
},
|
|
},
|
|
authToken: ownerToken,
|
|
}).then(e => e.expectNoGraphQLErrors());
|
|
expect(result.createProjectAccessToken.error).toEqual(null);
|
|
expect(result.createProjectAccessToken.ok).toEqual({
|
|
privateAccessKey: expect.any(String),
|
|
createdProjectAccessToken: {
|
|
id: expect.any(String),
|
|
title: 'an access token',
|
|
description: 'Some description',
|
|
createdAt: expect.any(String),
|
|
},
|
|
});
|
|
|
|
// no permissions :D
|
|
expect(await fetchPermissions(result.createProjectAccessToken.ok!.privateAccessKey)).toEqual([]);
|
|
});
|
|
|
|
test.concurrent('create: failure invalid title', async ({ expect }) => {
|
|
const { createOrg, ownerToken } = await initSeed().createOwner();
|
|
const org = await createOrg();
|
|
const { project } = await org.createProject(GraphQLSchema.ProjectType.Federation);
|
|
|
|
const result = await execute({
|
|
document: CreateProjectAccessTokenMutation,
|
|
variables: {
|
|
input: {
|
|
project: {
|
|
byId: project.id,
|
|
},
|
|
title: ' ',
|
|
description: 'Some description',
|
|
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
|
|
permissions: [],
|
|
},
|
|
},
|
|
authToken: ownerToken,
|
|
}).then(e => e.expectNoGraphQLErrors());
|
|
expect(result.createProjectAccessToken.ok).toEqual(null);
|
|
expect(result.createProjectAccessToken.error).toMatchInlineSnapshot(`
|
|
{
|
|
details: {
|
|
description: null,
|
|
title: Can only contain letters, numbers, " ", "_", and "-".,
|
|
},
|
|
message: Invalid input provided.,
|
|
}
|
|
`);
|
|
});
|
|
|
|
test.concurrent('create: failure invalid description', async ({ expect }) => {
|
|
const { createOrg, ownerToken } = await initSeed().createOwner();
|
|
const org = await createOrg();
|
|
const { project } = await org.createProject(GraphQLSchema.ProjectType.Federation);
|
|
|
|
const result = await execute({
|
|
document: CreateProjectAccessTokenMutation,
|
|
variables: {
|
|
input: {
|
|
project: {
|
|
byId: project.id,
|
|
},
|
|
title: 'an access token',
|
|
description: new Array(300).fill('A').join(''),
|
|
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
|
|
permissions: [],
|
|
},
|
|
},
|
|
authToken: ownerToken,
|
|
}).then(e => e.expectNoGraphQLErrors());
|
|
expect(result.createProjectAccessToken.ok).toEqual(null);
|
|
expect(result.createProjectAccessToken.error).toMatchInlineSnapshot(`
|
|
{
|
|
details: {
|
|
description: Maximum length is 248 characters.,
|
|
title: null,
|
|
},
|
|
message: Invalid input provided.,
|
|
}
|
|
`);
|
|
});
|
|
|
|
test.concurrent('create: failure because no access to project', async ({ expect }) => {
|
|
const actor1 = await initSeed().createOwner();
|
|
const actor2 = await initSeed().createOwner();
|
|
const org = await actor1.createOrg();
|
|
const { project } = await org.createProject(GraphQLSchema.ProjectType.Federation);
|
|
|
|
const errors = await execute({
|
|
document: CreateProjectAccessTokenMutation,
|
|
variables: {
|
|
input: {
|
|
project: {
|
|
byId: project.id,
|
|
},
|
|
title: 'an access token',
|
|
description: 'Some description',
|
|
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
|
|
permissions: [],
|
|
},
|
|
},
|
|
authToken: actor2.ownerToken,
|
|
}).then(e => e.expectGraphQLErrors());
|
|
expect(errors).toMatchObject([
|
|
{
|
|
extensions: {
|
|
code: 'UNAUTHORISED',
|
|
},
|
|
|
|
message: `No access (reason: "Missing permission for performing 'projectAccessToken:modify' on resource")`,
|
|
path: ['createProjectAccessToken'],
|
|
},
|
|
]);
|
|
});
|
|
|
|
test.concurrent(
|
|
'create: fail because of missing "projectAccessToken:modify" permission.',
|
|
async ({ expect }) => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const org = await createOrg();
|
|
const { member, createMemberRole, assignMemberRole, memberToken } =
|
|
await org.inviteAndJoinMember();
|
|
const memberRole = await createMemberRole(['organization:describe', 'project:describe']);
|
|
await assignMemberRole({
|
|
roleId: memberRole.id,
|
|
userId: member.id,
|
|
});
|
|
|
|
const { project } = await org.createProject(GraphQLSchema.ProjectType.Federation);
|
|
|
|
const errors = await execute({
|
|
document: CreateProjectAccessTokenMutation,
|
|
variables: {
|
|
input: {
|
|
project: {
|
|
byId: project.id,
|
|
},
|
|
title: 'an access token',
|
|
description: 'Some description',
|
|
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
|
|
permissions: [],
|
|
},
|
|
},
|
|
authToken: memberToken,
|
|
}).then(e => e.expectGraphQLErrors());
|
|
|
|
expect(errors).toMatchObject([
|
|
{
|
|
extensions: {
|
|
code: 'UNAUTHORISED',
|
|
},
|
|
message: `No access (reason: "Missing permission for performing 'projectAccessToken:modify' on resource")`,
|
|
path: ['createProjectAccessToken'],
|
|
},
|
|
]);
|
|
},
|
|
);
|
|
|
|
test.concurrent('query GraphQL API on resources with access', async ({ expect }) => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const org = await createOrg();
|
|
const {
|
|
member,
|
|
createMemberRole,
|
|
assignMemberRole,
|
|
memberToken: authToken,
|
|
} = await org.inviteAndJoinMember();
|
|
const memberRole = await createMemberRole([
|
|
'organization:describe',
|
|
'project:describe',
|
|
'projectAccessToken:modify',
|
|
]);
|
|
await assignMemberRole({
|
|
roleId: memberRole.id,
|
|
userId: member.id,
|
|
});
|
|
|
|
const { project } = await org.createProject(GraphQLSchema.ProjectType.Federation);
|
|
const { project: project1 } = await org.createProject(GraphQLSchema.ProjectType.Federation);
|
|
|
|
const result = await execute({
|
|
document: CreateProjectAccessTokenMutation,
|
|
variables: {
|
|
input: {
|
|
project: {
|
|
byId: project.id,
|
|
},
|
|
title: 'an access token',
|
|
description: 'a description',
|
|
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
|
|
permissions: ['project:describe'],
|
|
},
|
|
},
|
|
authToken,
|
|
}).then(e => e.expectNoGraphQLErrors());
|
|
expect(result.createProjectAccessToken.error).toEqual(null);
|
|
const organizationAccessToken = result.createProjectAccessToken.ok!.privateAccessKey;
|
|
|
|
expect(organizationAccessToken).toMatch(/^hvp1\//);
|
|
|
|
expect(await fetchPermissions(organizationAccessToken)).toEqual([
|
|
{
|
|
level: 'PROJECT',
|
|
resolvedPermissionGroups: [
|
|
{
|
|
permissions: [
|
|
{
|
|
permission: {
|
|
id: 'project:describe',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
resolvedResourceIds: [`${org.organization.slug}/${project.slug}`],
|
|
},
|
|
]);
|
|
|
|
const projectQuery = await execute({
|
|
document: SimpleProjectQuery,
|
|
variables: {
|
|
projectId: project.id,
|
|
},
|
|
authToken: organizationAccessToken,
|
|
}).then(e => e.expectNoGraphQLErrors());
|
|
expect(projectQuery).toEqual({
|
|
project: {
|
|
id: expect.any(String),
|
|
slug: project.slug,
|
|
},
|
|
});
|
|
|
|
const errors = await execute({
|
|
document: SimpleProjectQuery,
|
|
variables: {
|
|
projectId: project1.id,
|
|
},
|
|
authToken: organizationAccessToken,
|
|
}).then(e => e.expectGraphQLErrors());
|
|
expect(errors).toMatchObject([
|
|
{
|
|
extensions: {
|
|
code: 'UNAUTHORISED',
|
|
},
|
|
message: `No access (reason: "Missing permission for performing 'project:describe' on resource")`,
|
|
path: ['project'],
|
|
},
|
|
]);
|
|
});
|
|
|
|
test.concurrent('delete access token revokes access', async ({ expect }) => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const org = await createOrg();
|
|
const {
|
|
member,
|
|
createMemberRole,
|
|
assignMemberRole,
|
|
memberToken: authToken,
|
|
} = await org.inviteAndJoinMember();
|
|
const memberRole = await createMemberRole([
|
|
'organization:describe',
|
|
'project:describe',
|
|
'projectAccessToken:modify',
|
|
]);
|
|
await assignMemberRole({
|
|
roleId: memberRole.id,
|
|
userId: member.id,
|
|
});
|
|
|
|
const { project } = await org.createProject(GraphQLSchema.ProjectType.Federation);
|
|
|
|
const createProjectAccessTokenResult = await execute({
|
|
document: CreateProjectAccessTokenMutation,
|
|
variables: {
|
|
input: {
|
|
project: {
|
|
byId: project.id,
|
|
},
|
|
title: 'an access token',
|
|
description: 'a description',
|
|
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
|
|
permissions: ['project:describe'],
|
|
},
|
|
},
|
|
authToken,
|
|
}).then(e => e.expectNoGraphQLErrors());
|
|
expect(createProjectAccessTokenResult.createProjectAccessToken.error).toEqual(null);
|
|
const accessToken = createProjectAccessTokenResult.createProjectAccessToken.ok!;
|
|
|
|
expect(await fetchPermissions(accessToken.privateAccessKey)).toEqual([
|
|
{
|
|
level: 'PROJECT',
|
|
resolvedPermissionGroups: [
|
|
{
|
|
permissions: [
|
|
{
|
|
permission: {
|
|
id: 'project:describe',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
resolvedResourceIds: [`${org.organization.slug}/${project.slug}`],
|
|
},
|
|
]);
|
|
|
|
const deleteAccessTokenResult = await deleteAccessToken(
|
|
accessToken.createdProjectAccessToken.id,
|
|
authToken,
|
|
).then(res => res.expectNoGraphQLErrors());
|
|
expect(deleteAccessTokenResult).toEqual({
|
|
deleteAccessToken: {
|
|
error: null,
|
|
ok: {
|
|
deletedAccessTokenId: accessToken.createdProjectAccessToken.id,
|
|
},
|
|
},
|
|
});
|
|
|
|
const errors = await execute({
|
|
document: SimpleProjectQuery,
|
|
variables: {
|
|
projectId: project.id,
|
|
},
|
|
authToken: accessToken.privateAccessKey,
|
|
}).then(e => e.expectGraphQLErrors());
|
|
expect(errors).toMatchObject([
|
|
{
|
|
message: 'Invalid token provided',
|
|
},
|
|
]);
|
|
});
|
|
|
|
test.concurrent('cannot delete access token without sufficient permissions', async ({ expect }) => {
|
|
const { createOrg, ownerToken } = await initSeed().createOwner();
|
|
const org = await createOrg();
|
|
const { member, createMemberRole, assignMemberRole, memberToken } =
|
|
await org.inviteAndJoinMember();
|
|
const memberRole = await createMemberRole(['organization:describe', 'project:describe']);
|
|
await assignMemberRole({
|
|
roleId: memberRole.id,
|
|
userId: member.id,
|
|
});
|
|
|
|
const { project } = await org.createProject(GraphQLSchema.ProjectType.Federation);
|
|
|
|
const createProjectAccessTokenResult = await execute({
|
|
document: CreateProjectAccessTokenMutation,
|
|
variables: {
|
|
input: {
|
|
project: {
|
|
byId: project.id,
|
|
},
|
|
title: 'an access token',
|
|
description: 'a description',
|
|
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
|
|
permissions: ['project:describe'],
|
|
},
|
|
},
|
|
authToken: ownerToken,
|
|
}).then(e => e.expectNoGraphQLErrors());
|
|
|
|
expect(createProjectAccessTokenResult.createProjectAccessToken.error).toEqual(null);
|
|
const accessToken = createProjectAccessTokenResult.createProjectAccessToken.ok!;
|
|
|
|
const errors = await deleteAccessToken(
|
|
accessToken.createdProjectAccessToken.id,
|
|
memberToken,
|
|
).then(res => res.expectGraphQLErrors());
|
|
expect(errors).toMatchObject([
|
|
{
|
|
extensions: {
|
|
code: 'UNAUTHORISED',
|
|
},
|
|
|
|
message: `No access (reason: "Missing permission for performing 'projectAccessToken:modify' on resource")`,
|
|
path: ['deleteAccessToken'],
|
|
},
|
|
]);
|
|
});
|
|
|
|
test.concurrent(
|
|
'can delete access token without "accessToken:modify" permissions',
|
|
async ({ expect }) => {
|
|
const { createOrg, ownerToken } = await initSeed().createOwner();
|
|
const org = await createOrg();
|
|
const { member, createMemberRole, assignMemberRole, memberToken } =
|
|
await org.inviteAndJoinMember();
|
|
const memberRole = await createMemberRole([
|
|
'organization:describe',
|
|
'project:describe',
|
|
'accessToken:modify',
|
|
]);
|
|
await assignMemberRole({
|
|
roleId: memberRole.id,
|
|
userId: member.id,
|
|
});
|
|
|
|
const { project } = await org.createProject(GraphQLSchema.ProjectType.Federation);
|
|
|
|
const createProjectAccessTokenResult = await execute({
|
|
document: CreateProjectAccessTokenMutation,
|
|
variables: {
|
|
input: {
|
|
project: {
|
|
byId: project.id,
|
|
},
|
|
title: 'an access token',
|
|
description: 'a description',
|
|
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
|
|
permissions: ['project:describe'],
|
|
},
|
|
},
|
|
authToken: ownerToken,
|
|
}).then(e => e.expectNoGraphQLErrors());
|
|
|
|
expect(createProjectAccessTokenResult.createProjectAccessToken.error).toEqual(null);
|
|
const accessToken = createProjectAccessTokenResult.createProjectAccessToken.ok!;
|
|
|
|
const deleteAccessTokenResult = await deleteAccessToken(
|
|
accessToken.createdProjectAccessToken.id,
|
|
memberToken,
|
|
).then(res => res.expectNoGraphQLErrors());
|
|
expect(deleteAccessTokenResult).toEqual({
|
|
deleteAccessToken: {
|
|
error: null,
|
|
ok: {
|
|
deletedAccessTokenId: accessToken.createdProjectAccessToken.id,
|
|
},
|
|
},
|
|
});
|
|
},
|
|
);
|