Files
speckle-server/packages/server/modules/serverinvites/services/validation.ts
T
2024-07-18 16:07:16 +02:00

125 lines
3.6 KiB
TypeScript

import { UserRecord } from '@/modules/core/helpers/types'
import { CreateInviteParams } from '@/modules/serverinvites/domain/operations'
import { InviteCreateValidationError } from '@/modules/serverinvites/errors'
import {
ResourceTargets,
isServerInvite,
resolveTarget
} from '@/modules/serverinvites/helpers/inviteHelper'
import { UserWithOptionalRole } from '@/modules/core/repositories/users'
import { authorizeResolver } from '@/modules/shared'
import { Roles } from '@speckle/shared'
import { getStreamCollaborator } from '@/modules/core/repositories/streams'
import { TokenResourceIdentifier } from '@/modules/core/domain/tokens/types'
/**
* Validate invite creation input data
*/
export async function validateInput(
params: CreateInviteParams,
inviter: UserRecord | null,
resource?: { name: string } | null,
targetUser?: UserWithOptionalRole | null,
inviterResourceAccessLimits?: TokenResourceIdentifier[] | null
) {
const { message } = params
// validate inviter & invitee
validateTargetUser(params, targetUser)
await validateInviter(params, inviter, inviterResourceAccessLimits)
// validate resource
await validateResource(params, resource, targetUser)
// check if message too long
if (message) {
if (message.length >= 1024) {
throw new InviteCreateValidationError('Personal message too long')
}
}
}
function validateTargetUser(
params: CreateInviteParams,
targetUser?: UserRecord | null
) {
const { target } = params
const { userId } = resolveTarget(target)
if (userId && !targetUser) {
throw new InviteCreateValidationError('Attempting to invite an invalid user')
}
if (isServerInvite(params) && targetUser) {
throw new InviteCreateValidationError(
'This email is already associated with an account on this server'
)
}
}
/**
* Validate that the inviter has access to the resources he's trying to invite people to
*/
async function validateInviter(
params: CreateInviteParams,
inviter: UserRecord | null,
inviterResourceAccessLimits?: TokenResourceIdentifier[] | null
) {
const { resourceId, resourceTarget } = params
if (!inviter) throw new InviteCreateValidationError('Invalid inviter')
if (isServerInvite(params)) return
try {
if (resourceTarget === ResourceTargets.Streams) {
await authorizeResolver(
inviter.id,
resourceId!, // TODO: check null
Roles.Stream.Owner,
inviterResourceAccessLimits
)
} else {
throw new InviteCreateValidationError('Unexpected resource target type')
}
} catch (e) {
throw new InviteCreateValidationError(
"Inviter doesn't have proper access to the resource",
{ cause: e as Error }
)
}
}
/**
* Validate the target resource
*/
async function validateResource(
params: CreateInviteParams,
resource?: { name: string } | null,
targetUser?: UserRecord | null
) {
const { resourceId, resourceTarget, role } = params
if (resourceId && !resource) {
throw new InviteCreateValidationError("Couldn't resolve invite resource")
}
if (resourceTarget === ResourceTargets.Streams) {
if (targetUser) {
// Check if user isn't already associated with the stream
const isStreamCollaborator = !!(await getStreamCollaborator(
resourceId!, // TODO: verify this null
targetUser.id
))
if (isStreamCollaborator) {
throw new InviteCreateValidationError(
'The target user is already a collaborator of the specified project'
)
}
}
// TODO: check null role
if (!Object.values(Roles.Stream).includes(role!)) {
throw new InviteCreateValidationError('Unexpected stream invite role')
}
}
}