From 573970fc6c944b9f86e10bd159f8ef85a428c472 Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Tue, 15 Oct 2024 12:55:33 +0300 Subject: [PATCH 1/2] chore(server): core IoC #55 - createUserFactory --- .../activitystream/tests/activity.spec.js | 62 +++++- packages/server/modules/auth/index.ts | 51 ++++- .../server/modules/auth/strategies/local.ts | 9 +- .../modules/auth/tests/apps.graphql.spec.js | 61 +++++- .../server/modules/auth/tests/apps.spec.js | 62 +++++- .../server/modules/auth/tests/auth.spec.js | 64 +++++- .../tests/blobstorage.graph.spec.js | 63 +++++- .../tests/blobstorage.integration.spec.js | 63 +++++- .../comments/tests/comments.graph.spec.js | 63 +++++- .../modules/comments/tests/comments.spec.js | 63 +++++- .../modules/core/domain/users/operations.ts | 25 ++- .../modules/core/events/usersEmitter.ts | 2 + .../server/modules/core/repositories/users.ts | 37 +++- .../server/modules/core/services/users.js | 203 ++++++++++-------- .../modules/core/services/users/management.ts | 113 +++++++++- .../modules/core/tests/branches.spec.js | 63 +++++- .../server/modules/core/tests/commits.spec.js | 63 +++++- .../core/tests/favoriteStreams.spec.js | 63 +++++- .../server/modules/core/tests/generic.spec.js | 63 +++++- .../server/modules/core/tests/graph.spec.js | 61 +++++- .../modules/core/tests/graphSubs.spec.js | 62 +++++- .../core/tests/integration/createUser.spec.ts | 56 ++++- .../integration/emailVerification.spec.ts | 50 ++++- .../core/tests/integration/findUsers.spec.ts | 54 ++++- .../core/tests/integration/updateUser.spec.ts | 55 ++++- .../integration/userEmails.graph.spec.ts | 21 +- .../core/tests/integration/userEmails.spec.ts | 19 +- .../server/modules/core/tests/objects.spec.js | 63 +++++- .../server/modules/core/tests/rest.spec.js | 63 +++++- .../server/modules/core/tests/users.spec.js | 60 +++++- .../modules/core/tests/usersAdmin.spec.js | 62 +++++- .../modules/core/tests/usersAdminList.spec.ts | 61 +++++- .../modules/core/tests/usersGraphql.spec.ts | 24 ++- .../tests/fileuploads.integration.spec.ts | 55 ++++- .../modules/pwdreset/tests/pwdrest.spec.js | 62 +++++- .../server/modules/stats/tests/stats.spec.ts | 54 ++++- .../modules/webhooks/tests/cleanup.spec.ts | 54 ++++- .../modules/webhooks/tests/webhooks.spec.js | 61 +++++- packages/server/scripts/seedUsers.js | 62 +++++- packages/server/test/authHelper.ts | 54 ++++- 40 files changed, 2118 insertions(+), 188 deletions(-) diff --git a/packages/server/modules/activitystream/tests/activity.spec.js b/packages/server/modules/activitystream/tests/activity.spec.js index 0806949c2..c427d9ec7 100644 --- a/packages/server/modules/activitystream/tests/activity.spec.js +++ b/packages/server/modules/activitystream/tests/activity.spec.js @@ -1,7 +1,6 @@ /* istanbul ignore file */ const expect = require('chai').expect -const { createUser } = require('../../core/services/users') const { createPersonalAccessToken } = require('../../core/services/tokens') const { createObject } = require('../../core/services/objects') @@ -25,7 +24,38 @@ const { addStreamPermissionsAddedActivityFactory } = require('@/modules/activitystream/services/streamActivity') const { publish } = require('@/modules/shared/utils/subscriptions') -const { getUserFactory } = require('@/modules/core/repositories/users') +const { + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const getUser = getUserFactory({ db }) const getUserActivity = getUserActivityFactory({ db }) @@ -45,6 +75,34 @@ const addOrUpdateStreamCollaborator = addOrUpdateStreamCollaboratorFactory({ }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + let sendRequest describe('Activity @activity', () => { diff --git a/packages/server/modules/auth/index.ts b/packages/server/modules/auth/index.ts index b55c42f23..7ffc4fc18 100644 --- a/packages/server/modules/auth/index.ts +++ b/packages/server/modules/auth/index.ts @@ -17,8 +17,7 @@ import { getServerInfo } from '@/modules/core/services/generic' import { getUserByEmail, findOrCreateUser, - validatePasssword, - createUser + validatePasssword } from '@/modules/core/services/users' import { validateServerInviteFactory, @@ -39,7 +38,53 @@ import localStrategyBuilderFactory from '@/modules/auth/strategies/local' import oidcStrategyBuilderFactory from '@/modules/auth/strategies/oidc' import { getRateLimitResult } from '@/modules/core/services/ratelimiter' import { passportAuthenticateHandlerBuilderFactory } from '@/modules/auth/services/passportService' -import { legacyGetUserFactory } from '@/modules/core/repositories/users' +import { + countAdminUsersFactory, + getUserFactory, + legacyGetUserFactory, + storeUserAclFactory, + storeUserFactory +} from '@/modules/core/repositories/users' +import { createUserFactory } from '@/modules/core/services/users/management' +import { + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory, + findEmailFactory +} from '@/modules/core/repositories/userEmails' +import { UsersEmitter } from '@/modules/core/events/usersEmitter' +import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails' +import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request' +import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories' +import { renderEmail } from '@/modules/emails/services/emailRendering' +import { sendEmail } from '@/modules/emails/services/sending' + +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) const initializeDefaultApps = initializeDefaultAppsFactory({ getAllScopes: getAllScopesFactory({ db }), diff --git a/packages/server/modules/auth/strategies/local.ts b/packages/server/modules/auth/strategies/local.ts index d53b5ba9e..aec345d8c 100644 --- a/packages/server/modules/auth/strategies/local.ts +++ b/packages/server/modules/auth/strategies/local.ts @@ -1,8 +1,4 @@ -import { - createUser, - validatePasssword, - getUserByEmail -} from '@/modules/core/services/users' +import { validatePasssword, getUserByEmail } from '@/modules/core/services/users' import { getServerInfo } from '@/modules/core/services/generic' import { sendRateLimitResponse, @@ -23,6 +19,7 @@ import { ResolveAuthRedirectPath, ValidateServerInvite } from '@/modules/serverinvites/services/operations' +import { CreateValidatedUser } from '@/modules/core/domain/users/operations' const localStrategyBuilderFactory = (deps: { @@ -31,7 +28,7 @@ const localStrategyBuilderFactory = getServerInfo: typeof getServerInfo getRateLimitResult: typeof getRateLimitResult validateServerInvite: ValidateServerInvite - createUser: typeof createUser + createUser: CreateValidatedUser finalizeInvitedServerRegistration: FinalizeInvitedServerRegistration resolveAuthRedirectPath: ResolveAuthRedirectPath }): AuthStrategyBuilder => diff --git a/packages/server/modules/auth/tests/apps.graphql.spec.js b/packages/server/modules/auth/tests/apps.graphql.spec.js index 87f0eb973..e77d839b7 100644 --- a/packages/server/modules/auth/tests/apps.graphql.spec.js +++ b/packages/server/modules/auth/tests/apps.graphql.spec.js @@ -4,7 +4,6 @@ const chai = require('chai') const expect = chai.expect -const { createUser } = require('@/modules/core/services/users') const { createPersonalAccessToken, createAppToken, @@ -23,6 +22,38 @@ const { db } = require('@/db/knex') const { createAppTokenFromAccessCodeFactory } = require('@/modules/auth/services/serverApps') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') let sendRequest let server @@ -38,6 +69,34 @@ const createAppTokenFromAccessCode = createAppTokenFromAccessCodeFactory({ createBareToken }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + describe('GraphQL @apps-api', () => { let testUser let testUser2 diff --git a/packages/server/modules/auth/tests/apps.spec.js b/packages/server/modules/auth/tests/apps.spec.js index c673bc422..d4cd69579 100644 --- a/packages/server/modules/auth/tests/apps.spec.js +++ b/packages/server/modules/auth/tests/apps.spec.js @@ -1,7 +1,6 @@ /* istanbul ignore file */ const expect = require('chai').expect -const { createUser } = require(`@/modules/core/services/users`) const { validateToken, createAppToken, @@ -31,7 +30,40 @@ const { createAppTokenFromAccessCodeFactory, refreshAppTokenFactory } = require('@/modules/auth/services/serverApps') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') +const db = knex const getApp = getAppFactory({ db: knex }) const updateDefaultApp = updateDefaultAppFactory({ db: knex }) const getAllPublicApps = getAllPublicAppsFactory({ db: knex }) @@ -62,6 +94,34 @@ const refreshAppToken = refreshAppTokenFactory({ createBareToken }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + describe('Services @apps-services', () => { const actor = { name: 'Dimitrie Stefanescu', diff --git a/packages/server/modules/auth/tests/auth.spec.js b/packages/server/modules/auth/tests/auth.spec.js index 00debf94f..7d93f366b 100644 --- a/packages/server/modules/auth/tests/auth.spec.js +++ b/packages/server/modules/auth/tests/auth.spec.js @@ -1,9 +1,8 @@ const crs = require('crypto-random-string') const chai = require('chai') const request = require('supertest') -const { createUser } = require('@/modules/core/services/users') -const { updateServerInfo } = require('@/modules/core/services/generic') +const { updateServerInfo, getServerInfo } = require('@/modules/core/services/generic') const { getUserByEmail } = require('@/modules/core/services/users') const { TIME } = require('@speckle/shared') const { RATE_LIMITERS, createConsumer } = require('@/modules/core/services/ratelimiter') @@ -13,7 +12,9 @@ const { RateLimiterMemory } = require('rate-limiter-flexible') const { findInviteFactory, findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory } = require('@/modules/serverinvites/repositories/serverInvites') const db = require('@/db/knex') const { @@ -44,7 +45,34 @@ const { } = require('@/modules/activitystream/services/streamActivity') const { saveActivityFactory } = require('@/modules/activitystream/repositories') const { publish } = require('@/modules/shared/utils/subscriptions') -const { getUsersFactory, getUserFactory } = require('@/modules/core/repositories/users') +const { + getUsersFactory, + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const getUser = getUserFactory({ db }) const getUsers = getUsersFactory({ db }) @@ -83,6 +111,34 @@ const createStream = legacyCreateStreamFactory({ }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + const expect = chai.expect let app diff --git a/packages/server/modules/blobstorage/tests/blobstorage.graph.spec.js b/packages/server/modules/blobstorage/tests/blobstorage.graph.spec.js index 303244843..6e1b74ece 100644 --- a/packages/server/modules/blobstorage/tests/blobstorage.graph.spec.js +++ b/packages/server/modules/blobstorage/tests/blobstorage.graph.spec.js @@ -1,6 +1,5 @@ const { buildApolloServer } = require('@/app') const { truncateTables } = require('@/test/hooks') -const { createUser } = require('@/modules/core/services/users') const { gql } = require('graphql-tag') const { createBlobs } = require('@/modules/blobstorage/tests/helpers') const { expect } = require('chai') @@ -23,7 +22,9 @@ const { } = require('@/modules/serverinvites/services/creation') const { findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory } = require('@/modules/serverinvites/repositories/serverInvites') const { collectAndValidateCoreTargetsFactory @@ -39,7 +40,35 @@ const { } = require('@/modules/activitystream/services/streamActivity') const { saveActivityFactory } = require('@/modules/activitystream/repositories') const { publish } = require('@/modules/shared/utils/subscriptions') -const { getUsersFactory, getUserFactory } = require('@/modules/core/repositories/users') +const { + getUsersFactory, + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const getUser = getUserFactory({ db }) const getUsers = getUsersFactory({ db }) @@ -76,6 +105,34 @@ const createStream = legacyCreateStreamFactory({ }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + describe('Blobs graphql @blobstorage', () => { /** @type {import('@/test/graphqlHelper').ServerAndContext} */ let graphqlServer diff --git a/packages/server/modules/blobstorage/tests/blobstorage.integration.spec.js b/packages/server/modules/blobstorage/tests/blobstorage.integration.spec.js index 00ae31018..b791d3257 100644 --- a/packages/server/modules/blobstorage/tests/blobstorage.integration.spec.js +++ b/packages/server/modules/blobstorage/tests/blobstorage.integration.spec.js @@ -3,7 +3,6 @@ const request = require('supertest') const expect = require('chai').expect const { beforeEachContext } = require('@/test/hooks') const { createToken } = require('@/modules/core/services/tokens') -const { createUser } = require('@/modules/core/services/users') const { Scopes } = require('@/modules/core/helpers/mainConstants') const { getStreamFactory, @@ -22,7 +21,9 @@ const { } = require('@/modules/serverinvites/services/creation') const { findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory } = require('@/modules/serverinvites/repositories/serverInvites') const { collectAndValidateCoreTargetsFactory @@ -38,7 +39,35 @@ const { } = require('@/modules/activitystream/services/streamActivity') const { saveActivityFactory } = require('@/modules/activitystream/repositories') const { publish } = require('@/modules/shared/utils/subscriptions') -const { getUsersFactory, getUserFactory } = require('@/modules/core/repositories/users') +const { + getUsersFactory, + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const getUser = getUserFactory({ db }) const getUsers = getUsersFactory({ db }) @@ -75,6 +104,34 @@ const createStream = legacyCreateStreamFactory({ }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + describe('Blobs integration @blobstorage', () => { let app let token diff --git a/packages/server/modules/comments/tests/comments.graph.spec.js b/packages/server/modules/comments/tests/comments.graph.spec.js index 21c074ce6..80087275e 100644 --- a/packages/server/modules/comments/tests/comments.graph.spec.js +++ b/packages/server/modules/comments/tests/comments.graph.spec.js @@ -4,7 +4,6 @@ const crs = require('crypto-random-string') const { buildApolloServer } = require('@/app') const { beforeEachContext } = require('@/test/hooks') const { Roles } = require('@/modules/core/helpers/mainConstants') -const { createUser } = require('@/modules/core/services/users') const { gql } = require('graphql-tag') const { createObject } = require('@/modules/core/services/objects') const { @@ -69,7 +68,9 @@ const { } = require('@/modules/serverinvites/services/creation') const { findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory } = require('@/modules/serverinvites/repositories/serverInvites') const { collectAndValidateCoreTargetsFactory @@ -87,7 +88,35 @@ const { publish } = require('@/modules/shared/utils/subscriptions') const { addCommitCreatedActivityFactory } = require('@/modules/activitystream/services/commitActivity') -const { getUsersFactory, getUserFactory } = require('@/modules/core/repositories/users') +const { + getUsersFactory, + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { + findEmailFactory, + ensureNoPrimaryEmailForUserFactory, + createUserEmailFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const getUser = getUserFactory({ db }) const getUsers = getUsersFactory({ db }) @@ -168,6 +197,34 @@ const updateStream = legacyUpdateStreamFactory({ }) const grantPermissionsStream = grantStreamPermissionsFactory({ db }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + function buildCommentInputFromString(textString) { return convertBasicStringToDocument(textString) } diff --git a/packages/server/modules/comments/tests/comments.spec.js b/packages/server/modules/comments/tests/comments.spec.js index 1c97dd48d..bb670f969 100644 --- a/packages/server/modules/comments/tests/comments.spec.js +++ b/packages/server/modules/comments/tests/comments.spec.js @@ -3,7 +3,6 @@ const { packageRoot } = require('@/bootstrap') const expect = require('chai').expect const crs = require('crypto-random-string') const { beforeEachContext, truncateTables } = require('@/test/hooks') -const { createUser } = require('@/modules/core/services/users') const { createObject } = require('@/modules/core/services/objects') const { @@ -88,7 +87,9 @@ const { } = require('@/modules/serverinvites/services/creation') const { findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory } = require('@/modules/serverinvites/repositories/serverInvites') const { collectAndValidateCoreTargetsFactory @@ -106,7 +107,35 @@ const { publish } = require('@/modules/shared/utils/subscriptions') const { addCommitCreatedActivityFactory } = require('@/modules/activitystream/services/commitActivity') -const { getUsersFactory, getUserFactory } = require('@/modules/core/repositories/users') +const { + getUsersFactory, + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const getUser = getUserFactory({ db }) const getUsers = getUsersFactory({ db }) @@ -211,6 +240,34 @@ const createStream = legacyCreateStreamFactory({ }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + function buildCommentInputFromString(textString) { return convertBasicStringToDocument(textString) } diff --git a/packages/server/modules/core/domain/users/operations.ts b/packages/server/modules/core/domain/users/operations.ts index 42c9e310b..3658d7d1d 100644 --- a/packages/server/modules/core/domain/users/operations.ts +++ b/packages/server/modules/core/domain/users/operations.ts @@ -1,5 +1,6 @@ import { User, UserWithOptionalRole } from '@/modules/core/domain/users/types' -import { Nullable } from '@speckle/shared' +import { ServerAclRecord } from '@/modules/core/helpers/types' +import { Nullable, NullableKeysToOptional, ServerRoles } from '@speckle/shared' export type GetUserParams = Partial<{ /** @@ -23,6 +24,16 @@ export type GetUser = ( params?: GetUserParams ) => Promise> +export type StoreUser = (params: { + user: Omit, 'suuid' | 'createdAt'> +}) => Promise + +export type CountAdminUsers = () => Promise + +export type StoreUserAcl = (params: { + acl: ServerAclRecord +}) => Promise + export type LegacyGetUser = (id: string) => Promise export type LegacyGetPaginatedUsers = ( @@ -34,3 +45,15 @@ export type LegacyGetPaginatedUsers = ( export type LegacyGetPaginatedUsersCount = ( searchQuery?: string | null ) => Promise + +export type CreateValidatedUser = ( + user: NullableKeysToOptional> & { + email: string + verified?: boolean + password?: string + role?: ServerRoles + }, + options?: Partial<{ + skipPropertyValidation: boolean + }> +) => Promise diff --git a/packages/server/modules/core/events/usersEmitter.ts b/packages/server/modules/core/events/usersEmitter.ts index aedd79655..24ca25349 100644 --- a/packages/server/modules/core/events/usersEmitter.ts +++ b/packages/server/modules/core/events/usersEmitter.ts @@ -15,3 +15,5 @@ const { emit, listen } = initializeModuleEventEmitter({ }) export const UsersEmitter = { emit, listen, events: UsersEvents } +export type UsersEventsEmitter = (typeof UsersEmitter)['emit'] +export type UsersEventsListener = (typeof UsersEmitter)['listen'] diff --git a/packages/server/modules/core/repositories/users.ts b/packages/server/modules/core/repositories/users.ts index bcdfbe18e..fc4ee7369 100644 --- a/packages/server/modules/core/repositories/users.ts +++ b/packages/server/modules/core/repositories/users.ts @@ -1,5 +1,5 @@ import { ServerAcl, UserEmails, Users, knex } from '@/modules/core/dbSchema' -import { UserRecord, UserWithRole } from '@/modules/core/helpers/types' +import { ServerAclRecord, UserRecord, UserWithRole } from '@/modules/core/helpers/types' import { Nullable } from '@/modules/shared/helpers/typeHelper' import { clamp, isArray, omit } from 'lodash' import { metaHelpers } from '@/modules/core/helpers/meta' @@ -11,17 +11,21 @@ import { db } from '@/db/knex' import { markUserEmailAsVerifiedFactory } from '@/modules/core/services/users/emailVerification' import { UserWithOptionalRole } from '@/modules/core/domain/users/types' import { + CountAdminUsers, GetUser, GetUserParams, GetUsers, LegacyGetPaginatedUsers, LegacyGetPaginatedUsersCount, - LegacyGetUser + LegacyGetUser, + StoreUser, + StoreUserAcl } from '@/modules/core/domain/users/operations' export type { UserWithOptionalRole, GetUserParams } const tables = { - users: (db: Knex) => db(Users.name) + users: (db: Knex) => db(Users.name), + serverAcl: (db: Knex) => db(ServerAcl.name) } function sanitizeUserRecord>(user: T): T { @@ -323,3 +327,30 @@ export const legacyGetPaginatedUsersCount = const [userCount] = await getUsersBaseQuery(query, { searchQuery }).count() return parseInt(userCount.count) } + +export const storeUserFactory = + (deps: { db: Knex }): StoreUser => + async (params) => { + const { user } = params + const [newUser] = await tables.users(deps.db).insert(user, '*') + return newUser + } + +export const countAdminUsersFactory = + (deps: { db: Knex }): CountAdminUsers => + async () => { + const [{ count }] = await tables + .serverAcl(deps.db) + .where({ role: Roles.Server.Admin }) + .count() + + return parseInt(count as string) + } + +export const storeUserAclFactory = + (deps: { db: Knex }): StoreUserAcl => + async (params) => { + const { acl } = params + const [newAcl] = await tables.serverAcl(deps.db).insert(acl, '*') + return newAcl + } diff --git a/packages/server/modules/core/services/users.js b/packages/server/modules/core/services/users.js index bb45c56af..1a46ebfe9 100644 --- a/packages/server/modules/core/services/users.js +++ b/packages/server/modules/core/services/users.js @@ -10,34 +10,49 @@ const { const { validateUserPassword, updateUserAndNotify, - MINIMUM_PASSWORD_LENGTH + MINIMUM_PASSWORD_LENGTH, + createUserFactory } = require('@/modules/core/services/users/management') const Users = () => UsersSchema.knex() const Acl = () => ServerAclSchema.knex() const { LIMITED_USER_FIELDS } = require('@/modules/core/helpers/userHelper') -const { getUserByEmail, getUserFactory } = require('@/modules/core/repositories/users') -const { UsersEmitter, UsersEvents } = require('@/modules/core/events/usersEmitter') -const { pick, omit } = require('lodash') +const { + getUserByEmail, + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { omit } = require('lodash') const { dbLogger } = require('@/logging/logging') const { UserInputError, PasswordTooShortError } = require('@/modules/core/errors/userinput') const { Roles } = require('@speckle/shared') -const { getServerInfo } = require('@/modules/core/services/generic') -const { sanitizeImageUrl } = require('@/modules/shared/helpers/sanitization') const { - createUserEmailFactory, findPrimaryEmailForUserFactory, findEmailFactory, + createUserEmailFactory, ensureNoPrimaryEmailForUserFactory } = require('@/modules/core/repositories/userEmails') +const { db } = require('@/db/knex') + +const { deleteStreamFactory } = require('@/modules/core/repositories/streams') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') const { validateAndCreateUserEmailFactory } = require('@/modules/core/services/userEmails') -const { db } = require('@/db/knex') const { finalizeInvitedServerRegistrationFactory } = require('@/modules/serverinvites/services/processing') @@ -45,15 +60,7 @@ const { deleteServerOnlyInvitesFactory, updateAllInviteTargetsFactory } = require('@/modules/serverinvites/repositories/serverInvites') -const { - requestNewEmailVerificationFactory -} = require('@/modules/emails/services/verification/request') -const { - deleteOldAndInsertNewVerificationFactory -} = require('@/modules/emails/repositories') -const { renderEmail } = require('@/modules/emails/services/emailRendering') -const { sendEmail } = require('@/modules/emails/services/sending') -const { deleteStreamFactory } = require('@/modules/core/repositories/streams') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const _changeUserRole = async ({ userId, role }) => await Acl().where({ userId }).update({ role }) @@ -71,101 +78,119 @@ const _ensureAtleastOneAdminRemains = async (userId) => { } } -const getUser = getUserFactory({ db }) +const findEmail = findEmailFactory({ db }) const requestNewEmailVerification = requestNewEmailVerificationFactory({ - findEmail: findEmailFactory({ db }), - getUser, + findEmail, + getUser: getUserFactory({ db }), getServerInfo, deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), renderEmail, sendEmail }) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) module.exports = { - /* - Users - */ + // /* + // Users + // */ - /** - * @param {{}} user - * @param {{skipPropertyValidation: boolean } | undefined} options - * @returns {Promise} - */ - async createUser(user, options = undefined) { - // ONLY ALLOW SKIPPING WHEN CREATING USERS FOR TESTS, IT'S UNSAFE OTHERWISE - const { skipPropertyValidation = false } = options || {} + // /** + // * @param {{}} user + // * @param {{skipPropertyValidation: boolean } | undefined} options + // * @returns {Promise} + // */ + // async createUser(user, options = undefined) { + // // ONLY ALLOW SKIPPING WHEN CREATING USERS FOR TESTS, IT'S UNSAFE OTHERWISE + // const { skipPropertyValidation = false } = options || {} - if (!user.email?.length) throw new UserInputError('E-mail address is required') + // if (!user.email?.length) throw new UserInputError('E-mail address is required') - let expectedRole = null - if (user.role) { - const isValidRole = Object.values(Roles.Server).includes(user.role) - const isValidIfGuestModeEnabled = - user.role !== Roles.Server.Guest || (await getServerInfo()).guestModeEnabled - expectedRole = isValidRole && isValidIfGuestModeEnabled ? user.role : null - } - delete user.role + // let expectedRole = null + // if (user.role) { + // const isValidRole = Object.values(Roles.Server).includes(user.role) + // const isValidIfGuestModeEnabled = + // user.role !== Roles.Server.Guest || (await getServerInfo()).guestModeEnabled + // expectedRole = isValidRole && isValidIfGuestModeEnabled ? user.role : null + // } + // delete user.role - user = skipPropertyValidation - ? user - : pick(user, ['id', 'bio', 'email', 'password', 'name', 'company', 'verified']) + // user = skipPropertyValidation + // ? user + // : pick(user, ['id', 'bio', 'email', 'password', 'name', 'company', 'verified']) - const newId = crs({ length: 10 }) - user.id = newId - user.email = user.email.toLowerCase() + // const newId = crs({ length: 10 }) + // user.id = newId + // user.email = user.email.toLowerCase() - if (!user.name) throw new UserInputError('User name is required') + // if (!user.name) throw new UserInputError('User name is required') - if (user.avatar) { - user.avatar = sanitizeImageUrl(user.avatar) - } + // if (user.avatar) { + // user.avatar = sanitizeImageUrl(user.avatar) + // } - if (user.password) { - if (user.password.length < MINIMUM_PASSWORD_LENGTH) - throw new PasswordTooShortError(MINIMUM_PASSWORD_LENGTH) - user.passwordDigest = await bcrypt.hash(user.password, 10) - } - delete user.password + // if (user.password) { + // if (user.password.length < MINIMUM_PASSWORD_LENGTH) + // throw new PasswordTooShortError(MINIMUM_PASSWORD_LENGTH) + // user.passwordDigest = await bcrypt.hash(user.password, 10) + // } + // delete user.password - const userEmail = await findEmailFactory({ db })({ - email: user.email - }) - if (userEmail) throw new UserInputError('Email taken. Try logging in?') + // const userEmail = await findEmailFactory({ db })({ + // email: user.email + // }) + // if (userEmail) throw new UserInputError('Email taken. Try logging in?') - const [newUser] = (await Users().insert(user, UsersSchema.cols)) || [] - if (!newUser) throw new Error("Couldn't create user") + // const [newUser] = (await Users().insert(user, UsersSchema.cols)) || [] + // if (!newUser) throw new Error("Couldn't create user") - const userRole = - (await countAdminUsers()) === 0 - ? Roles.Server.Admin - : expectedRole || Roles.Server.User + // const userRole = + // (await countAdminUsers()) === 0 + // ? Roles.Server.Admin + // : expectedRole || Roles.Server.User - await Acl().insert({ userId: newId, role: userRole }) + // await Acl().insert({ userId: newId, role: userRole }) - const validateAndCreateUserEmail = validateAndCreateUserEmailFactory({ - createUserEmail: createUserEmailFactory({ db }), - ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), - findEmail: findEmailFactory({ db }), - updateEmailInvites: finalizeInvitedServerRegistrationFactory({ - deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), - updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) - }), - requestNewEmailVerification - }) + // const validateAndCreateUserEmail = validateAndCreateUserEmailFactory({ + // createUserEmail: createUserEmailFactory({ db }), + // ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + // findEmail: findEmailFactory({ db }), + // updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + // deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + // updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + // }), + // requestNewEmailVerification + // }) - await validateAndCreateUserEmail({ - userEmail: { - email: user.email, - userId: user.id, - verified: user.verified, - primary: true - } - }) + // await validateAndCreateUserEmail({ + // userEmail: { + // email: user.email, + // userId: user.id, + // verified: user.verified, + // primary: true + // } + // }) - await UsersEmitter.emit(UsersEvents.Created, { user: newUser }) + // await UsersEmitter.emit(UsersEvents.Created, { user: newUser }) - return newUser.id - }, + // return newUser.id + // }, /** * @param {{user: {email: string, name?: string, role?: import('@speckle/shared').ServerRoles, bio?: string, verified?: boolean}}} param0 @@ -184,7 +209,7 @@ module.exports = { user.password = crs({ length: 20 }) user.verified = true // because we trust the external identity provider, no? return { - id: await module.exports.createUser(user), + id: await createUser(user), email: user.email, isNewUser: true } diff --git a/packages/server/modules/core/services/users/management.ts b/packages/server/modules/core/services/users/management.ts index 308beede9..ae70820d6 100644 --- a/packages/server/modules/core/services/users/management.ts +++ b/packages/server/modules/core/services/users/management.ts @@ -1,14 +1,28 @@ import { db } from '@/db/knex' import { saveActivityFactory } from '@/modules/activitystream/repositories' import { addUserUpdatedActivityFactory } from '@/modules/activitystream/services/userActivity' +import { + CountAdminUsers, + CreateValidatedUser, + StoreUser, + StoreUserAcl +} from '@/modules/core/domain/users/operations' import { UserUpdateError, UserValidationError } from '@/modules/core/errors/user' -import { PasswordTooShortError } from '@/modules/core/errors/userinput' +import { PasswordTooShortError, UserInputError } from '@/modules/core/errors/userinput' import { UserUpdateInput } from '@/modules/core/graph/generated/graphql' import type { UserRecord } from '@/modules/core/helpers/userHelper' import { getUserFactory, updateUser } from '@/modules/core/repositories/users' +import { getServerInfo } from '@/modules/core/services/generic' import { sanitizeImageUrl } from '@/modules/shared/helpers/sanitization' -import { isNullOrUndefined } from '@speckle/shared' +import { isNullOrUndefined, NullableKeysToOptional, Roles } from '@speckle/shared' +import { pick } from 'lodash' import bcrypt from 'bcrypt' +import crs from 'crypto-random-string' +import { + FindEmail, + ValidateAndCreateUserEmail +} from '@/modules/core/domain/userEmails/operations' +import { UsersEvents, UsersEventsEmitter } from '@/modules/core/events/usersEmitter' export const MINIMUM_PASSWORD_LENGTH = 8 @@ -99,3 +113,98 @@ export async function changePassword( { skipClean: true } ) } + +export const createUserFactory = + (deps: { + getServerInfo: typeof getServerInfo + findEmail: FindEmail + storeUser: StoreUser + countAdminUsers: CountAdminUsers + storeUserAcl: StoreUserAcl + validateAndCreateUserEmail: ValidateAndCreateUserEmail + usersEventsEmitter: UsersEventsEmitter + }): CreateValidatedUser => + async (user, options = undefined) => { + // ONLY ALLOW SKIPPING WHEN CREATING USERS FOR TESTS, IT'S UNSAFE OTHERWISE + const { skipPropertyValidation = false } = options || {} + + let finalUser: typeof user & + Omit, 'suuid' | 'createdAt'> = { + ...user, + id: crs({ length: 10 }), + verified: user.verified || false + } + + if (!finalUser.email?.length) throw new UserInputError('E-mail address is required') + + let expectedRole = null + if (finalUser.role) { + const isValidRole = Object.values(Roles.Server).includes(finalUser.role) + const isValidIfGuestModeEnabled = + finalUser.role !== Roles.Server.Guest || + (await deps.getServerInfo()).guestModeEnabled + expectedRole = isValidRole && isValidIfGuestModeEnabled ? finalUser.role : null + } + delete finalUser.role + + finalUser = skipPropertyValidation + ? finalUser + : (pick(finalUser, [ + 'id', + 'bio', + 'email', + 'password', + 'name', + 'company', + 'verified', + 'avatar' + ]) as typeof finalUser) + + finalUser.email = finalUser.email.toLowerCase() + + if (!finalUser.name) throw new UserInputError('User name is required') + + if (finalUser.avatar) { + finalUser.avatar = sanitizeImageUrl(user.avatar) + } + + if (finalUser.password) { + if (finalUser.password.length < MINIMUM_PASSWORD_LENGTH) + throw new PasswordTooShortError(MINIMUM_PASSWORD_LENGTH) + finalUser.passwordDigest = await bcrypt.hash(finalUser.password, 10) + } + delete finalUser.password + + const userEmail = await deps.findEmail({ + email: finalUser.email + }) + if (userEmail) throw new UserInputError('Email taken. Try logging in?') + + const newUser = await deps.storeUser({ user: finalUser }) + if (!newUser) throw new Error("Couldn't create user") + + const userRole = + (await deps.countAdminUsers()) === 0 + ? Roles.Server.Admin + : expectedRole || Roles.Server.User + + await deps.storeUserAcl({ + acl: { + userId: finalUser.id, + role: userRole + } + }) + + await deps.validateAndCreateUserEmail({ + userEmail: { + email: finalUser.email, + userId: finalUser.id, + verified: finalUser.verified, + primary: true + } + }) + + await deps.usersEventsEmitter(UsersEvents.Created, { user: newUser }) + + return newUser.id + } diff --git a/packages/server/modules/core/tests/branches.spec.js b/packages/server/modules/core/tests/branches.spec.js index 4ac0aec6d..1f831bbb2 100644 --- a/packages/server/modules/core/tests/branches.spec.js +++ b/packages/server/modules/core/tests/branches.spec.js @@ -8,7 +8,6 @@ const { sleep } = require('@/test/helpers') const expect = chai.expect const knex = require('@/db/knex') -const { createUser } = require('../services/users') const { createObject } = require('../services/objects') const { getBranchesByStreamId } = require('../services/branches') @@ -58,7 +57,9 @@ const { } = require('@/modules/serverinvites/services/creation') const { findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory } = require('@/modules/serverinvites/repositories/serverInvites') const { collectAndValidateCoreTargetsFactory @@ -76,7 +77,35 @@ const { publish } = require('@/modules/shared/utils/subscriptions') const { addCommitCreatedActivityFactory } = require('@/modules/activitystream/services/commitActivity') -const { getUsersFactory, getUserFactory } = require('@/modules/core/repositories/users') +const { + getUsersFactory, + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const db = knex const Commits = () => knex('commits') @@ -157,6 +186,34 @@ const createStream = legacyCreateStreamFactory({ }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + describe('Branches @core-branches', () => { const user = { name: 'Dimitrie Stefanescu', diff --git a/packages/server/modules/core/tests/commits.spec.js b/packages/server/modules/core/tests/commits.spec.js index bc7f3a921..598ab12c9 100644 --- a/packages/server/modules/core/tests/commits.spec.js +++ b/packages/server/modules/core/tests/commits.spec.js @@ -3,7 +3,6 @@ const expect = require('chai').expect const { beforeEachContext } = require('@/test/hooks') -const { createUser } = require('../services/users') const { createObject } = require('../services/objects') const { @@ -67,7 +66,9 @@ const { } = require('@/modules/serverinvites/services/creation') const { findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory } = require('@/modules/serverinvites/repositories/serverInvites') const { collectAndValidateCoreTargetsFactory @@ -82,7 +83,35 @@ const { } = require('@/modules/activitystream/services/streamActivity') const { saveActivityFactory } = require('@/modules/activitystream/repositories') const { publish } = require('@/modules/shared/utils/subscriptions') -const { getUsersFactory, getUserFactory } = require('@/modules/core/repositories/users') +const { + getUsersFactory, + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const getUser = getUserFactory({ db }) const getUsers = getUsersFactory({ db }) @@ -175,6 +204,34 @@ const createStream = legacyCreateStreamFactory({ }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + describe('Commits @core-commits', () => { const user = { name: 'Dimitrie Stefanescu', diff --git a/packages/server/modules/core/tests/favoriteStreams.spec.js b/packages/server/modules/core/tests/favoriteStreams.spec.js index b85f1ba2e..be5571367 100644 --- a/packages/server/modules/core/tests/favoriteStreams.spec.js +++ b/packages/server/modules/core/tests/favoriteStreams.spec.js @@ -3,7 +3,6 @@ const expect = require('chai').expect const { buildApolloServer } = require('@/app') const { StreamFavorites, Streams, Users } = require('@/modules/core/dbSchema') -const { createUser } = require('@/modules/core/services/users') const { truncateTables } = require('@/test/hooks') const { gql } = require('graphql-tag') const { sleep } = require('@/test/helpers') @@ -29,7 +28,9 @@ const { } = require('@/modules/serverinvites/services/creation') const { findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory } = require('@/modules/serverinvites/repositories/serverInvites') const { collectAndValidateCoreTargetsFactory @@ -45,7 +46,35 @@ const { } = require('@/modules/activitystream/services/streamActivity') const { saveActivityFactory } = require('@/modules/activitystream/repositories') const { publish } = require('@/modules/shared/utils/subscriptions') -const { getUsersFactory, getUserFactory } = require('@/modules/core/repositories/users') +const { + getUsersFactory, + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const getUser = getUserFactory({ db }) const getUsers = getUsersFactory({ db }) @@ -82,6 +111,34 @@ const createStream = legacyCreateStreamFactory({ }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + /** * Cleaning up relevant tables */ diff --git a/packages/server/modules/core/tests/generic.spec.js b/packages/server/modules/core/tests/generic.spec.js index d907f79ac..b8de697ae 100644 --- a/packages/server/modules/core/tests/generic.spec.js +++ b/packages/server/modules/core/tests/generic.spec.js @@ -10,7 +10,6 @@ const envHelperMock = mockRequireModule( const expect = require('chai').expect const { beforeEachContext } = require('@/test/hooks') -const { createUser } = require('@/modules/core/services/users') const { validateScopes, authorizeResolver } = require('@/modules/shared') const { buildContext } = require('@/modules/shared/middleware') @@ -34,7 +33,9 @@ const { } = require('@/modules/serverinvites/services/creation') const { findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory } = require('@/modules/serverinvites/repositories/serverInvites') const { collectAndValidateCoreTargetsFactory @@ -50,7 +51,35 @@ const { } = require('@/modules/activitystream/services/streamActivity') const { saveActivityFactory } = require('@/modules/activitystream/repositories') const { publish } = require('@/modules/shared/utils/subscriptions') -const { getUsersFactory, getUserFactory } = require('@/modules/core/repositories/users') +const { + getUsersFactory, + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const getUser = getUserFactory({ db }) const getUsers = getUsersFactory({ db }) @@ -87,6 +116,34 @@ const createStream = legacyCreateStreamFactory({ }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + describe('Generic AuthN & AuthZ controller tests', () => { before(async () => { await beforeEachContext() diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index 6f32fccfe..a34e4efa2 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -5,7 +5,7 @@ const request = require('supertest') const { beforeEachContext, initializeTestServer } = require(`@/test/hooks`) const { generateManyObjects } = require(`@/test/helpers`) -const { createUser, changeUserRole } = require('@/modules/core/services/users') +const { changeUserRole } = require('@/modules/core/services/users') const { createPersonalAccessToken } = require('../services/tokens') const { Roles, Scopes } = require('@speckle/shared') const cryptoRandomString = require('crypto-random-string') @@ -31,8 +31,37 @@ const { const { publish } = require('@/modules/shared/utils/subscriptions') const { getUserFactory, - legacyGetPaginatedUsersFactory + legacyGetPaginatedUsersFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory } = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const getUser = getUserFactory({ db }) const getStream = getStreamFactory({ db }) @@ -66,6 +95,34 @@ const addOrUpdateStreamCollaborator = addOrUpdateStreamCollaboratorFactory({ }) const getUsers = legacyGetPaginatedUsersFactory({ db }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + let app let server let sendRequest diff --git a/packages/server/modules/core/tests/graphSubs.spec.js b/packages/server/modules/core/tests/graphSubs.spec.js index 6770fcadf..f6a8e8b26 100644 --- a/packages/server/modules/core/tests/graphSubs.spec.js +++ b/packages/server/modules/core/tests/graphSubs.spec.js @@ -7,7 +7,6 @@ const { execute } = require('@apollo/client/core') const { SubscriptionClient } = require('subscriptions-transport-ws') const ws = require('ws') -const { createUser } = require('../services/users') const { createPersonalAccessToken } = require('../services/tokens') const { beforeEachContext } = require(`@/test/hooks`) @@ -28,7 +27,38 @@ const { addStreamPermissionsAddedActivityFactory } = require('@/modules/activitystream/services/streamActivity') const { publish } = require('@/modules/shared/utils/subscriptions') -const { getUserFactory } = require('@/modules/core/repositories/users') +const { + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const saveActivity = saveActivityFactory({ db }) const validateStreamAccess = validateStreamAccessFactory({ authorizeResolver }) @@ -48,6 +78,34 @@ const addOrUpdateStreamCollaborator = addOrUpdateStreamCollaboratorFactory({ }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + let addr let wsAddr let childPort = null diff --git a/packages/server/modules/core/tests/integration/createUser.spec.ts b/packages/server/modules/core/tests/integration/createUser.spec.ts index 4343325ef..e688316e3 100644 --- a/packages/server/modules/core/tests/integration/createUser.spec.ts +++ b/packages/server/modules/core/tests/integration/createUser.spec.ts @@ -1,5 +1,4 @@ import { expect } from 'chai' -import { createUser } from '@/modules/core/services/users' import { beforeEach, describe, it } from 'mocha' import { beforeEachContext } from '@/test/hooks' import { db } from '@/db/knex' @@ -9,10 +8,61 @@ import { } from '@/modules/core/helpers/testHelpers' import { expectToThrow } from '@/test/assertionHelper' import { PasswordTooShortError } from '@/modules/core/errors/userinput' -import { findPrimaryEmailForUserFactory } from '@/modules/core/repositories/userEmails' -import { legacyGetUserFactory } from '@/modules/core/repositories/users' +import { + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory, + findEmailFactory, + findPrimaryEmailForUserFactory +} from '@/modules/core/repositories/userEmails' +import { + countAdminUsersFactory, + getUserFactory, + legacyGetUserFactory, + storeUserAclFactory, + storeUserFactory +} from '@/modules/core/repositories/users' +import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request' +import { getServerInfo } from '@/modules/core/services/generic' +import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories' +import { renderEmail } from '@/modules/emails/services/emailRendering' +import { sendEmail } from '@/modules/emails/services/sending' +import { createUserFactory } from '@/modules/core/services/users/management' +import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails' +import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing' +import { + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { UsersEmitter } from '@/modules/core/events/usersEmitter' const getUser = legacyGetUserFactory({ db }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) describe('Users @core-users', () => { beforeEach(async () => { diff --git a/packages/server/modules/core/tests/integration/emailVerification.spec.ts b/packages/server/modules/core/tests/integration/emailVerification.spec.ts index 854869a13..e776ea6ef 100644 --- a/packages/server/modules/core/tests/integration/emailVerification.spec.ts +++ b/packages/server/modules/core/tests/integration/emailVerification.spec.ts @@ -1,16 +1,64 @@ import { describe } from 'mocha' import { + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory, findEmailFactory, updateUserEmailFactory } from '@/modules/core/repositories/userEmails' import { db } from '@/db/knex' -import { createUser } from '@/modules/core/services/users' import { createRandomEmail, createRandomPassword } from '@/modules/core/helpers/testHelpers' import { markUserEmailAsVerifiedFactory } from '@/modules/core/services/users/emailVerification' import { expect } from 'chai' +import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request' +import { + countAdminUsersFactory, + getUserFactory, + storeUserAclFactory, + storeUserFactory +} from '@/modules/core/repositories/users' +import { getServerInfo } from '@/modules/core/services/generic' +import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories' +import { renderEmail } from '@/modules/emails/services/emailRendering' +import { sendEmail } from '@/modules/emails/services/sending' +import { createUserFactory } from '@/modules/core/services/users/management' +import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails' +import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing' +import { + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { UsersEmitter } from '@/modules/core/events/usersEmitter' + +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) describe('Verification @user-emails', () => { it('should mark user email as verified', async () => { diff --git a/packages/server/modules/core/tests/integration/findUsers.spec.ts b/packages/server/modules/core/tests/integration/findUsers.spec.ts index 57f5eeb92..b984ed70e 100644 --- a/packages/server/modules/core/tests/integration/findUsers.spec.ts +++ b/packages/server/modules/core/tests/integration/findUsers.spec.ts @@ -1,19 +1,67 @@ import { describe } from 'mocha' -import { createUser } from '@/modules/core/services/users' import { createRandomEmail, createRandomPassword } from '@/modules/core/helpers/testHelpers' -import { updateUserEmailFactory } from '@/modules/core/repositories/userEmails' +import { + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory, + findEmailFactory, + updateUserEmailFactory +} from '@/modules/core/repositories/userEmails' import { db } from '@/db/knex' import { expect } from 'chai' import { + countAdminUsersFactory, getUserByEmail, + getUserFactory, getUsersFactory, - listUsers + listUsers, + storeUserAclFactory, + storeUserFactory } from '@/modules/core/repositories/users' +import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request' +import { getServerInfo } from '@/modules/core/services/generic' +import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories' +import { renderEmail } from '@/modules/emails/services/emailRendering' +import { sendEmail } from '@/modules/emails/services/sending' +import { createUserFactory } from '@/modules/core/services/users/management' +import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails' +import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing' +import { + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { UsersEmitter } from '@/modules/core/events/usersEmitter' const getUsers = getUsersFactory({ db }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) describe('Find users @core', () => { describe('getUsers', () => { diff --git a/packages/server/modules/core/tests/integration/updateUser.spec.ts b/packages/server/modules/core/tests/integration/updateUser.spec.ts index fc4e3b559..05f989126 100644 --- a/packages/server/modules/core/tests/integration/updateUser.spec.ts +++ b/packages/server/modules/core/tests/integration/updateUser.spec.ts @@ -1,5 +1,4 @@ import { expect } from 'chai' -import { createUser } from '@/modules/core/services/users' import { beforeEach, describe, it } from 'mocha' import { beforeEachContext } from '@/test/hooks' import { db } from '@/db/knex' @@ -8,12 +7,64 @@ import { createRandomPassword } from '@/modules/core/helpers/testHelpers' import { UserEmails } from '@/modules/core/dbSchema' -import { legacyGetUserFactory, updateUser } from '@/modules/core/repositories/users' +import { + countAdminUsersFactory, + getUserFactory, + legacyGetUserFactory, + storeUserAclFactory, + storeUserFactory, + updateUser +} from '@/modules/core/repositories/users' import { expectToThrow } from '@/test/assertionHelper' +import { + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory, + findEmailFactory +} from '@/modules/core/repositories/userEmails' +import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request' +import { getServerInfo } from '@/modules/core/services/generic' +import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories' +import { renderEmail } from '@/modules/emails/services/emailRendering' +import { sendEmail } from '@/modules/emails/services/sending' +import { createUserFactory } from '@/modules/core/services/users/management' +import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails' +import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing' +import { + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { UsersEmitter } from '@/modules/core/events/usersEmitter' const userEmailsDB = db(UserEmails.name) const getUser = legacyGetUserFactory({ db }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) describe('Users @core-users', () => { beforeEach(async () => { diff --git a/packages/server/modules/core/tests/integration/userEmails.graph.spec.ts b/packages/server/modules/core/tests/integration/userEmails.graph.spec.ts index 2ae2ec387..becb07974 100644 --- a/packages/server/modules/core/tests/integration/userEmails.graph.spec.ts +++ b/packages/server/modules/core/tests/integration/userEmails.graph.spec.ts @@ -1,7 +1,6 @@ import { beforeEachContext, truncateTables } from '@/test/hooks' import { expect } from 'chai' import { describe, it } from 'mocha' -import { createUser } from '@/modules/core/services/users' import { createRandomEmail, createRandomPassword @@ -30,7 +29,14 @@ import { getServerInfo } from '@/modules/core/services/generic' import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories' import { renderEmail } from '@/modules/emails/services/emailRendering' import { sendEmail } from '@/modules/emails/services/sending' -import { legacyGetUserFactory } from '@/modules/core/repositories/users' +import { + countAdminUsersFactory, + legacyGetUserFactory, + storeUserAclFactory, + storeUserFactory +} from '@/modules/core/repositories/users' +import { createUserFactory } from '@/modules/core/services/users/management' +import { UsersEmitter } from '@/modules/core/events/usersEmitter' const getUser = legacyGetUserFactory({ db }) const requestNewEmailVerification = requestNewEmailVerificationFactory({ @@ -53,6 +59,17 @@ const createUserEmail = validateAndCreateUserEmailFactory({ requestNewEmailVerification }) +const findEmail = findEmailFactory({ db }) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: createUserEmail, + usersEventsEmitter: UsersEmitter.emit +}) + describe('User emails graphql @core', () => { before(async () => { await beforeEachContext() diff --git a/packages/server/modules/core/tests/integration/userEmails.spec.ts b/packages/server/modules/core/tests/integration/userEmails.spec.ts index 06b030d76..2c145639c 100644 --- a/packages/server/modules/core/tests/integration/userEmails.spec.ts +++ b/packages/server/modules/core/tests/integration/userEmails.spec.ts @@ -1,14 +1,16 @@ import { before } from 'mocha' -import { createUser } from '@/modules/core/services/users' import { beforeEachContext } from '@/test/hooks' import { expect } from 'chai' import { + countAdminUsersFactory, getUserByEmail, getUserFactory, legacyGetPaginatedUsersCount, legacyGetPaginatedUsersFactory, listUsers, - markUserAsVerified + markUserAsVerified, + storeUserAclFactory, + storeUserFactory } from '@/modules/core/repositories/users' import * as UsersService from '@/modules/core/services/users' import { db } from '@/db/knex' @@ -42,6 +44,8 @@ import { getServerInfo } from '@/modules/core/services/generic' import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories' import { renderEmail } from '@/modules/emails/services/emailRendering' import { sendEmail } from '@/modules/emails/services/sending' +import { createUserFactory } from '@/modules/core/services/users/management' +import { UsersEmitter } from '@/modules/core/events/usersEmitter' const getUsers = legacyGetPaginatedUsersFactory({ db }) const countUsers = legacyGetPaginatedUsersCount({ db }) @@ -67,6 +71,17 @@ const createUserEmail = validateAndCreateUserEmailFactory({ requestNewEmailVerification }) +const findEmail = findEmailFactory({ db }) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: createUserEmail, + usersEventsEmitter: UsersEmitter.emit +}) + describe('Core @user-emails', () => { before(async () => { await beforeEachContext() diff --git a/packages/server/modules/core/tests/objects.spec.js b/packages/server/modules/core/tests/objects.spec.js index afb8ed78a..6c4ad7f34 100644 --- a/packages/server/modules/core/tests/objects.spec.js +++ b/packages/server/modules/core/tests/objects.spec.js @@ -7,7 +7,6 @@ const { cloneDeep, times, random, padStart } = require('lodash') const { beforeEachContext } = require('@/test/hooks') const { getAnIdForThisOnePlease } = require('@/test/helpers') -const { createUser } = require('../services/users') const { createObject, createObjects, @@ -35,7 +34,9 @@ const { } = require('@/modules/serverinvites/services/creation') const { findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory } = require('@/modules/serverinvites/repositories/serverInvites') const { collectAndValidateCoreTargetsFactory @@ -51,7 +52,35 @@ const { } = require('@/modules/activitystream/services/streamActivity') const { saveActivityFactory } = require('@/modules/activitystream/repositories') const { publish } = require('@/modules/shared/utils/subscriptions') -const { getUsersFactory, getUserFactory } = require('@/modules/core/repositories/users') +const { + getUsersFactory, + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const sampleCommit = JSON.parse(`{ "Objects": [ @@ -111,6 +140,34 @@ const createStream = legacyCreateStreamFactory({ }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + describe('Objects @core-objects', () => { const userOne = { name: 'Dimitrie Stefanescu', diff --git a/packages/server/modules/core/tests/rest.spec.js b/packages/server/modules/core/tests/rest.spec.js index 4c2f3dc76..37cb4dd06 100644 --- a/packages/server/modules/core/tests/rest.spec.js +++ b/packages/server/modules/core/tests/rest.spec.js @@ -8,7 +8,6 @@ const crypto = require('crypto') const { beforeEachContext } = require('@/test/hooks') const { createManyObjects } = require('@/test/helpers') -const { createUser } = require('../services/users') const { createPersonalAccessToken } = require('../services/tokens') const { Scopes } = require('@speckle/shared') const { @@ -28,7 +27,9 @@ const { } = require('@/modules/serverinvites/services/creation') const { findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory } = require('@/modules/serverinvites/repositories/serverInvites') const { collectAndValidateCoreTargetsFactory @@ -44,7 +45,35 @@ const { } = require('@/modules/activitystream/services/streamActivity') const { saveActivityFactory } = require('@/modules/activitystream/repositories') const { publish } = require('@/modules/shared/utils/subscriptions') -const { getUsersFactory, getUserFactory } = require('@/modules/core/repositories/users') +const { + getUsersFactory, + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const getUser = getUserFactory({ db }) const getUsers = getUsersFactory({ db }) @@ -81,6 +110,34 @@ const createStream = legacyCreateStreamFactory({ }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + describe('Upload/Download Routes @api-rest', () => { const userA = { name: 'd1', diff --git a/packages/server/modules/core/tests/users.spec.js b/packages/server/modules/core/tests/users.spec.js index bead17f6e..5df5aec96 100644 --- a/packages/server/modules/core/tests/users.spec.js +++ b/packages/server/modules/core/tests/users.spec.js @@ -4,7 +4,6 @@ const assert = require('assert') const { changeUserRole, - createUser, findOrCreateUser, getUserByEmail, searchUsers, @@ -65,7 +64,9 @@ const { } = require('@/modules/serverinvites/services/creation') const { findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory } = require('@/modules/serverinvites/repositories/serverInvites') const { collectAndValidateCoreTargetsFactory @@ -86,8 +87,33 @@ const { const { getUsersFactory, getUserFactory, - legacyGetUserFactory + legacyGetUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory } = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const getUser = legacyGetUserFactory({ db }) const getUsers = getUsersFactory({ db }) @@ -151,6 +177,34 @@ const createStream = legacyCreateStreamFactory({ }) const grantPermissionsStream = grantStreamPermissionsFactory({ db }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + describe('Actors & Tokens @user-services', () => { const myTestActor = { name: 'Dimitrie Stefanescu', diff --git a/packages/server/modules/core/tests/usersAdmin.spec.js b/packages/server/modules/core/tests/usersAdmin.spec.js index b90c12c25..530e5e36d 100644 --- a/packages/server/modules/core/tests/usersAdmin.spec.js +++ b/packages/server/modules/core/tests/usersAdmin.spec.js @@ -2,8 +2,6 @@ const expect = require('chai').expect const assert = require('assert') const { - createUser, - deleteUser, changeUserRole, getUserRole @@ -13,13 +11,71 @@ const { Roles } = require('@speckle/shared') const cryptoRandomString = require('crypto-random-string') const { legacyGetPaginatedUsersFactory, - legacyGetPaginatedUsersCount + legacyGetPaginatedUsersCount, + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory } = require('@/modules/core/repositories/users') const { db } = require('@/db/knex') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const getUsers = legacyGetPaginatedUsersFactory({ db }) const countUsers = legacyGetPaginatedUsersCount({ db }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + describe('User admin @user-services', () => { const myTestActor = { name: 'Gergo Jedlicska', diff --git a/packages/server/modules/core/tests/usersAdminList.spec.ts b/packages/server/modules/core/tests/usersAdminList.spec.ts index 63f440a63..df53a6da5 100644 --- a/packages/server/modules/core/tests/usersAdminList.spec.ts +++ b/packages/server/modules/core/tests/usersAdminList.spec.ts @@ -1,6 +1,5 @@ import { ServerInvites, Streams, Users } from '@/modules/core/dbSchema' import { truncateTables } from '@/test/hooks' -import { createUser } from '@/modules/core/services/users' import { times, clamp } from 'lodash' import { createStreamInviteDirectly } from '@/test/speckle-helpers/inviteHelper' import { getAdminUsersList } from '@/test/graphql/users' @@ -22,8 +21,10 @@ import { import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' import { + deleteServerOnlyInvitesFactory, findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + updateAllInviteTargetsFactory } from '@/modules/serverinvites/repositories/serverInvites' import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' @@ -33,7 +34,27 @@ import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' import { saveActivityFactory } from '@/modules/activitystream/repositories' import { publish } from '@/modules/shared/utils/subscriptions' -import { getUserFactory, getUsersFactory } from '@/modules/core/repositories/users' +import { + countAdminUsersFactory, + getUserFactory, + getUsersFactory, + storeUserAclFactory, + storeUserFactory +} from '@/modules/core/repositories/users' +import { + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory, + findEmailFactory +} from '@/modules/core/repositories/userEmails' +import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request' +import { getServerInfo } from '@/modules/core/services/generic' +import { renderEmail } from '@/modules/emails/services/emailRendering' +import { sendEmail } from '@/modules/emails/services/sending' +import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories' +import { createUserFactory } from '@/modules/core/services/users/management' +import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails' +import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing' +import { UsersEmitter } from '@/modules/core/events/usersEmitter' // To ensure that the invites are created in the correct order, we need to wait a bit between each creation const WAIT_TIMEOUT = 5 @@ -74,6 +95,34 @@ const createStream = legacyCreateStreamFactory({ }) const createInviteDirectly = createStreamInviteDirectly +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + function randomEl(array: T[]): T { return array[Math.floor(Math.random() * array.length)] } @@ -100,7 +149,8 @@ describe('[Admin users list]', () => { name: 'Mr Server Admin Dude', email: 'adminuserguy@gmail.com', password: 'sn3aky-1337-b1m', - id: undefined as Optional + id: undefined as Optional, + verified: false } const USER_COUNT = 15 @@ -156,7 +206,8 @@ describe('[Admin users list]', () => { remainingSearchQueryUserCount-- >= 1 ? SEARCH_QUERY : '' }`, email: `speckleuser${i}@gmail.com`, - password: 'sn3aky-1337-b1m' + password: 'sn3aky-1337-b1m', + verified: false }) userIds.push(id) await wait(WAIT_TIMEOUT) diff --git a/packages/server/modules/core/tests/usersGraphql.spec.ts b/packages/server/modules/core/tests/usersGraphql.spec.ts index 57c273f3d..d62eceb5e 100644 --- a/packages/server/modules/core/tests/usersGraphql.spec.ts +++ b/packages/server/modules/core/tests/usersGraphql.spec.ts @@ -3,7 +3,6 @@ import { BasicTestUser, createTestUsers } from '@/test/authHelper' import { getActiveUser, getOtherUser } from '@/test/graphql/users' import { beforeEachContext, truncateTables } from '@/test/hooks' import { expect } from 'chai' -import { createUser } from '@/modules/core/services/users' import { createRandomEmail, createRandomPassword @@ -34,7 +33,14 @@ import { getServerInfo } from '@/modules/core/services/generic' import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories' import { renderEmail } from '@/modules/emails/services/emailRendering' import { sendEmail } from '@/modules/emails/services/sending' -import { getUserFactory } from '@/modules/core/repositories/users' +import { + countAdminUsersFactory, + getUserFactory, + storeUserAclFactory, + storeUserFactory +} from '@/modules/core/repositories/users' +import { UsersEmitter } from '@/modules/core/events/usersEmitter' +import { createUserFactory } from '@/modules/core/services/users/management' const getUser = getUserFactory({ db }) const requestNewEmailVerification = requestNewEmailVerificationFactory({ @@ -57,6 +63,17 @@ const createUserEmail = validateAndCreateUserEmailFactory({ requestNewEmailVerification }) +const findEmail = findEmailFactory({ db }) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: createUserEmail, + usersEventsEmitter: UsersEmitter.emit +}) + describe('Users (GraphQL)', () => { const me: BasicTestUser = { id: '', @@ -147,7 +164,8 @@ describe('Users (GraphQL)', () => { const userId = await createUser({ name: 'emails user', email: createRandomEmail(), - password: createRandomPassword() + password: createRandomPassword(), + verified: false }) await createUserEmail({ userEmail: { diff --git a/packages/server/modules/fileuploads/tests/fileuploads.integration.spec.ts b/packages/server/modules/fileuploads/tests/fileuploads.integration.spec.ts index e27b2cb00..ff963d901 100644 --- a/packages/server/modules/fileuploads/tests/fileuploads.integration.spec.ts +++ b/packages/server/modules/fileuploads/tests/fileuploads.integration.spec.ts @@ -3,7 +3,6 @@ import { expect } from 'chai' import { beforeEachContext, initializeTestServer } from '@/test/hooks' -import { createUser } from '@/modules/core/services/users' import { createToken } from '@/modules/core/services/tokens' import type { Server } from 'http' import type { Express } from 'express' @@ -23,8 +22,10 @@ import { import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' import { + deleteServerOnlyInvitesFactory, findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + updateAllInviteTargetsFactory } from '@/modules/serverinvites/repositories/serverInvites' import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' @@ -34,7 +35,27 @@ import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' import { saveActivityFactory } from '@/modules/activitystream/repositories' import { publish } from '@/modules/shared/utils/subscriptions' -import { getUserFactory, getUsersFactory } from '@/modules/core/repositories/users' +import { + countAdminUsersFactory, + getUserFactory, + getUsersFactory, + storeUserAclFactory, + storeUserFactory +} from '@/modules/core/repositories/users' +import { + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory, + findEmailFactory +} from '@/modules/core/repositories/userEmails' +import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request' +import { getServerInfo } from '@/modules/core/services/generic' +import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories' +import { renderEmail } from '@/modules/emails/services/emailRendering' +import { createUserFactory } from '@/modules/core/services/users/management' +import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails' +import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing' +import { UsersEmitter } from '@/modules/core/events/usersEmitter' +import { sendEmail } from '@/modules/emails/services/sending' const getUser = getUserFactory({ db }) const getUsers = getUsersFactory({ db }) @@ -71,6 +92,34 @@ const createStream = legacyCreateStreamFactory({ }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + describe('FileUploads @fileuploads', () => { let server: Server let app: Express diff --git a/packages/server/modules/pwdreset/tests/pwdrest.spec.js b/packages/server/modules/pwdreset/tests/pwdrest.spec.js index 1e7287bbe..8efe46376 100644 --- a/packages/server/modules/pwdreset/tests/pwdrest.spec.js +++ b/packages/server/modules/pwdreset/tests/pwdrest.spec.js @@ -4,10 +4,70 @@ const knex = require('@/db/knex') const ResetTokens = () => knex('pwdreset_tokens') const { beforeEachContext } = require('@/test/hooks') -const { createUser } = require('@/modules/core/services/users') const { localAuthRestApi } = require('@/modules/auth/tests/helpers/registration') const { expectToThrow } = require('@/test/assertionHelper') const { expect } = require('chai') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') + +const db = knex +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) describe('Password reset requests @passwordresets', () => { let app diff --git a/packages/server/modules/stats/tests/stats.spec.ts b/packages/server/modules/stats/tests/stats.spec.ts index 1fc2e8814..58868cbc4 100644 --- a/packages/server/modules/stats/tests/stats.spec.ts +++ b/packages/server/modules/stats/tests/stats.spec.ts @@ -1,6 +1,5 @@ /* istanbul ignore file */ import { expect } from 'chai' -import { createUser } from '@/modules/core/services/users' import { createPersonalAccessToken } from '@/modules/core/services/tokens' import { createObjects } from '@/modules/core/services/objects' import { beforeEachContext, initializeTestServer } from '@/test/hooks' @@ -49,8 +48,10 @@ import { import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' import { + deleteServerOnlyInvitesFactory, findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + updateAllInviteTargetsFactory } from '@/modules/serverinvites/repositories/serverInvites' import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' @@ -60,7 +61,27 @@ import { addStreamCreatedActivityFactory } from '@/modules/activitystream/servic import { saveActivityFactory } from '@/modules/activitystream/repositories' import { publish } from '@/modules/shared/utils/subscriptions' import { addCommitCreatedActivityFactory } from '@/modules/activitystream/services/commitActivity' -import { getUserFactory, getUsersFactory } from '@/modules/core/repositories/users' +import { + countAdminUsersFactory, + getUserFactory, + getUsersFactory, + storeUserAclFactory, + storeUserFactory +} from '@/modules/core/repositories/users' +import { + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory, + findEmailFactory +} from '@/modules/core/repositories/userEmails' +import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request' +import { getServerInfo } from '@/modules/core/services/generic' +import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories' +import { renderEmail } from '@/modules/emails/services/emailRendering' +import { sendEmail } from '@/modules/emails/services/sending' +import { createUserFactory } from '@/modules/core/services/users/management' +import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails' +import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing' +import { UsersEmitter } from '@/modules/core/events/usersEmitter' const getUsers = getUsersFactory({ db }) const markCommitStreamUpdated = markCommitStreamUpdatedFactory({ db }) @@ -118,6 +139,33 @@ const createStream = legacyCreateStreamFactory({ projectsEventsEmitter: ProjectsEmitter.emit }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) const params = { numUsers: 25, numStreams: 30, numObjects: 100, numCommits: 100 } diff --git a/packages/server/modules/webhooks/tests/cleanup.spec.ts b/packages/server/modules/webhooks/tests/cleanup.spec.ts index c2b604138..25dd0bc85 100644 --- a/packages/server/modules/webhooks/tests/cleanup.spec.ts +++ b/packages/server/modules/webhooks/tests/cleanup.spec.ts @@ -2,6 +2,7 @@ import knex, { db } from '@/db/knex' import { saveActivityFactory } from '@/modules/activitystream/repositories' import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' +import { UsersEmitter } from '@/modules/core/events/usersEmitter' import { createRandomEmail, createRandomPassword @@ -11,19 +12,39 @@ import { createStreamFactory, getStreamFactory } from '@/modules/core/repositories/streams' -import { getUserFactory, getUsersFactory } from '@/modules/core/repositories/users' +import { + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory, + findEmailFactory +} from '@/modules/core/repositories/userEmails' +import { + countAdminUsersFactory, + getUserFactory, + getUsersFactory, + storeUserAclFactory, + storeUserFactory +} from '@/modules/core/repositories/users' +import { getServerInfo } from '@/modules/core/services/generic' import { createStreamReturnRecordFactory, legacyCreateStreamFactory } from '@/modules/core/services/streams/management' -import { createUser } from '@/modules/core/services/users' +import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails' +import { createUserFactory } from '@/modules/core/services/users/management' +import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories' +import { renderEmail } from '@/modules/emails/services/emailRendering' +import { sendEmail } from '@/modules/emails/services/sending' +import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request' import { + deleteServerOnlyInvitesFactory, findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + updateAllInviteTargetsFactory } from '@/modules/serverinvites/repositories/serverInvites' import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' +import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing' import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' import { getEventBus } from '@/modules/shared/services/eventBus' import { publish } from '@/modules/shared/utils/subscriptions' @@ -72,6 +93,33 @@ const createStream = legacyCreateStreamFactory({ projectsEventsEmitter: ProjectsEmitter.emit }) }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) const countWebhooks = async () => { const [{ count }] = await WebhooksConfig().count() diff --git a/packages/server/modules/webhooks/tests/webhooks.spec.js b/packages/server/modules/webhooks/tests/webhooks.spec.js index bc03d2cce..52d0d8142 100644 --- a/packages/server/modules/webhooks/tests/webhooks.spec.js +++ b/packages/server/modules/webhooks/tests/webhooks.spec.js @@ -9,7 +9,6 @@ const { } = require('@/test/hooks') const { noErrors } = require('@/test/helpers') const { createPersonalAccessToken } = require('../../core/services/tokens') -const { createUser } = require('../../core/services/users') const { Scopes, Roles } = require('@speckle/shared') const { createWebhookConfigFactory, @@ -47,7 +46,9 @@ const { } = require('@/modules/serverinvites/services/creation') const { findUserByTargetFactory, - insertInviteAndDeleteOldFactory + insertInviteAndDeleteOldFactory, + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory } = require('@/modules/serverinvites/repositories/serverInvites') const { collectAndValidateCoreTargetsFactory @@ -63,7 +64,34 @@ const { } = require('@/modules/activitystream/services/streamActivity') const { saveActivityFactory } = require('@/modules/activitystream/repositories') const { publish } = require('@/modules/shared/utils/subscriptions') -const { getUserFactory, getUsersFactory } = require('@/modules/core/repositories/users') +const { + getUserFactory, + getUsersFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') const getUser = getUserFactory({ db }) const getUsers = getUsersFactory({ db }) @@ -104,6 +132,33 @@ const createStream = legacyCreateStreamFactory({ }) }) const grantPermissionsStream = grantStreamPermissionsFactory({ db }) +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) describe('Webhooks @webhooks', () => { const getWebhook = getWebhookByIdFactory({ db }) diff --git a/packages/server/scripts/seedUsers.js b/packages/server/scripts/seedUsers.js index 174b6258e..1fbfdc96f 100644 --- a/packages/server/scripts/seedUsers.js +++ b/packages/server/scripts/seedUsers.js @@ -1,8 +1,68 @@ require('../bootstrap') +const { db } = require('@/db/knex') const { logger } = require('@/logging/logging') -const { createUser } = require('@/modules/core/services/users') +const { UsersEmitter } = require('@/modules/core/events/usersEmitter') +const { + findEmailFactory, + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory +} = require('@/modules/core/repositories/userEmails') +const { + getUserFactory, + storeUserFactory, + countAdminUsersFactory, + storeUserAclFactory +} = require('@/modules/core/repositories/users') +const { getServerInfo } = require('@/modules/core/services/generic') +const { + validateAndCreateUserEmailFactory +} = require('@/modules/core/services/userEmails') +const { createUserFactory } = require('@/modules/core/services/users/management') +const { + deleteOldAndInsertNewVerificationFactory +} = require('@/modules/emails/repositories') +const { renderEmail } = require('@/modules/emails/services/emailRendering') +const { sendEmail } = require('@/modules/emails/services/sending') +const { + requestNewEmailVerificationFactory +} = require('@/modules/emails/services/verification/request') +const { + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { + finalizeInvitedServerRegistrationFactory +} = require('@/modules/serverinvites/services/processing') const axios = require('axios').default +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + const main = async () => { const userInputs = ( await axios.get('https://randomuser.me/api/?results=250') diff --git a/packages/server/test/authHelper.ts b/packages/server/test/authHelper.ts index e03646185..694a3c2e4 100644 --- a/packages/server/test/authHelper.ts +++ b/packages/server/test/authHelper.ts @@ -1,9 +1,61 @@ +import { db } from '@/db/knex' +import { UsersEmitter } from '@/modules/core/events/usersEmitter' import { AllScopes, ServerRoles } from '@/modules/core/helpers/mainConstants' import { UserRecord } from '@/modules/core/helpers/types' +import { + createUserEmailFactory, + ensureNoPrimaryEmailForUserFactory, + findEmailFactory +} from '@/modules/core/repositories/userEmails' +import { + countAdminUsersFactory, + getUserFactory, + storeUserAclFactory, + storeUserFactory +} from '@/modules/core/repositories/users' +import { getServerInfo } from '@/modules/core/services/generic' import { createPersonalAccessToken } from '@/modules/core/services/tokens' -import { createUser } from '@/modules/core/services/users' +import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails' +import { createUserFactory } from '@/modules/core/services/users/management' +import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories' +import { renderEmail } from '@/modules/emails/services/emailRendering' +import { sendEmail } from '@/modules/emails/services/sending' +import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request' +import { + deleteServerOnlyInvitesFactory, + updateAllInviteTargetsFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing' import { kebabCase, omit } from 'lodash' +const findEmail = findEmailFactory({ db }) +const requestNewEmailVerification = requestNewEmailVerificationFactory({ + findEmail, + getUser: getUserFactory({ db }), + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + renderEmail, + sendEmail +}) +const createUser = createUserFactory({ + getServerInfo, + findEmail, + storeUser: storeUserFactory({ db }), + countAdminUsers: countAdminUsersFactory({ db }), + storeUserAcl: storeUserAclFactory({ db }), + validateAndCreateUserEmail: validateAndCreateUserEmailFactory({ + createUserEmail: createUserEmailFactory({ db }), + ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), + findEmail, + updateEmailInvites: finalizeInvitedServerRegistrationFactory({ + deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), + updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) + }), + requestNewEmailVerification + }), + usersEventsEmitter: UsersEmitter.emit +}) + export type BasicTestUser = { name: string email: string From c59cf5453a4d7a2f4f29c75c8b9a87e4a77edb29 Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Tue, 15 Oct 2024 12:58:14 +0300 Subject: [PATCH 2/2] minor cleanup --- .../server/modules/core/services/users.js | 85 ------------------- 1 file changed, 85 deletions(-) diff --git a/packages/server/modules/core/services/users.js b/packages/server/modules/core/services/users.js index 1a46ebfe9..2795bf16b 100644 --- a/packages/server/modules/core/services/users.js +++ b/packages/server/modules/core/services/users.js @@ -107,91 +107,6 @@ const createUser = createUserFactory({ }) module.exports = { - // /* - // Users - // */ - - // /** - // * @param {{}} user - // * @param {{skipPropertyValidation: boolean } | undefined} options - // * @returns {Promise} - // */ - // async createUser(user, options = undefined) { - // // ONLY ALLOW SKIPPING WHEN CREATING USERS FOR TESTS, IT'S UNSAFE OTHERWISE - // const { skipPropertyValidation = false } = options || {} - - // if (!user.email?.length) throw new UserInputError('E-mail address is required') - - // let expectedRole = null - // if (user.role) { - // const isValidRole = Object.values(Roles.Server).includes(user.role) - // const isValidIfGuestModeEnabled = - // user.role !== Roles.Server.Guest || (await getServerInfo()).guestModeEnabled - // expectedRole = isValidRole && isValidIfGuestModeEnabled ? user.role : null - // } - // delete user.role - - // user = skipPropertyValidation - // ? user - // : pick(user, ['id', 'bio', 'email', 'password', 'name', 'company', 'verified']) - - // const newId = crs({ length: 10 }) - // user.id = newId - // user.email = user.email.toLowerCase() - - // if (!user.name) throw new UserInputError('User name is required') - - // if (user.avatar) { - // user.avatar = sanitizeImageUrl(user.avatar) - // } - - // if (user.password) { - // if (user.password.length < MINIMUM_PASSWORD_LENGTH) - // throw new PasswordTooShortError(MINIMUM_PASSWORD_LENGTH) - // user.passwordDigest = await bcrypt.hash(user.password, 10) - // } - // delete user.password - - // const userEmail = await findEmailFactory({ db })({ - // email: user.email - // }) - // if (userEmail) throw new UserInputError('Email taken. Try logging in?') - - // const [newUser] = (await Users().insert(user, UsersSchema.cols)) || [] - // if (!newUser) throw new Error("Couldn't create user") - - // const userRole = - // (await countAdminUsers()) === 0 - // ? Roles.Server.Admin - // : expectedRole || Roles.Server.User - - // await Acl().insert({ userId: newId, role: userRole }) - - // const validateAndCreateUserEmail = validateAndCreateUserEmailFactory({ - // createUserEmail: createUserEmailFactory({ db }), - // ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }), - // findEmail: findEmailFactory({ db }), - // updateEmailInvites: finalizeInvitedServerRegistrationFactory({ - // deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }), - // updateAllInviteTargets: updateAllInviteTargetsFactory({ db }) - // }), - // requestNewEmailVerification - // }) - - // await validateAndCreateUserEmail({ - // userEmail: { - // email: user.email, - // userId: user.id, - // verified: user.verified, - // primary: true - // } - // }) - - // await UsersEmitter.emit(UsersEvents.Created, { user: newUser }) - - // return newUser.id - // }, - /** * @param {{user: {email: string, name?: string, role?: import('@speckle/shared').ServerRoles, bio?: string, verified?: boolean}}} param0 * @returns {Promise<{