fix(fe2): not being able to remove member from workspace (#4468)

* fix(fe2): not being able to remove member from workspace

* minor comment

* withTransaction refactor
This commit is contained in:
Kristaps Fabians Geikins
2025-04-17 10:44:55 +03:00
committed by GitHub
parent c33052e697
commit 0cc19dbdf5
9 changed files with 326 additions and 237 deletions
@@ -264,7 +264,7 @@ export const useSettingsMembersActions = (params: {
footerItems.push({
title: 'Remove from workspace...',
id: WorkspaceUserActionTypes.RemoveFromWorkspace,
disabled: isOnlyAdmin.value,
disabled: isOnlyAdmin.value && targetUserRole.value === Roles.Workspace.Admin,
disabledTooltip: 'There must be at least one admin in this workspace'
})
}
@@ -120,33 +120,31 @@ export const getBillingRouter = (): Router => {
})
logger.info(OperationStatus.start, '[{operationName} ({operationStatus})] ')
// this must use a transaction
const trx = await db.transaction()
const completeCheckout = completeCheckoutSessionFactory({
getCheckoutSession: getCheckoutSessionFactory({ db: trx }),
updateCheckoutSessionStatus: updateCheckoutSessionStatusFactory({
db: trx
}),
upsertPaidWorkspacePlan: upsertPaidWorkspacePlanFactory({ db: trx }),
upsertWorkspaceSubscription: upsertWorkspaceSubscriptionFactory({
db: trx
}),
getSubscriptionData: getSubscriptionDataFactory({
stripe
}),
emitEvent: getEventBus().emit
})
await withOperationLogging(
async () =>
await withTransaction(
completeCheckout({
sessionId: session.id,
subscriptionId
}),
trx
async ({ db }) => {
const completeCheckout = completeCheckoutSessionFactory({
getCheckoutSession: getCheckoutSessionFactory({ db }),
updateCheckoutSessionStatus: updateCheckoutSessionStatusFactory({
db
}),
upsertPaidWorkspacePlan: upsertPaidWorkspacePlanFactory({ db }),
upsertWorkspaceSubscription: upsertWorkspaceSubscriptionFactory({
db
}),
getSubscriptionData: getSubscriptionDataFactory({
stripe
}),
emitEvent: getEventBus().emit
})
return completeCheckout({
sessionId: session.id,
subscriptionId
})
},
{ db }
),
{
logger,
@@ -23,9 +23,7 @@ import { reconcileWorkspaceSubscriptionFactory } from '@/modules/gatekeeper/clie
import Stripe from 'stripe'
import { cloneDeep } from 'lodash'
import { Logger } from '@/observability/logging'
// get all workspace plan from the DB
// foreach workspace:
import { withTransaction } from '@/modules/shared/helpers/dbHelper'
export const migrateOldWorkspacePlans =
({ db, stripe, logger }: { db: Knex; stripe: Stripe; logger: Logger }) =>
@@ -52,9 +50,14 @@ export const migrateOldWorkspacePlans =
for (const oldPlan of oldPlanWorkspaces) {
try {
await migrateWorkspacePlan({ db, stripe, logger })({
workspaceId: oldPlan.workspaceId
})
await withTransaction(
async ({ db }) => {
await migrateWorkspacePlan({ db, stripe, logger })({
workspaceId: oldPlan.workspaceId
})
},
{ db }
)
} catch (err) {
logger.error(
{ err, workspaceId: oldPlan.workspaceId, oldPlan },
@@ -166,10 +169,9 @@ export const migrateWorkspacePlan =
'Migrating {workspaceId} from old plan {workspacePlan} to new plan {newTargetPlan}'
)
const trx = await db.transaction()
// add editor seats to everyone
const workspaceMembers = await getWorkspaceRolesFactory({ db: trx })({
const workspaceMembers = await getWorkspaceRolesFactory({ db })({
workspaceId
})
const seats = workspaceMembers.map((m) => ({
@@ -184,7 +186,7 @@ export const migrateWorkspacePlan =
'Inserting {migratedSeatsCount} new seats for the workspace {workspaceId}'
)
await trx<WorkspaceSeat>('workspace_seats')
await db<WorkspaceSeat>('workspace_seats')
.insert(seats)
.onConflict(['workspaceId', 'userId'])
.merge()
@@ -193,7 +195,7 @@ export const migrateWorkspacePlan =
{ migratedSeatsCount: seats.length },
'Workspace {workspaceId} has added {migratedSeatsCount} seats'
)
await upsertWorkspacePlanFactory({ db: trx })({
await upsertWorkspacePlanFactory({ db })({
//@ts-expect-error the switch above makes sure things are ok
workspacePlan: {
workspaceId,
@@ -220,7 +222,7 @@ export const migrateWorkspacePlan =
)
}
// if stripe paid plan, convert the stripe sub to use all editor seats
const workspaceSubscription = await getWorkspaceSubscriptionFactory({ db: trx })({
const workspaceSubscription = await getWorkspaceSubscriptionFactory({ db })({
workspaceId
})
if (!workspaceSubscription)
@@ -264,7 +266,6 @@ export const migrateWorkspacePlan =
prorationBehavior: 'create_prorations'
})
}
await trx.commit()
log.info('🥳 Workspace plan migration completed for workspace {workspaceId}')
// add and editor seat to all workspace members
@@ -147,53 +147,87 @@ export const startQueue = async () => {
const sourceDb = await getProjectDbClient({ projectId })
const sourceObjectStorage = await getProjectObjectStorage({ projectId })
const targetDb = await (await getRegionDb({ regionKey })).transaction()
const targetDb = await getRegionDb({ regionKey })
const targetObjectStorage = await getRegionObjectStorage({ regionKey })
const updateProjectRegion = updateProjectRegionFactory({
getProject: getProjectFactory({ db: sourceDb }),
getAvailableRegions: getAvailableRegionsFactory({
getRegions: getRegionsFactory({ db }),
canWorkspaceUseRegions: canWorkspaceUseRegionsFactory({
getWorkspacePlan: getWorkspacePlanFactory({ db })
})
}),
copyWorkspace: copyWorkspaceFactory({ sourceDb, targetDb }),
copyProjects: copyProjectsFactory({ sourceDb, targetDb }),
copyProjectModels: copyProjectModelsFactory({ sourceDb, targetDb }),
copyProjectVersions: copyProjectVersionsFactory({ sourceDb, targetDb }),
copyProjectObjects: copyProjectObjectsFactory({ sourceDb, targetDb }),
copyProjectAutomations: copyProjectAutomationsFactory({ sourceDb, targetDb }),
copyProjectComments: copyProjectCommentsFactory({ sourceDb, targetDb }),
copyProjectWebhooks: copyProjectWebhooksFactory({ sourceDb, targetDb }),
copyProjectBlobs: copyProjectBlobs({
sourceDb,
sourceObjectStorage,
targetDb,
targetObjectStorage
}),
validateProjectRegionCopy: validateProjectRegionCopyFactory({
countProjectModels: getStreamBranchCountFactory({ db: sourceDb }),
countProjectVersions: getStreamCommitCountFactory({ db: sourceDb }),
countProjectObjects: getStreamObjectCountFactory({ db: sourceDb }),
countProjectAutomations: getProjectAutomationsTotalCountFactory({
db: sourceDb
}),
countProjectComments: getStreamCommentCountFactory({ db: sourceDb }),
getProjectWebhooks: getStreamWebhooksFactory({ db: sourceDb })
}),
updateProjectRegionKey: updateProjectRegionKeyFactory({
upsertProjectRegionKey: upsertProjectRegionKeyFactory({ db }),
cacheDeleteRegionKey: deleteRegionKeyFromCacheFactory({
redis: getGenericRedis()
}),
emitEvent: getEventBus().emit
})
})
return await withTransaction(
updateProjectRegion({ projectId, regionKey }),
targetDb
async ({ db: targetDbTrx }) => {
const updateProjectRegion = updateProjectRegionFactory({
getProject: getProjectFactory({ db: sourceDb }),
getAvailableRegions: getAvailableRegionsFactory({
getRegions: getRegionsFactory({ db }),
canWorkspaceUseRegions: canWorkspaceUseRegionsFactory({
getWorkspacePlan: getWorkspacePlanFactory({ db })
})
}),
copyWorkspace: copyWorkspaceFactory({
sourceDb,
targetDb: targetDbTrx
}),
copyProjects: copyProjectsFactory({
sourceDb,
targetDb: targetDbTrx
}),
copyProjectModels: copyProjectModelsFactory({
sourceDb,
targetDb: targetDbTrx
}),
copyProjectVersions: copyProjectVersionsFactory({
sourceDb,
targetDb: targetDbTrx
}),
copyProjectObjects: copyProjectObjectsFactory({
sourceDb,
targetDb: targetDbTrx
}),
copyProjectAutomations: copyProjectAutomationsFactory({
sourceDb,
targetDb: targetDbTrx
}),
copyProjectComments: copyProjectCommentsFactory({
sourceDb,
targetDb: targetDbTrx
}),
copyProjectWebhooks: copyProjectWebhooksFactory({
sourceDb,
targetDb: targetDbTrx
}),
copyProjectBlobs: copyProjectBlobs({
sourceDb,
sourceObjectStorage,
targetDb: targetDbTrx,
targetObjectStorage
}),
validateProjectRegionCopy: validateProjectRegionCopyFactory({
countProjectModels: getStreamBranchCountFactory({
db: sourceDb
}),
countProjectVersions: getStreamCommitCountFactory({
db: sourceDb
}),
countProjectObjects: getStreamObjectCountFactory({
db: sourceDb
}),
countProjectAutomations: getProjectAutomationsTotalCountFactory({
db: sourceDb
}),
countProjectComments: getStreamCommentCountFactory({
db: sourceDb
}),
getProjectWebhooks: getStreamWebhooksFactory({ db: sourceDb })
}),
updateProjectRegionKey: updateProjectRegionKeyFactory({
upsertProjectRegionKey: upsertProjectRegionKeyFactory({ db }),
cacheDeleteRegionKey: deleteRegionKeyFromCacheFactory({
redis: getGenericRedis()
}),
emitEvent: getEventBus().emit
})
})
return updateProjectRegion({ projectId, regionKey })
},
{ db: targetDb }
)
}
case 'delete-project-region-data':
@@ -1,6 +1,13 @@
import { EmitArg, EventBus, EventBusEmit } from '@/modules/shared/services/eventBus'
import { Knex } from 'knex'
/**
* TODO: Fix api - make operationFactory db arg actually return the trx. Currently many usages of this
* are not working correctly cause they just use the db, skipping the transaction
*
* Also: withOperationLogging and withOperationTransaction could all be merged into this, with
* this having a better name like `operationFactory`
*/
export const commandFactory =
<TOperation extends (...args: Parameters<TOperation>) => ReturnType<TOperation>>({
db,
@@ -3,7 +3,7 @@ import { Knex } from 'knex'
import { isString } from 'lodash'
import { postgresMaxConnections } from '@/modules/shared/helpers/envHelper'
import { EnvironmentResourceError } from '@/modules/shared/errors'
import { isNonNullable } from '@speckle/shared'
import { isNonNullable, MaybeAsync } from '@speckle/shared'
export type BatchedSelectOptions = {
/**
@@ -107,17 +107,20 @@ export const numberOfFreeConnections = (knex: Knex) => {
}
export const withTransaction = async <T>(
callback: Promise<T> | T,
trx: Knex.Transaction
): Promise<T> => {
operation: (args: { db: Knex }) => MaybeAsync<T>,
params: {
db: Knex
}
) => {
const { db } = params
const trx = await db.transaction()
try {
return await callback
const result = await operation({ db: trx })
await trx.commit()
return result
} catch (e) {
await trx.rollback()
throw e
} finally {
if (trx.isTransaction && !trx.isCompleted()) {
await trx.commit()
}
}
}
@@ -711,60 +711,87 @@ export const initializeEventListenersFactory =
})
}),
eventBus.listen(WorkspaceEvents.RoleDeleted, async ({ payload }) => {
const trx = await db.transaction()
const onWorkspaceRoleDeleted = onWorkspaceRoleDeletedFactory({
queryAllWorkspaceProjects: queryAllWorkspaceProjectsFactory({ getStreams }),
deleteProjectRole: deleteProjectRoleFactory({ db: trx }),
deleteWorkspaceSeat: deleteWorkspaceSeatFactory({ db: trx })
})
await withTransaction(onWorkspaceRoleDeleted(payload.acl), trx)
await withTransaction(
async ({ db: trx }) => {
const onWorkspaceRoleDeleted = onWorkspaceRoleDeletedFactory({
queryAllWorkspaceProjects: queryAllWorkspaceProjectsFactory({
getStreams
}),
deleteProjectRole: deleteProjectRoleFactory({ db: trx }),
deleteWorkspaceSeat: deleteWorkspaceSeatFactory({ db: trx })
})
return await onWorkspaceRoleDeleted(payload.acl)
},
{ db }
)
}),
eventBus.listen(WorkspaceEvents.RoleUpdated, async ({ payload }) => {
const trx = await db.transaction()
const onWorkspaceRoleUpdated = onWorkspaceRoleUpdatedFactory({
getWorkspaceWithPlan: getWorkspaceWithPlanFactory({ db }),
getWorkspaceRoleToDefaultProjectRoleMapping:
getWorkspaceRoleToDefaultProjectRoleMappingFactory({
getWorkspaceWithPlan: getWorkspaceWithPlanFactory({ db })
}),
queryAllWorkspaceProjects: queryAllWorkspaceProjectsFactory({ getStreams }),
setStreamCollaborator: setStreamCollaboratorFactory({
getUser: getUserFactory({ db }),
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
emitEvent: eventBus.emit,
grantStreamPermissions: grantStreamPermissionsFactory({ db: trx }),
isStreamCollaborator: isStreamCollaboratorFactory({
getStream: getStreamFactory({ db })
}),
revokeStreamPermissions: revokeStreamPermissionsFactory({ db: trx })
}),
getStreamsCollaboratorCounts: getStreamsCollaboratorCountsFactory({ db })
})
await withTransaction(onWorkspaceRoleUpdated(payload), trx)
await withTransaction(
async ({ db: trx }) => {
const onWorkspaceRoleUpdated = onWorkspaceRoleUpdatedFactory({
getWorkspaceWithPlan: getWorkspaceWithPlanFactory({ db }),
getWorkspaceRoleToDefaultProjectRoleMapping:
getWorkspaceRoleToDefaultProjectRoleMappingFactory({
getWorkspaceWithPlan: getWorkspaceWithPlanFactory({ db })
}),
queryAllWorkspaceProjects: queryAllWorkspaceProjectsFactory({
getStreams
}),
setStreamCollaborator: setStreamCollaboratorFactory({
getUser: getUserFactory({ db }),
validateStreamAccess: validateStreamAccessFactory({
authorizeResolver
}),
emitEvent: eventBus.emit,
grantStreamPermissions: grantStreamPermissionsFactory({
db: trx
}),
isStreamCollaborator: isStreamCollaboratorFactory({
getStream: getStreamFactory({ db })
}),
revokeStreamPermissions: revokeStreamPermissionsFactory({
db: trx
})
}),
getStreamsCollaboratorCounts: getStreamsCollaboratorCountsFactory({ db })
})
return await onWorkspaceRoleUpdated(payload)
},
{ db }
)
}),
eventBus.listen(WorkspaceEvents.SeatUpdated, async (payload) => {
const trx = await db.transaction()
const onWorkspaceSeatUpdated = onWorkspaceSeatUpdatedFactory({
setStreamCollaborator: setStreamCollaboratorFactory({
getUser: getUserFactory({ db }),
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
emitEvent: eventBus.emit,
grantStreamPermissions: grantStreamPermissionsFactory({ db: trx }),
isStreamCollaborator: isStreamCollaboratorFactory({
getStream: getStreamFactory({ db })
}),
revokeStreamPermissions: revokeStreamPermissionsFactory({ db: trx })
}),
queryAllWorkspaceProjects: queryAllWorkspaceProjectsFactory({ getStreams }),
getWorkspaceWithPlan: getWorkspaceWithPlanFactory({ db }),
getWorkspaceRoleForUser: getWorkspaceRoleForUserFactory({ db }),
getWorkspaceSeatTypeToProjectRoleMapping:
getWorkspaceSeatTypeToProjectRoleMappingFactory({
getWorkspaceWithPlan: getWorkspaceWithPlanFactory({ db })
await withTransaction(
async ({ db: trx }) => {
const onWorkspaceSeatUpdated = onWorkspaceSeatUpdatedFactory({
setStreamCollaborator: setStreamCollaboratorFactory({
getUser: getUserFactory({ db }),
validateStreamAccess: validateStreamAccessFactory({
authorizeResolver
}),
emitEvent: eventBus.emit,
grantStreamPermissions: grantStreamPermissionsFactory({ db: trx }),
isStreamCollaborator: isStreamCollaboratorFactory({
getStream: getStreamFactory({ db })
}),
revokeStreamPermissions: revokeStreamPermissionsFactory({ db: trx })
}),
queryAllWorkspaceProjects: queryAllWorkspaceProjectsFactory({
getStreams
}),
getWorkspaceWithPlan: getWorkspaceWithPlanFactory({ db }),
getWorkspaceRoleForUser: getWorkspaceRoleForUserFactory({ db }),
getWorkspaceSeatTypeToProjectRoleMapping:
getWorkspaceSeatTypeToProjectRoleMappingFactory({
getWorkspaceWithPlan: getWorkspaceWithPlanFactory({ db })
})
})
})
await withTransaction(onWorkspaceSeatUpdated(payload), trx)
return await onWorkspaceSeatUpdated(payload)
},
{ db }
)
}),
eventBus.listen('**', emitWorkspaceGraphqlSubscriptions),
eventBus.listen(
@@ -733,17 +733,19 @@ export = FF_WORKSPACES_MODULE_ENABLED
if (!role) {
// this is currently not working with the command factory
// TODO: include the onWorkspaceRoleDeletedFactory listener service
const trx = await db.transaction()
const deleteWorkspaceRole = deleteWorkspaceRoleFactory({
deleteWorkspaceRole: repoDeleteWorkspaceRoleFactory({ db: trx }),
getWorkspaceRoles: getWorkspaceRolesFactory({ db: trx }),
emitWorkspaceEvent: getEventBus().emit
})
await withOperationLogging(
async () =>
await withTransaction(
deleteWorkspaceRole({ workspaceId, userId }),
trx
async ({ db: trx }) => {
const deleteWorkspaceRole = deleteWorkspaceRoleFactory({
deleteWorkspaceRole: repoDeleteWorkspaceRoleFactory({ db: trx }),
getWorkspaceRoles: getWorkspaceRolesFactory({ db: trx }),
emitWorkspaceEvent: getEventBus().emit
})
return await deleteWorkspaceRole({ workspaceId, userId })
},
{ db }
),
{
logger,
@@ -758,18 +760,18 @@ export = FF_WORKSPACES_MODULE_ENABLED
const updateWorkspaceRole = commandFactory({
db,
eventBus,
operationFactory: ({ db, emit }) =>
operationFactory: ({ trx, emit }) =>
updateWorkspaceRoleFactory({
upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }),
getWorkspaceWithDomains: getWorkspaceWithDomainsFactory({ db }),
upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db: trx }),
getWorkspaceWithDomains: getWorkspaceWithDomainsFactory({ db: trx }),
findVerifiedEmailsByUserId: findVerifiedEmailsByUserIdFactory({
db
db: trx
}),
getWorkspaceRoles: getWorkspaceRolesFactory({ db }),
getWorkspaceRoles: getWorkspaceRolesFactory({ db: trx }),
emitWorkspaceEvent: emit,
ensureValidWorkspaceRoleSeat: ensureValidWorkspaceRoleSeatFactory({
createWorkspaceSeat: createWorkspaceSeatFactory({ db }),
getWorkspaceUserSeat: getWorkspaceUserSeatFactory({ db }),
createWorkspaceSeat: createWorkspaceSeatFactory({ db: trx }),
getWorkspaceUserSeat: getWorkspaceUserSeatFactory({ db: trx }),
eventEmit: emit
})
})
@@ -946,17 +948,22 @@ export = FF_WORKSPACES_MODULE_ENABLED
})
// this is currently not working with the command factory
// TODO: include the onWorkspaceRoleDeletedFactory listener service
const trx = await db.transaction()
const deleteWorkspaceRole = deleteWorkspaceRoleFactory({
deleteWorkspaceRole: repoDeleteWorkspaceRoleFactory({ db: trx }),
getWorkspaceRoles: getWorkspaceRolesFactory({ db: trx }),
emitWorkspaceEvent: getEventBus().emit
})
await withOperationLogging(
async () =>
await withTransaction(
deleteWorkspaceRole({ workspaceId, userId: context.userId! }),
trx
async ({ db: trx }) => {
const deleteWorkspaceRole = deleteWorkspaceRoleFactory({
deleteWorkspaceRole: repoDeleteWorkspaceRoleFactory({ db: trx }),
getWorkspaceRoles: getWorkspaceRolesFactory({ db: trx }),
emitWorkspaceEvent: getEventBus().emit
})
return await deleteWorkspaceRole({
workspaceId,
userId: context.userId!
})
},
{ db }
),
{
logger,
+88 -76
View File
@@ -257,87 +257,99 @@ export const getSsoRouter = (): Router => {
query: oidcCallbackRequestQuery
}),
async (req, res, next) => {
const trx = await db.transaction()
const handleOidcCallback = handleOidcCallbackFactory({
getWorkspaceRoles: getWorkspaceRolesFactory({ db: trx }),
getWorkspaceBySlug: getWorkspaceBySlugFactory({ db: trx }),
createOidcProvider: createOidcProviderFactory({
getOIDCProviderValidationRequest: getOIDCProviderValidationRequestFactory({
redis: getGenericRedis(),
decrypt: getDecryptor()
}),
saveSsoProviderRegistration: saveSsoProviderRegistrationFactory({
getWorkspaceSsoProvider: getWorkspaceSsoProviderFactory({
db: trx,
decrypt: getDecryptor()
}),
storeProviderRecord: storeSsoProviderRecordFactory({
db: trx,
encrypt: getEncryptor()
}),
associateSsoProviderWithWorkspace: associateSsoProviderWithWorkspaceFactory(
{
db: trx
}
)
})
}),
getOidcProvider: getOidcProviderFactory({
getWorkspaceSsoProvider: getWorkspaceSsoProviderFactory({
db: trx,
decrypt: getDecryptor()
})
}),
getOidcProviderUserData: getOidcProviderUserDataFactory(),
tryGetSpeckleUserData: tryGetSpeckleUserDataFactory({
findEmail: findEmailFactory({ db: trx }),
getUser: getUserFactory({ db: trx }),
getUserEmails: findEmailsByUserIdFactory({ db: trx })
}),
createWorkspaceUserFromSsoProfile: createWorkspaceUserFromSsoProfileFactory({
createUser: createUserFactory({
getServerInfo: getServerInfoFactory({ db: trx }),
findEmail: findEmailFactory({ db: trx }),
storeUser: storeUserFactory({ db: trx }),
countAdminUsers: countAdminUsersFactory({ db: trx }),
storeUserAcl: storeUserAclFactory({ db: trx }),
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
createUserEmail: createUserEmailFactory({ db: trx }),
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({
db: trx
try {
await withTransaction(
async ({ db: trx }) => {
const handleOidcCallback = handleOidcCallbackFactory({
getWorkspaceRoles: getWorkspaceRolesFactory({ db: trx }),
getWorkspaceBySlug: getWorkspaceBySlugFactory({ db: trx }),
createOidcProvider: createOidcProviderFactory({
getOIDCProviderValidationRequest:
getOIDCProviderValidationRequestFactory({
redis: getGenericRedis(),
decrypt: getDecryptor()
}),
saveSsoProviderRegistration: saveSsoProviderRegistrationFactory({
getWorkspaceSsoProvider: getWorkspaceSsoProviderFactory({
db: trx,
decrypt: getDecryptor()
}),
storeProviderRecord: storeSsoProviderRecordFactory({
db: trx,
encrypt: getEncryptor()
}),
associateSsoProviderWithWorkspace:
associateSsoProviderWithWorkspaceFactory({
db: trx
})
})
}),
findEmail: findEmailFactory({ db: trx }),
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db: trx }),
updateAllInviteTargets: updateAllInviteTargetsFactory({ db: trx })
getOidcProvider: getOidcProviderFactory({
getWorkspaceSsoProvider: getWorkspaceSsoProviderFactory({
db: trx,
decrypt: getDecryptor()
})
}),
requestNewEmailVerification: requestNewEmailVerificationFactory({
getOidcProviderUserData: getOidcProviderUserDataFactory(),
tryGetSpeckleUserData: tryGetSpeckleUserDataFactory({
findEmail: findEmailFactory({ db: trx }),
getUser: getUserFactory({ db: trx }),
getServerInfo: getServerInfoFactory({ db: trx }),
deleteOldAndInsertNewVerification:
deleteOldAndInsertNewVerificationFactory({ db: trx }),
renderEmail,
sendEmail
})
}),
emitEvent: getEventBus().emit
}),
upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db: trx }),
findInvite: findInviteFactory({ db: trx }),
deleteInvite: deleteInviteFactory({ db: trx })
}),
linkUserWithSsoProvider: linkUserWithSsoProviderFactory({
findEmailsByUserId: findEmailsByUserIdFactory({ db: trx }),
createUserEmail: createUserEmailFactory({ db: trx }),
updateUserEmail: updateUserEmailFactory({ db: trx }),
logger: req.log
}),
upsertUserSsoSession: upsertUserSsoSessionFactory({ db: trx })
})
getUserEmails: findEmailsByUserIdFactory({ db: trx })
}),
createWorkspaceUserFromSsoProfile:
createWorkspaceUserFromSsoProfileFactory({
createUser: createUserFactory({
getServerInfo: getServerInfoFactory({ db: trx }),
findEmail: findEmailFactory({ db: trx }),
storeUser: storeUserFactory({ db: trx }),
countAdminUsers: countAdminUsersFactory({ db: trx }),
storeUserAcl: storeUserAclFactory({ db: trx }),
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
createUserEmail: createUserEmailFactory({ db: trx }),
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({
db: trx
}),
findEmail: findEmailFactory({ db: trx }),
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({
db: trx
}),
updateAllInviteTargets: updateAllInviteTargetsFactory({
db: trx
})
}),
requestNewEmailVerification: requestNewEmailVerificationFactory({
findEmail: findEmailFactory({ db: trx }),
getUser: getUserFactory({ db: trx }),
getServerInfo: getServerInfoFactory({ db: trx }),
deleteOldAndInsertNewVerification:
deleteOldAndInsertNewVerificationFactory({
db: trx
}),
renderEmail,
sendEmail
})
}),
emitEvent: getEventBus().emit
}),
upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db: trx }),
findInvite: findInviteFactory({ db: trx }),
deleteInvite: deleteInviteFactory({ db: trx })
}),
linkUserWithSsoProvider: linkUserWithSsoProviderFactory({
findEmailsByUserId: findEmailsByUserIdFactory({ db: trx }),
createUserEmail: createUserEmailFactory({ db: trx }),
updateUserEmail: updateUserEmailFactory({ db: trx }),
logger: req.log
}),
upsertUserSsoSession: upsertUserSsoSessionFactory({ db: trx })
})
await handleOidcCallback(req, res, next)
},
{ db }
)
try {
await withTransaction(handleOidcCallback(req, res, next), trx)
return next()
} catch (e) {
const errorMessage = getErrorMessage(e)