Files
speckle-server/packages/server/modules/serverinvites/services/coreFinalization.ts
T

122 lines
3.6 KiB
TypeScript

import {
AddOrUpdateStreamCollaborator,
GetStream
} from '@/modules/core/domain/streams/operations'
import { StreamInvalidAccessError } from '@/modules/core/errors/stream'
import { isResourceAllowed } from '@/modules/core/helpers/token'
import { ProjectInviteResourceType } from '@/modules/serverinvites/domain/constants'
import { InviteFinalizingError } from '@/modules/serverinvites/errors'
import {
InviteFinalizationAction,
ProcessFinalizedResourceInvite,
ValidateResourceInviteBeforeFinalization
} from '@/modules/serverinvites/services/operations'
import { Roles } from '@speckle/shared'
type ValidateProjectInviteBeforeFinalizationFactoryDeps = {
getProject: GetStream
}
export const validateProjectInviteBeforeFinalizationFactory =
(
deps: ValidateProjectInviteBeforeFinalizationFactoryDeps
): ValidateResourceInviteBeforeFinalization =>
async (params) => {
const { getProject } = deps
const { invite, finalizerUserId, action, finalizerResourceAccessLimits } = params
if (invite.resource.resourceType !== ProjectInviteResourceType) {
throw new InviteFinalizingError(
'Attempting to finalize non-project invite as project invite',
{ info: { invite, finalizerUserId } }
)
}
// If decline, skip all further validation
if (action === InviteFinalizationAction.DECLINE) {
return
}
const project = await getProject({
streamId: invite.resource.resourceId,
userId: finalizerUserId
})
if (!project) {
throw new InviteFinalizingError(
'Attempting to finalize invite to a non-existant project'
)
}
if (action === InviteFinalizationAction.CANCEL) {
if (project.role !== Roles.Stream.Owner) {
throw new InviteFinalizingError(
'Attempting to cancel invite to a project that the user does not own'
)
}
} else {
if (project.role) {
throw new InviteFinalizingError(
'Attempting to finalize invite to a project that the user already has access to'
)
}
}
if (
!isResourceAllowed({
resourceId: project.id,
resourceType: 'project',
resourceAccessRules: finalizerResourceAccessLimits
})
) {
throw new InviteFinalizingError(
'You are not allowed to process an invite for this project'
)
}
}
type ProcessFinalizedProjectInviteFactoryDeps = {
getProject: GetStream
addProjectRole: AddOrUpdateStreamCollaborator
}
export const processFinalizedProjectInviteFactory =
(deps: ProcessFinalizedProjectInviteFactoryDeps): ProcessFinalizedResourceInvite =>
async (params) => {
const { getProject, addProjectRole } = deps
const { invite, finalizerUserId, action } = params
const project = await getProject({ streamId: invite.resource.resourceId })
if (action === InviteFinalizationAction.DECLINE) {
// Skip validation so user can get rid of the invite regardless
return
}
if (!project) {
throw new InviteFinalizingError(
'Attempting to finalize invite to a non-existant project'
)
}
if (action === InviteFinalizationAction.ACCEPT) {
try {
await addProjectRole(
project.id,
finalizerUserId,
invite.resource.role || Roles.Stream.Contributor,
invite.inviterId,
null,
{ fromInvite: invite }
)
} catch (e) {
if (!(e instanceof StreamInvalidAccessError)) {
throw e
}
throw new InviteFinalizingError(
'Original inviter no longer has the rights to invite you to this project'
)
}
}
}