Files
speckle-server/packages/server/modules/serverinvites/services/validation.ts
T
Gergő Jedlicska c6cd4c311d feat(serverinvites): create domain module in server invites (#2401)
* chore(serverinvites): repository refactor for multiregion

* chore(serverinvites): remove migrated functions from old repository

* chore(serverinvites): refactor serverInviteForToken resolver for multiregion

* chore(serverinvites): invite processing service refactor for multiregion

* chore(serverinvites): subscription refactor for multiregion

* chore(serverinvites): move buildEmailContents to dedicated file

* chore(serverinvites): deleteAllStreamInvites function multiregion refactor

* chore(serverinvites): refactor deleteServerOnlyInvites multiregion repository

* chore(serverinvites): complete repository refactor for multiregion

* feat(serverinvites): create domain module in server invites

* fix(serverinvites): no relative imports

* feat(serverinvites): extract individual types from repository

* feat(serverinvites): move interfaces to operations

* fix(serverinvites): update imports referencing old interfaces file

* fix(serverinvites): type mismatch for insert invite and delete old

* chore(serverinvites): refactor to single repo function

* test(serverinvites): fix tests

* fix(serverinvites): use domain types in all places

* feat(serverinvites): WIP unity

* feat(serverinvites): move to new facory names and types

* feat(serverinvites): fix tests

* fix(serverinvites): use factory name

---------

Co-authored-by: Alessandro Magionami <alessandro.magionami@gmail.com>
2024-06-25 13:24:37 +02:00

121 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 '../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')
}
}
}