feat(server): support editor -> viewer seat downgrades (#4181)

* new seat based project role checks implemented

* everything done

* minor bugfix
This commit is contained in:
Kristaps Fabians Geikins
2025-03-14 14:21:25 +02:00
committed by GitHub
parent 50fd05afe8
commit d903e8ffc4
30 changed files with 975 additions and 337 deletions
@@ -1,5 +1,4 @@
import { WorkspaceEvents } from '@/modules/workspacesCore/domain/events'
import { StreamRecord } from '@/modules/core/helpers/types'
import {
Workspace,
WorkspaceAcl,
@@ -20,12 +19,9 @@ import {
StreamRoles,
WorkspaceRoles
} from '@speckle/shared'
import {
WorkspaceCreationState,
WorkspaceRoleToDefaultProjectRoleMapping
} from '@/modules/workspaces/domain/types'
import { WorkspaceCreationState } from '@/modules/workspaces/domain/types'
import { WorkspaceTeam } from '@/modules/workspaces/domain/types'
import { Stream } from '@/modules/core/domain/streams/types'
import { Stream, StreamWithOptionalRole } from '@/modules/core/domain/streams/types'
import { TokenResourceIdentifier } from '@/modules/core/domain/tokens/types'
import { ServerRegion } from '@/modules/multiregion/domain/types'
import { SetOptional } from 'type-fest'
@@ -199,22 +195,37 @@ export type UpdateWorkspaceRole = (
* Only add or upgrade role, prevent downgrades
*/
preventRoleDowngrade?: boolean
updatedByUserId: string
}
) => Promise<void>
export type GetWorkspaceRoleToDefaultProjectRoleMapping = (args: {
export type GetWorkspaceRolesAllowedProjectRolesFactory = (params: {
workspaceId: string
}) => Promise<WorkspaceRoleToDefaultProjectRoleMapping>
}) => Promise<{
defaultProjectRole: (args: {
workspaceRole: WorkspaceRoles
seatType: MaybeNullOrUndefined<WorkspaceSeatType>
}) => StreamRoles | null
allowedProjectRoles: (args: {
workspaceRole: WorkspaceRoles
seatType: MaybeNullOrUndefined<WorkspaceSeatType>
}) => StreamRoles[]
}>
/** Workspace Projects */
type QueryAllWorkspaceProjectsArgs = {
workspaceId: string
/**
* Optionally get project roles for a specific user
*/
userId?: string
}
export type QueryAllWorkspaceProjects = (
args: QueryAllWorkspaceProjectsArgs
) => AsyncGenerator<StreamRecord[], void, unknown>
) => AsyncGenerator<StreamWithOptionalRole[], void, unknown>
/** Workspace Project Roles */
@@ -337,7 +348,9 @@ export type GetWorkspaceJoinRequest = (
) => Promise<WorkspaceJoinRequest | undefined>
export type ApproveWorkspaceJoinRequest = (
params: Pick<WorkspaceJoinRequest, 'workspaceId' | 'userId'>
params: Pick<WorkspaceJoinRequest, 'workspaceId' | 'userId'> & {
approvedByUserId: string
}
) => Promise<boolean>
export type DenyWorkspaceJoinRequest = (
@@ -388,7 +401,10 @@ export type CopyProjectAutomations = (params: {
}) => Promise<Record<string, number>>
export type AssignWorkspaceSeat = (
params: Pick<WorkspaceSeat, 'userId' | 'workspaceId'> & { type: WorkspaceSeatType }
params: Pick<WorkspaceSeat, 'userId' | 'workspaceId'> & {
type: WorkspaceSeatType
assignedByUserId: string
}
) => Promise<WorkspaceSeat>
export type EnsureValidWorkspaceRoleSeat = (params: {
@@ -1,7 +1,7 @@
export { WorkspaceInviteResourceTarget } from '@/modules/workspacesCore/domain/types'
import { LimitedUserRecord, UserWithRole } from '@/modules/core/helpers/types'
import { WorkspaceInviteResourceType } from '@/modules/workspacesCore/domain/constants'
import { StreamRoles, WorkspaceRoles } from '@speckle/shared'
import { WorkspaceRoles } from '@speckle/shared'
declare module '@/modules/serverinvites/domain/types' {
interface InviteResourceTargetTypeMap {
@@ -22,10 +22,6 @@ export type WorkspaceTeamMember = UserWithRole<LimitedUserRecord> & {
export type WorkspaceTeam = WorkspaceTeamMember[]
export type WorkspaceRoleToDefaultProjectRoleMapping = {
[key in WorkspaceRoles]: StreamRoles | null
}
export type WorkspaceCreationState = {
workspaceId: string
completed: boolean