feat(server): expose fields for managing access tokens via the public API (#6710)

This commit is contained in:
Laurin Quast 2025-04-09 08:52:43 +02:00 committed by GitHub
parent a3f1ea9edd
commit 916e5799c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 267 additions and 226 deletions

View file

@ -843,7 +843,7 @@ export function initSeed() {
userId: input.userId,
roleId: input.roleId,
resources: input.resources ?? {
mode: GraphQLSchema.ResourceAssignmentMode.All,
mode: GraphQLSchema.ResourceAssignmentModeType.All,
projects: [],
},
},

View file

@ -86,7 +86,7 @@ test.concurrent('create: success', async () => {
},
title: 'a access token',
description: 'Some description',
resources: { mode: GraphQLSchema.ResourceAssignmentMode.All },
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: [],
},
},
@ -118,7 +118,7 @@ test.concurrent('create: failure invalid title', async ({ expect }) => {
},
title: ' ',
description: 'Some description',
resources: { mode: GraphQLSchema.ResourceAssignmentMode.All },
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: [],
},
},
@ -149,7 +149,7 @@ test.concurrent('create: failure invalid description', async ({ expect }) => {
},
title: 'a access token',
description: new Array(300).fill('A').join(''),
resources: { mode: GraphQLSchema.ResourceAssignmentMode.All },
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: [],
},
},
@ -181,7 +181,7 @@ test.concurrent('create: failure because no access to organization', async ({ ex
},
title: 'a access token',
description: 'Some description',
resources: { mode: GraphQLSchema.ResourceAssignmentMode.All },
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: [],
},
},
@ -213,7 +213,7 @@ test.concurrent('query GraphQL API on resources with access', async ({ expect })
},
title: 'a access token',
description: 'a description',
resources: { mode: GraphQLSchema.ResourceAssignmentMode.All },
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: ['organization:describe', 'project:describe'],
},
},
@ -263,11 +263,11 @@ test.concurrent('query GraphQL API on resources without access', async ({ expect
title: 'a access token',
description: 'a description',
resources: {
mode: GraphQLSchema.ResourceAssignmentMode.Granular,
mode: GraphQLSchema.ResourceAssignmentModeType.Granular,
projects: [
{
projectId: project1.project.id,
targets: { mode: GraphQLSchema.ResourceAssignmentMode.All },
targets: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
},
],
},
@ -327,7 +327,7 @@ test.concurrent('pagination', async ({ expect }) => {
title: 'first access token',
description: 'a description',
resources: {
mode: GraphQLSchema.ResourceAssignmentMode.All,
mode: GraphQLSchema.ResourceAssignmentModeType.All,
},
permissions: ['organization:describe'],
},
@ -345,7 +345,7 @@ test.concurrent('pagination', async ({ expect }) => {
title: 'second access token',
description: 'a description',
resources: {
mode: GraphQLSchema.ResourceAssignmentMode.All,
mode: GraphQLSchema.ResourceAssignmentModeType.All,
},
permissions: ['organization:describe'],
},

View file

@ -1,4 +1,4 @@
import { ProjectType, ResourceAssignmentMode } from 'testkit/gql/graphql';
import { ProjectType, ResourceAssignmentModeType } from 'testkit/gql/graphql';
import { updateProjectSlug } from '../../../testkit/flow';
import { initSeed } from '../../../testkit/seed';
@ -217,7 +217,7 @@ test.concurrent('prevent access to projects with assigned resources on member',
roleId: member.role.id,
userId: member.user.id,
resources: {
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
projects: [],
},
});
@ -246,11 +246,11 @@ test.concurrent('restrict access to single project with assigned resources on me
roleId: member.role.id,
userId: member.user.id,
resources: {
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
projects: [
{
projectId: firstProject.id,
targets: { mode: ResourceAssignmentMode.All },
targets: { mode: ResourceAssignmentModeType.All },
},
],
},

View file

@ -1,6 +1,6 @@
import {
ProjectType,
ResourceAssignmentMode,
ResourceAssignmentModeType,
RuleInstanceSeverityLevel,
} from 'testkit/gql/graphql';
// eslint-disable-next-line import/no-extraneous-dependencies
@ -1370,7 +1370,7 @@ test.concurrent(
await assignMemberRole({
roleId: member.role.id,
userId: member.user.id,
resources: { mode: ResourceAssignmentMode.Granular, projects: [] },
resources: { mode: ResourceAssignmentModeType.Granular, projects: [] },
});
// Attempt approving the failed schema check
@ -1448,7 +1448,7 @@ test.concurrent(
await assignMemberRole({
roleId: memberRole.id,
userId: member.user.id,
resources: { mode: ResourceAssignmentMode.Granular, projects: [] },
resources: { mode: ResourceAssignmentModeType.Granular, projects: [] },
});
// Attempt approving the failed schema check
@ -1529,12 +1529,12 @@ test.concurrent(
roleId: memberRole.id,
userId: member.user.id,
resources: {
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
projects: [
{
projectId: project.id,
targets: {
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
targets: [],
},
},
@ -1618,17 +1618,17 @@ test.concurrent(
roleId: memberRole.id,
userId: member.user.id,
resources: {
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
projects: [
{
projectId: project.id,
targets: {
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
targets: [
{
targetId: target.id,
appDeployments: { mode: ResourceAssignmentMode.All },
services: { mode: ResourceAssignmentMode.All },
appDeployments: { mode: ResourceAssignmentModeType.All },
services: { mode: ResourceAssignmentModeType.All },
},
],
},

View file

@ -770,7 +770,7 @@ test('schema:check without `--target` flag fails for organization access token',
const privateKey = await createOrganizationAccessToken({
permissions: ['schemaCheck:create', 'project:describe'],
resources: {
mode: GraphQLSchema.ResourceAssignmentMode.All,
mode: GraphQLSchema.ResourceAssignmentModeType.All,
},
});
@ -809,7 +809,7 @@ test('schema:check with `--target` flag succeeds for organization access token',
const privateKey = await createOrganizationAccessToken({
permissions: ['schemaCheck:create', 'project:describe'],
resources: {
mode: GraphQLSchema.ResourceAssignmentMode.All,
mode: GraphQLSchema.ResourceAssignmentModeType.All,
},
});
@ -841,7 +841,7 @@ test('schema:publish without `--target` flag fails for organization access token
const privateKey = await createOrganizationAccessToken({
permissions: ['project:describe', 'schemaVersion:publish'],
resources: {
mode: GraphQLSchema.ResourceAssignmentMode.All,
mode: GraphQLSchema.ResourceAssignmentModeType.All,
},
});
@ -881,7 +881,7 @@ test('schema:publish with `--target` flag succeeds for organization access token
const privateKey = await createOrganizationAccessToken({
permissions: ['project:describe', 'schemaVersion:publish'],
resources: {
mode: GraphQLSchema.ResourceAssignmentMode.All,
mode: GraphQLSchema.ResourceAssignmentModeType.All,
},
});

View file

@ -1,5 +1,5 @@
import { initSeed } from 'testkit/seed';
import { ResourceAssignmentMode } from '../../testkit/gql/graphql';
import { ResourceAssignmentModeType } from '../../testkit/gql/graphql';
import { getServiceHost } from '../../testkit/utils';
test('/:targetId > operation is accepted with wildcard access token', async () => {
@ -12,7 +12,7 @@ test('/:targetId > operation is accepted with wildcard access token', async () =
const accessToken = await createOrganizationAccessToken({
permissions: ['usage:report'],
resources: {
mode: ResourceAssignmentMode.All,
mode: ResourceAssignmentModeType.All,
},
});
@ -71,12 +71,12 @@ test('/:targetId > operation is denied without access to target', async () => {
const accessToken = await createOrganizationAccessToken({
permissions: ['usage:report'],
resources: {
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
projects: [
{
projectId: project.id,
targets: {
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
targets: [],
},
},
@ -133,22 +133,22 @@ test('/:targetId > operation is accepted with specific access to target', async
const accessToken = await createOrganizationAccessToken({
permissions: ['usage:report'],
resources: {
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
projects: [
{
projectId: project.id,
targets: {
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
targets: [
{
targetId: target.id,
services: {
services: [],
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
},
appDeployments: {
appDeployments: [],
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
},
},
],
@ -213,7 +213,7 @@ test('/:orgSlug/:projectSlug/:targetSlug > operation is accepted with wildcard a
const accessToken = await createOrganizationAccessToken({
permissions: ['usage:report'],
resources: {
mode: ResourceAssignmentMode.All,
mode: ResourceAssignmentModeType.All,
},
});
@ -275,12 +275,12 @@ test('/:orgSlug/:projectSlug/:targetSlug > operation is denied without access to
const accessToken = await createOrganizationAccessToken({
permissions: ['usage:report'],
resources: {
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
projects: [
{
projectId: project.id,
targets: {
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
targets: [],
},
},
@ -340,22 +340,22 @@ test('/:orgSlug/:projectSlug/:targetSlug > operation is accepted with specific a
const accessToken = await createOrganizationAccessToken({
permissions: ['usage:report'],
resources: {
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
projects: [
{
projectId: project.id,
targets: {
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
targets: [
{
targetId: target.id,
services: {
services: [],
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
},
appDeployments: {
appDeployments: [],
mode: ResourceAssignmentMode.Granular,
mode: ResourceAssignmentModeType.Granular,
},
},
],

View file

@ -5,10 +5,6 @@ export default gql`
me: User!
}
interface Error {
message: String!
}
extend type Mutation {
updateMe(input: UpdateMeInput!): UpdateMeResult!
}
@ -97,19 +93,19 @@ export default gql`
TOKENS_WRITE
}
enum PermissionLevel {
organization
project
target
service
appDeployment
enum PermissionLevelType {
ORGANIZATION
PROJECT
TARGET
SERVICE
APP_DEPLOYMENT
}
type Permission {
id: ID!
title: String!
description: String!
level: PermissionLevel!
level: PermissionLevelType!
dependsOnId: ID
isReadOnly: Boolean!
warning: String

View file

@ -1,4 +1,4 @@
import { getPermissionGroup } from '../lib/authz';
import { getPermissionGroup, type ResourceLevel } from '../lib/authz';
import type { PermissionResolvers } from './../../../__generated__/types';
/*
@ -18,9 +18,24 @@ export const Permission: PermissionResolvers = {
return permission.isReadOnly ?? false;
},
level: async (permission, _arg, _ctx) => {
return getPermissionGroup(permission.id);
return resourceLevelToResourceLevelType(getPermissionGroup(permission.id));
},
warning: async (permission, _arg, _ctx) => {
return permission.warning ?? null;
},
};
function resourceLevelToResourceLevelType(resourceLevel: ResourceLevel) {
switch (resourceLevel) {
case 'target':
return 'TARGET' as const;
case 'service':
return 'SERVICE' as const;
case 'project':
return 'PROJECT' as const;
case 'organization':
return 'ORGANIZATION' as const;
case 'appDeployment':
return 'APP_DEPLOYMENT' as const;
}
}

View file

@ -12,6 +12,17 @@ export const permissionGroups: Array<PermissionGroup> = [
},
],
},
{
id: 'access-tokens',
title: 'Access Tokens',
permissions: [
{
id: 'accessToken:modify',
title: 'Manage access tokens',
description: 'Fetch, create and delete access tokens.',
},
],
},
{
id: 'project',
title: 'Project',

View file

@ -41,11 +41,11 @@ export default gql`
deleteMemberRole(input: DeleteMemberRoleInput!): DeleteMemberRoleResult!
assignMemberRole(input: AssignMemberRoleInput!): AssignMemberRoleResult!
createOrganizationAccessToken(
input: CreateOrganizationAccessTokenInput!
): CreateOrganizationAccessTokenResult!
input: CreateOrganizationAccessTokenInput! @tag(name: "public")
): CreateOrganizationAccessTokenResult! @tag(name: "public")
deleteOrganizationAccessToken(
input: DeleteOrganizationAccessTokenInput!
): DeleteOrganizationAccessTokenResult!
input: DeleteOrganizationAccessTokenInput! @tag(name: "public")
): DeleteOrganizationAccessTokenResult! @tag(name: "public")
}
input OrganizationReferenceInput @oneOf {
@ -54,64 +54,68 @@ export default gql`
}
input CreateOrganizationAccessTokenInput {
organization: OrganizationReferenceInput!
title: String!
description: String
permissions: [String!]!
resources: ResourceAssignmentInput!
organization: OrganizationReferenceInput! @tag(name: "public")
title: String! @tag(name: "public")
description: String @tag(name: "public")
permissions: [String!]! @tag(name: "public")
resources: ResourceAssignmentInput! @tag(name: "public")
}
type CreateOrganizationAccessTokenResult {
ok: CreateOrganizationAccessTokenResultOk
error: CreateOrganizationAccessTokenResultError
ok: CreateOrganizationAccessTokenResultOk @tag(name: "public")
error: CreateOrganizationAccessTokenResultError @tag(name: "public")
}
type CreateOrganizationAccessTokenResultOk {
createdOrganizationAccessToken: OrganizationAccessToken!
privateAccessKey: String!
privateAccessKey: String! @tag(name: "public")
}
type CreateOrganizationAccessTokenResultError implements Error {
message: String!
details: CreateOrganizationAccessTokenResultErrorDetails
type CreateOrganizationAccessTokenResultError {
message: String! @tag(name: "public")
details: CreateOrganizationAccessTokenResultErrorDetails @tag(name: "public")
}
type CreateOrganizationAccessTokenResultErrorDetails {
"""
Error message for the input title.
"""
title: String
title: String @tag(name: "public")
"""
Error message for the input description.
"""
description: String
description: String @tag(name: "public")
}
type OrganizationAccessToken {
id: ID!
title: String!
description: String
permissions: [String!]!
resources: ResourceAssignment!
firstCharacters: String!
createdAt: DateTime!
id: ID! @tag(name: "public")
title: String! @tag(name: "public")
description: String @tag(name: "public")
permissions: [String!]! @tag(name: "public")
resources: ResourceAssignment! @tag(name: "public")
firstCharacters: String! @tag(name: "public")
createdAt: DateTime! @tag(name: "public")
}
input DeleteOrganizationAccessTokenInput {
organizationAccessTokenId: ID!
organizationAccessToken: OrganizationAccessTokenReference! @tag(name: "public")
}
input OrganizationAccessTokenReference @oneOf @tag(name: "public") {
byId: ID @tag(name: "public")
}
type DeleteOrganizationAccessTokenResult {
ok: DeleteOrganizationAccessTokenResultOk
error: DeleteOrganizationAccessTokenResultError
ok: DeleteOrganizationAccessTokenResultOk @tag(name: "public")
error: DeleteOrganizationAccessTokenResultError @tag(name: "public")
}
type DeleteOrganizationAccessTokenResultOk {
deletedOrganizationAccessTokenId: ID!
deletedOrganizationAccessTokenId: ID! @tag(name: "public")
}
type DeleteOrganizationAccessTokenResultError implements Error {
message: String!
type DeleteOrganizationAccessTokenResultError {
message: String! @tag(name: "public")
}
type UpdateOrganizationSlugResult {
@ -338,21 +342,24 @@ export default gql`
"""
Paginated organization access tokens.
"""
accessTokens(first: Int, after: String): OrganizationAccessTokenConnection!
accessTokens(
first: Int @tag(name: "public")
after: String @tag(name: "public")
): OrganizationAccessTokenConnection! @tag(name: "public")
"""
Get organization access token by id.
"""
accessToken(id: ID!): OrganizationAccessToken
accessToken(id: ID! @tag(name: "public")): OrganizationAccessToken @tag(name: "public")
}
type OrganizationAccessTokenEdge {
node: OrganizationAccessToken!
cursor: String!
node: OrganizationAccessToken! @tag(name: "public")
cursor: String! @tag(name: "public")
}
type OrganizationAccessTokenConnection {
pageInfo: PageInfo!
edges: [OrganizationAccessTokenEdge!]!
pageInfo: PageInfo! @tag(name: "public")
edges: [OrganizationAccessTokenEdge!]! @tag(name: "public")
}
type OrganizationConnection {
@ -566,9 +573,15 @@ export default gql`
viewerCanRemove: Boolean!
}
enum ResourceAssignmentMode {
all
granular
enum ResourceAssignmentModeType {
"""
Apply to all subresouces of the resource.
"""
ALL @tag(name: "public")
"""
Apply to specific subresouces of the resource.
"""
GRANULAR @tag(name: "public")
}
type MemberConnection {
@ -577,98 +590,98 @@ export default gql`
}
input AppDeploymentResourceAssignmentInput {
appDeployment: String!
appDeployment: String! @tag(name: "public")
}
input TargetAppDeploymentsResourceAssignmentInput {
"""
Whether the permissions should apply for all app deployments within the target.
"""
mode: ResourceAssignmentMode!
mode: ResourceAssignmentModeType! @tag(name: "public")
"""
Specific app deployments within the target for which the permissions should be applied.
"""
appDeployments: [AppDeploymentResourceAssignmentInput!]
appDeployments: [AppDeploymentResourceAssignmentInput!] @tag(name: "public")
}
input ServiceResourceAssignmentInput {
serviceName: String!
serviceName: String! @tag(name: "public")
}
input TargetServicesResourceAssignmentInput {
"""
Whether the permissions should apply for all services within the target or only selected ones.
"""
mode: ResourceAssignmentMode!
mode: ResourceAssignmentModeType! @tag(name: "public")
"""
Specific services within the target for which the permissions should be applied.
"""
services: [ServiceResourceAssignmentInput!]
services: [ServiceResourceAssignmentInput!] @tag(name: "public")
}
input TargetResourceAssignmentInput {
targetId: ID!
services: TargetServicesResourceAssignmentInput!
appDeployments: TargetAppDeploymentsResourceAssignmentInput!
targetId: ID! @tag(name: "public")
services: TargetServicesResourceAssignmentInput! @tag(name: "public")
appDeployments: TargetAppDeploymentsResourceAssignmentInput! @tag(name: "public")
}
input ProjectTargetsResourceAssignmentInput {
"""
Whether the permissions should apply for all targets within the project or only selected ones.
"""
mode: ResourceAssignmentMode!
mode: ResourceAssignmentModeType! @tag(name: "public")
"""
Specific targets within the projects for which the permissions should be applied.
"""
targets: [TargetResourceAssignmentInput!]
targets: [TargetResourceAssignmentInput!] @tag(name: "public")
}
input ProjectResourceAssignmentInput {
projectId: ID!
targets: ProjectTargetsResourceAssignmentInput!
projectId: ID! @tag(name: "public")
targets: ProjectTargetsResourceAssignmentInput! @tag(name: "public")
}
input ResourceAssignmentInput {
"""
Whether the permissions should apply for all projects within the organization or only selected ones.
"""
mode: ResourceAssignmentMode!
mode: ResourceAssignmentModeType! @tag(name: "public")
"""
Specific projects within the organization for which the permissions should be applied.
"""
projects: [ProjectResourceAssignmentInput!]
projects: [ProjectResourceAssignmentInput!] @tag(name: "public")
}
type TargetServicesResourceAssignment {
mode: ResourceAssignmentMode!
services: [String!]
mode: ResourceAssignmentModeType! @tag(name: "public")
services: [String!] @tag(name: "public")
}
type TargetAppDeploymentsResourceAssignment {
mode: ResourceAssignmentMode!
appDeployments: [String!]
mode: ResourceAssignmentModeType! @tag(name: "public")
appDeployments: [String!] @tag(name: "public")
}
type TargetResouceAssignment {
targetId: ID!
target: Target!
services: TargetServicesResourceAssignment!
appDeployments: TargetAppDeploymentsResourceAssignment!
targetId: ID! @tag(name: "public")
target: Target! @tag(name: "public")
services: TargetServicesResourceAssignment! @tag(name: "public")
appDeployments: TargetAppDeploymentsResourceAssignment! @tag(name: "public")
}
type ProjectTargetsResourceAssignment {
mode: ResourceAssignmentMode!
targets: [TargetResouceAssignment!]
mode: ResourceAssignmentModeType! @tag(name: "public")
targets: [TargetResouceAssignment!] @tag(name: "public")
}
type ProjectResourceAssignment {
projectId: ID!
project: Project!
targets: ProjectTargetsResourceAssignment!
projectId: ID! @tag(name: "public")
project: Project! @tag(name: "public")
targets: ProjectTargetsResourceAssignment! @tag(name: "public")
}
type ResourceAssignment {
mode: ResourceAssignmentMode!
projects: [ProjectResourceAssignment!]
mode: ResourceAssignmentModeType! @tag(name: "public")
projects: [ProjectResourceAssignment!] @tag(name: "public")
}
`;

View file

@ -137,7 +137,7 @@ export class OrganizationAccessTokens {
const assignedResources =
await this.resourceAssignments.transformGraphQLResourceAssignmentInputToResourceAssignmentGroup(
organizationId,
args.assignedResources ?? { mode: 'granular' },
args.assignedResources ?? { mode: 'GRANULAR' },
);
const permissions = Array.from(

View file

@ -36,7 +36,7 @@ export class ResourceAssignments {
resources: ResourceAssignmentGroup;
}): Promise<GraphQLSchema.ResolversTypes['ResourceAssignment']> {
if (args.resources.mode === '*') {
return { mode: 'all' };
return { mode: 'ALL' };
}
const projects = await this.storage.findProjectsByIds({
projectIds: args.resources.projects.map(project => project.id),
@ -54,7 +54,7 @@ export class ResourceAssignments {
});
return {
mode: 'granular' as const,
mode: 'GRANULAR' as const,
projects: filteredProjects
.map(projectAssignment => {
const project = projects.get(projectAssignment.id);
@ -67,9 +67,9 @@ export class ResourceAssignments {
project,
targets:
projectAssignment.targets.mode === '*'
? { mode: 'all' as const }
? { mode: 'ALL' as const }
: {
mode: 'granular' as const,
mode: 'GRANULAR' as const,
targets: projectAssignment.targets.targets
.map(targetAssignment => {
const target = targets.get(targetAssignment.id);
@ -80,18 +80,18 @@ export class ResourceAssignments {
target,
services:
targetAssignment.services.mode === '*'
? { mode: 'all' as const }
? { mode: 'ALL' as const }
: {
mode: 'granular' as const,
mode: 'GRANULAR' as const,
services: targetAssignment.services.services.map(
service => service.serviceName,
),
},
appDeployments:
targetAssignment.appDeployments.mode === '*'
? { mode: 'all' as const }
? { mode: 'ALL' as const }
: {
mode: 'granular' as const,
mode: 'GRANULAR' as const,
appDeployments:
targetAssignment.appDeployments.appDeployments.map(
deployment => deployment.appName,
@ -124,7 +124,7 @@ export class ResourceAssignments {
!input.projects ||
// No need to resolve the projects if mode "all" is used.
// We will not store the selection in the database.
input.mode === 'all'
input.mode === 'ALL'
) {
return {
mode: '*',
@ -171,14 +171,14 @@ export class ResourceAssignments {
type: 'project',
id: project.id,
targets: {
mode: record.targets.mode === 'all' ? '*' : 'granular',
mode: record.targets.mode === 'ALL' ? '*' : 'granular',
targets: projectTargets,
},
});
// No need to resolve the projects if mode "a;ll" is used.
// We will not store the selection in the database.
if (record.targets.mode === 'all') {
if (record.targets.mode === 'ALL') {
continue;
}
@ -217,7 +217,7 @@ export class ResourceAssignments {
services:
// monolith schemas do not have services.
record.project.type === GraphQLSchema.ProjectType.SINGLE ||
targetRecord.services.mode === 'all'
targetRecord.services.mode === 'ALL'
? { mode: '*' }
: {
mode: 'granular',
@ -229,7 +229,7 @@ export class ResourceAssignments {
})) ?? [],
},
appDeployments:
targetRecord.appDeployments.mode === 'all'
targetRecord.appDeployments.mode === 'ALL'
? { mode: '*' }
: {
mode: 'granular',

View file

@ -5,7 +5,7 @@ export const deleteOrganizationAccessToken: NonNullable<
MutationResolvers['deleteOrganizationAccessToken']
> = async (_parent, args, { injector }) => {
const result = await injector.get(OrganizationAccessTokens).delete({
organizationAccessTokenId: args.input.organizationAccessTokenId,
organizationAccessTokenId: args.input.organizationAccessToken.byId,
});
return {

View file

@ -52,4 +52,8 @@ export default gql`
startCursor: String! @tag(name: "public")
endCursor: String! @tag(name: "public")
}
interface Error {
message: String!
}
`;

View file

@ -252,7 +252,7 @@ function MemberRole(props: {
return (
<>
{member.role.name}
{member.resourceAssignment.mode === GraphQLSchema.ResourceAssignmentMode.All ? (
{member.resourceAssignment.mode === GraphQLSchema.ResourceAssignmentModeType.All ? (
' (all resources)'
) : member.resourceAssignment.projects?.length ? (
<>

View file

@ -145,7 +145,7 @@ export function ResourceSelector(props: {
const [serviceAppsState, setServiceAppsState] = useState(ServicesAppsState.service);
const projectState = useMemo(() => {
if (props.selection.mode === GraphQLSchema.ResourceAssignmentMode.All) {
if (props.selection.mode === GraphQLSchema.ResourceAssignmentModeType.All) {
return null;
}
@ -190,7 +190,7 @@ export function ResourceSelector(props: {
projectId: item.id,
projectSlug: item.slug,
targets: {
mode: GraphQLSchema.ResourceAssignmentMode.Granular,
mode: GraphQLSchema.ResourceAssignmentModeType.Granular,
targets: [],
},
});
@ -235,7 +235,7 @@ export function ResourceSelector(props: {
if (
projectState.activeProject.projectSelection.targets.mode ===
GraphQLSchema.ResourceAssignmentMode.All
GraphQLSchema.ResourceAssignmentModeType.All
) {
return {
selection: '*',
@ -244,7 +244,7 @@ export function ResourceSelector(props: {
produce(props.selection, state => {
const project = state.projects?.find(project => project.projectId === projectId);
if (!project) return;
project.targets.mode = GraphQLSchema.ResourceAssignmentMode.Granular;
project.targets.mode = GraphQLSchema.ResourceAssignmentModeType.Granular;
}),
);
},
@ -311,15 +311,15 @@ export function ResourceSelector(props: {
targetId: item.id,
targetSlug: item.slug,
appDeployments: {
mode: GraphQLSchema.ResourceAssignmentMode.Granular,
mode: GraphQLSchema.ResourceAssignmentModeType.Granular,
appDeployments: [],
},
services: {
mode:
// for single projects we choose "All" by default as there is no granular selection available
projectType === GraphQLSchema.ProjectType.Single
? GraphQLSchema.ResourceAssignmentMode.All
: GraphQLSchema.ResourceAssignmentMode.Granular,
? GraphQLSchema.ResourceAssignmentModeType.All
: GraphQLSchema.ResourceAssignmentModeType.Granular,
services: [],
},
});
@ -353,7 +353,7 @@ export function ResourceSelector(props: {
produce(props.selection, state => {
const project = state.projects?.find(project => project.projectId === projectId);
if (!project) return;
project.targets.mode = GraphQLSchema.ResourceAssignmentMode.All;
project.targets.mode = GraphQLSchema.ResourceAssignmentModeType.All;
}),
);
setBreadcrumb({ projectId });
@ -397,7 +397,7 @@ export function ResourceSelector(props: {
if (
targetState.activeTarget.targetSelection.services.mode ===
GraphQLSchema.ResourceAssignmentMode.All
GraphQLSchema.ResourceAssignmentModeType.All
) {
return {
selection: '*' as const,
@ -408,7 +408,7 @@ export function ResourceSelector(props: {
if (!project) return;
const target = project.targets.targets?.find(target => target.targetId === targetId);
if (!target) return;
target.services.mode = GraphQLSchema.ResourceAssignmentMode.Granular;
target.services.mode = GraphQLSchema.ResourceAssignmentModeType.Granular;
}),
);
},
@ -449,7 +449,7 @@ export function ResourceSelector(props: {
const target = project.targets.targets?.find(target => target.targetId === targetId);
if (!target) return;
target.services.mode = GraphQLSchema.ResourceAssignmentMode.All;
target.services.mode = GraphQLSchema.ResourceAssignmentModeType.All;
}),
);
},
@ -512,7 +512,7 @@ export function ResourceSelector(props: {
if (
targetState.activeTarget.targetSelection.services.mode ===
GraphQLSchema.ResourceAssignmentMode.All
GraphQLSchema.ResourceAssignmentModeType.All
) {
return {
selection: '*' as const,
@ -523,7 +523,7 @@ export function ResourceSelector(props: {
if (!project) return;
const target = project.targets.targets?.find(target => target.targetId === targetId);
if (!target) return;
target.appDeployments.mode = GraphQLSchema.ResourceAssignmentMode.Granular;
target.appDeployments.mode = GraphQLSchema.ResourceAssignmentModeType.Granular;
}),
);
},
@ -549,7 +549,7 @@ export function ResourceSelector(props: {
const target = project.targets.targets?.find(target => target.targetId === targetId);
if (!target) return;
target.appDeployments.mode = GraphQLSchema.ResourceAssignmentMode.All;
target.appDeployments.mode = GraphQLSchema.ResourceAssignmentModeType.All;
}),
);
},
@ -603,7 +603,7 @@ export function ResourceSelector(props: {
<Tabs
defaultValue="granular"
value={
props.selection.mode === GraphQLSchema.ResourceAssignmentMode.All ? 'full' : 'granular'
props.selection.mode === GraphQLSchema.ResourceAssignmentModeType.All ? 'full' : 'granular'
}
>
<TabsList variant="content" className="mt-1">
@ -613,7 +613,7 @@ export function ResourceSelector(props: {
onClick={() => {
props.onSelectionChange({
...props.selection,
mode: GraphQLSchema.ResourceAssignmentMode.All,
mode: GraphQLSchema.ResourceAssignmentModeType.All,
});
setBreadcrumb(null);
}}
@ -626,7 +626,7 @@ export function ResourceSelector(props: {
onClick={() => {
props.onSelectionChange({
...props.selection,
mode: GraphQLSchema.ResourceAssignmentMode.Granular,
mode: GraphQLSchema.ResourceAssignmentModeType.Granular,
});
}}
>
@ -744,7 +744,7 @@ export function ResourceSelector(props: {
title={
selection.project.slug +
(selection.projectSelection.targets.mode ===
GraphQLSchema.ResourceAssignmentMode.All
GraphQLSchema.ResourceAssignmentModeType.All
? ' (all targets, all services)'
: ` (${selection.projectSelection.targets.targets?.length ?? 0} target${selection.projectSelection.targets.targets?.length === 1 ? '' : 's'})`)
}
@ -802,7 +802,7 @@ export function ResourceSelector(props: {
GraphQLSchema.ProjectType.Single
? ' (full access)'
: selection.targetSelection.services.mode ===
GraphQLSchema.ResourceAssignmentMode.All
GraphQLSchema.ResourceAssignmentModeType.All
? ' (all services)'
: ` (${selection.targetSelection.services.services?.length ?? 0} service${selection.targetSelection.services?.services?.length === 1 ? '' : 's'})`)
}
@ -850,7 +850,7 @@ export function ResourceSelector(props: {
{serviceAppsState === ServicesAppsState.service && (
<div className="flex flex-1 flex-col border-y border-r pt-2">
{projectState.activeProject?.projectSelection.targets.mode ===
GraphQLSchema.ResourceAssignmentMode.All ? (
GraphQLSchema.ResourceAssignmentModeType.All ? (
<div className="text-muted-foreground px-2 text-xs">
Access to all services of projects targets granted.
</div>
@ -927,7 +927,7 @@ export function ResourceSelector(props: {
{serviceAppsState === ServicesAppsState.apps && (
<div className="flex flex-1 flex-col border-y border-r pt-2">
{projectState.activeProject?.projectSelection.targets.mode ===
GraphQLSchema.ResourceAssignmentMode.All ? (
GraphQLSchema.ResourceAssignmentModeType.All ? (
<div className="text-muted-foreground px-2 text-xs">
Access to all apps of projects targets granted.
</div>

View file

@ -8,7 +8,7 @@ import {
import { Badge } from '@/components/ui/badge';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { FragmentType, graphql, useFragment } from '@/gql';
import { PermissionLevel } from '@/gql/graphql';
import { PermissionLevelType } from '@/gql/graphql';
import { ResultOf } from '@graphql-typed-document-node/core';
export const SelectedPermissionOverview_PermissionGroupFragment = graphql(`
@ -34,7 +34,7 @@ export type SelectedPermissionOverviewProps = {
/** default: true */
isExpanded?: boolean;
/** option for injecting additional content within a permission group. */
additionalGroupContent?: (group: { level: PermissionLevel }) => React.ReactNode;
additionalGroupContent?: (group: { level: PermissionLevelType }) => React.ReactNode;
};
export function SelectedPermissionOverview(props: SelectedPermissionOverviewProps) {
@ -49,23 +49,23 @@ export function SelectedPermissionOverview(props: SelectedPermissionOverviewProp
return [
{
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'Organization',
},
{
level: PermissionLevel.Project,
level: PermissionLevelType.Project,
title: 'Project',
},
{
level: PermissionLevel.Target,
level: PermissionLevelType.Target,
title: 'Target',
},
{
level: PermissionLevel.Service,
level: PermissionLevelType.Service,
title: 'Service',
},
{
level: PermissionLevel.AppDeployment,
level: PermissionLevelType.AppDeployment,
title: 'App Deployment',
},
].map(group => (
@ -90,7 +90,7 @@ type MembershipPermissionGroup = AvailableMembershipPermissions[number];
function PermissionLevelGroup(props: {
title: string;
permissionLevel: PermissionLevel;
permissionLevel: PermissionLevelType;
memberPermissionGroups: AvailableMembershipPermissions;
activePermissionIds: ReadonlySet<string>;
/** whether only allowed permissions should be shown */

View file

@ -206,7 +206,9 @@ function DeleteAccessTokenConfirmationDialogue(props: DeleteAccessTokenConfirmat
onClick={() =>
mutate({
input: {
organizationAccessTokenId: props.accessTokenId,
organizationAccessToken: {
byId: props.accessTokenId,
},
},
}).then(result => {
if (result.error) {

View file

@ -116,7 +116,7 @@ export function CreateAccessTokenSheetContent(
props.organization,
);
const [resourceSelection, setResourceSelection] = useState<ResourceSelection>(() => ({
mode: GraphQLSchema.ResourceAssignmentMode.All,
mode: GraphQLSchema.ResourceAssignmentModeType.All,
projects: [],
}));

View file

@ -7,57 +7,57 @@ import type { ResourceSelection } from '../../members/resource-selector';
export function resolveResources(
organizationSlug: string,
resources: ResourceSelection,
): null | Record<GraphQLSchema.PermissionLevel, Array<string>> {
if (resources.mode === GraphQLSchema.ResourceAssignmentMode.All || !resources.projects) {
): null | Record<GraphQLSchema.PermissionLevelType, Array<string>> {
if (resources.mode === GraphQLSchema.ResourceAssignmentModeType.All || !resources.projects) {
return null;
}
const resolvedResources: Record<GraphQLSchema.PermissionLevel, Array<string>> = {
[GraphQLSchema.PermissionLevel.Organization]: [organizationSlug],
[GraphQLSchema.PermissionLevel.Project]: [],
[GraphQLSchema.PermissionLevel.Target]: [],
[GraphQLSchema.PermissionLevel.AppDeployment]: [],
[GraphQLSchema.PermissionLevel.Service]: [],
const resolvedResources: Record<GraphQLSchema.PermissionLevelType, Array<string>> = {
[GraphQLSchema.PermissionLevelType.Organization]: [organizationSlug],
[GraphQLSchema.PermissionLevelType.Project]: [],
[GraphQLSchema.PermissionLevelType.Target]: [],
[GraphQLSchema.PermissionLevelType.AppDeployment]: [],
[GraphQLSchema.PermissionLevelType.Service]: [],
};
for (const project of resources.projects) {
resolvedResources[GraphQLSchema.PermissionLevel.Project].push(
resolvedResources[GraphQLSchema.PermissionLevelType.Project].push(
`${organizationSlug}/${project.projectSlug}`,
);
if (project.targets.mode === GraphQLSchema.ResourceAssignmentMode.All) {
resolvedResources[GraphQLSchema.PermissionLevel.Target].push(
if (project.targets.mode === GraphQLSchema.ResourceAssignmentModeType.All) {
resolvedResources[GraphQLSchema.PermissionLevelType.Target].push(
`${organizationSlug}/${project.projectSlug}/*`,
);
resolvedResources[GraphQLSchema.PermissionLevel.Service].push(
resolvedResources[GraphQLSchema.PermissionLevelType.Service].push(
`${organizationSlug}/${project.projectSlug}/*/service/*`,
);
resolvedResources[GraphQLSchema.PermissionLevel.AppDeployment].push(
resolvedResources[GraphQLSchema.PermissionLevelType.AppDeployment].push(
`${organizationSlug}/${project.projectSlug}/*/appDeployment/*`,
);
continue;
}
for (const target of project.targets.targets) {
resolvedResources[GraphQLSchema.PermissionLevel.Target].push(
resolvedResources[GraphQLSchema.PermissionLevelType.Target].push(
`${organizationSlug}/${project.projectSlug}/${target.targetSlug}`,
);
if (target.services.mode === GraphQLSchema.ResourceAssignmentMode.All) {
resolvedResources[GraphQLSchema.PermissionLevel.Service].push(
if (target.services.mode === GraphQLSchema.ResourceAssignmentModeType.All) {
resolvedResources[GraphQLSchema.PermissionLevelType.Service].push(
`${organizationSlug}/${project.projectSlug}/${target.targetSlug}/service/*`,
);
} else if (target.services.services) {
for (const service of target.services.services) {
resolvedResources[GraphQLSchema.PermissionLevel.Service].push(
resolvedResources[GraphQLSchema.PermissionLevelType.Service].push(
`${organizationSlug}/${project.projectSlug}/${target.targetSlug}/service/${service.serviceName}`,
);
}
}
if (target.appDeployments.mode === GraphQLSchema.ResourceAssignmentMode.All) {
resolvedResources[GraphQLSchema.PermissionLevel.AppDeployment].push(
if (target.appDeployments.mode === GraphQLSchema.ResourceAssignmentModeType.All) {
resolvedResources[GraphQLSchema.PermissionLevelType.AppDeployment].push(
`${organizationSlug}/${project.projectSlug}/${target.targetSlug}/appDeployment/*`,
);
} else if (target.appDeployments.appDeployments) {
for (const appDeployment of target.appDeployments.appDeployments) {
resolvedResources[GraphQLSchema.PermissionLevel.AppDeployment].push(
resolvedResources[GraphQLSchema.PermissionLevelType.AppDeployment].push(
`${organizationSlug}/${project.projectSlug}/${target.targetSlug}/appDeployment/${appDeployment.appDeployment}`,
);
}
@ -68,21 +68,21 @@ export function resolveResources(
return resolvedResources;
}
export function permissionLevelToResourceName(level: GraphQLSchema.PermissionLevel) {
export function permissionLevelToResourceName(level: GraphQLSchema.PermissionLevelType) {
switch (level) {
case GraphQLSchema.PermissionLevel.Organization: {
case GraphQLSchema.PermissionLevelType.Organization: {
return 'organizations';
}
case GraphQLSchema.PermissionLevel.Project: {
case GraphQLSchema.PermissionLevelType.Project: {
return 'projects';
}
case GraphQLSchema.PermissionLevel.Target: {
case GraphQLSchema.PermissionLevelType.Target: {
return 'targets';
}
case GraphQLSchema.PermissionLevel.Service: {
case GraphQLSchema.PermissionLevelType.Service: {
return 'services';
}
case GraphQLSchema.PermissionLevel.AppDeployment: {
case GraphQLSchema.PermissionLevelType.AppDeployment: {
return 'app deployments';
}
}

View file

@ -1,4 +1,4 @@
import { Organization, PermissionLevel } from '@/gql/graphql';
import { Organization, PermissionLevelType } from '@/gql/graphql';
export const availableMemberPermissionGroups: Organization['availableMemberPermissionGroups'] = [
{
@ -12,7 +12,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'organization:describe',
dependsOnId: null,
description: 'Member can see the organization. Permission can not be modified.',
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'View organization',
},
{
@ -21,7 +21,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'support:manageTickets',
dependsOnId: null,
description: 'Member can access, create and reply to support tickets.',
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'Access support tickets',
},
{
@ -30,7 +30,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'organization:modifySlug',
dependsOnId: null,
description: 'Member can modify the organization slug.',
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'Update organization slug',
},
{
@ -39,7 +39,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'auditLog:export',
dependsOnId: null,
description: 'Member can access and export the audit log.',
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'Export audit log',
},
{
@ -48,7 +48,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'organization:delete',
dependsOnId: null,
description: 'Member can delete the Organization.',
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'Delete organization',
},
],
@ -64,7 +64,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'member:describe',
dependsOnId: null,
description: 'Member can access the organization member overview.',
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'View members',
},
{
@ -73,7 +73,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'member:modify',
dependsOnId: 'member:describe',
description: 'Member can assign roles to users.',
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'Assign member role',
warning:
'Granting a role the ability to assign roles enables it to elevate its own permissions.',
@ -91,7 +91,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'billing:describe',
dependsOnId: null,
description: 'Member can view the billing information.',
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'View billing',
},
{
@ -100,7 +100,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'billing:update',
dependsOnId: 'billing:describe',
description: 'Member can change the organization plan.',
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'Update billing',
},
],
@ -116,7 +116,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'oidc:modify',
dependsOnId: null,
description: 'Member can connect, modify, and remove an OIDC provider to the connection.',
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'Manage OpenID Connect integration',
},
],
@ -133,7 +133,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
dependsOnId: null,
description:
'Member can connect, modify, and remove access for the GitHub integration and repository access.',
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'Manage GitHub integration',
},
],
@ -150,7 +150,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
dependsOnId: null,
description:
'Member can connect, modify, and remove access for the Slack integration and repository access.',
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'Manage Slack integration',
},
],
@ -166,7 +166,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'project:create',
dependsOnId: null,
description: 'Member can create new projects.',
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'Create project',
},
{
@ -175,7 +175,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'project:describe',
dependsOnId: null,
description: 'Member can access the specified projects.',
level: PermissionLevel.Project,
level: PermissionLevelType.Project,
title: 'View project',
},
{
@ -184,7 +184,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'project:delete',
dependsOnId: 'project:describe',
description: 'Member can access the specified projects.',
level: PermissionLevel.Project,
level: PermissionLevelType.Project,
title: 'Delete project',
},
{
@ -193,7 +193,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'project:modifySettings',
dependsOnId: 'project:describe',
description: 'Member can access the specified projects.',
level: PermissionLevel.Project,
level: PermissionLevelType.Project,
title: 'Modify Settings',
},
],
@ -209,7 +209,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'schemaLinting:modifyOrganizationRules',
dependsOnId: null,
description: 'Member can view and modify the organization schema linting rules.',
level: PermissionLevel.Organization,
level: PermissionLevelType.Organization,
title: 'Manage organization level schema linting',
},
{
@ -218,7 +218,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'schemaLinting:modifyProjectRules',
dependsOnId: 'project:describe',
description: 'Member can view and modify the projects schema linting rules.',
level: PermissionLevel.Project,
level: PermissionLevelType.Project,
title: 'Manage project level schema linting',
},
],
@ -234,7 +234,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'target:create',
dependsOnId: 'project:describe',
description: 'Member can create new projects.',
level: PermissionLevel.Project,
level: PermissionLevelType.Project,
title: 'Create target',
},
{
@ -243,7 +243,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'target:delete',
dependsOnId: 'project:describe',
description: 'Member can access the specified projects.',
level: PermissionLevel.Target,
level: PermissionLevelType.Target,
title: 'Delete target',
},
{
@ -252,7 +252,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'target:modifySettings',
dependsOnId: 'project:describe',
description: 'Member can access the specified projects.',
level: PermissionLevel.Target,
level: PermissionLevelType.Target,
title: 'Modify settings',
},
{
@ -261,7 +261,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'alert:modify',
dependsOnId: 'project:describe',
description: 'Can create alerts for schema versions.',
level: PermissionLevel.Project,
level: PermissionLevelType.Project,
title: 'Modify alerts',
},
{
@ -270,7 +270,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'targetAccessToken:modify',
dependsOnId: 'project:describe',
description: 'Allow managing access tokens for CLI and Usage Reporting.',
level: PermissionLevel.Target,
level: PermissionLevelType.Target,
title: 'Manage registry access tokens',
},
{
@ -279,7 +279,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'cdnAccessToken:modify',
dependsOnId: 'project:describe',
description: 'Allow managing access tokens for the CDN.',
level: PermissionLevel.Target,
level: PermissionLevelType.Target,
title: 'Manage CDN access tokens',
},
],
@ -295,7 +295,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'laboratory:describe',
dependsOnId: 'project:describe',
description: 'Member can access the laboratory, view and execute GraphQL documents.',
level: PermissionLevel.Target,
level: PermissionLevelType.Target,
title: 'View laboratory',
},
{
@ -305,7 +305,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
dependsOnId: 'laboratory:describe',
description:
'Member can create, delete and update collections and documents in the laboratory.',
level: PermissionLevel.Target,
level: PermissionLevelType.Target,
title: 'Modify laboratory',
},
{
@ -314,7 +314,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'laboratory:modifyPreflightScript',
dependsOnId: 'laboratory:describe',
description: 'Member can update the laboratory preflight script.',
level: PermissionLevel.Target,
level: PermissionLevelType.Target,
title: 'Modify the laboratory preflight script',
},
],
@ -330,7 +330,7 @@ export const availableMemberPermissionGroups: Organization['availableMemberPermi
id: 'schemaCheck:approve',
dependsOnId: 'project:describe',
description: 'Member can approve failed schema checks.',
level: PermissionLevel.Service,
level: PermissionLevelType.Service,
title: 'Approve schema check',
},
],