2c122a138d
* feat(multiregion): replace user replication * chore(multiregion): optimise replication * maybe it's this * postgres is fun * once more * chore(multiregion): only replicate test user creation during multiregion tests * feat: improved replicate_query logic * fix: minor * fix: starting issue * feat: included user create and delete specs to multiregion * feat: removed console logs * fix: user defaults * fix: multiregion test helper * fix: update scenarios for users * refactor(multiregion): swap replicateQuery concept to asMultiregionOperation (#5301) feat(multiregion): introduced asMultregionOperator, refactor test to user builder classes * chore: renamings * fix: remove comments * feat: remove user replication * refactor: simplified spec usages * chore: comments * chore: branches and favs * chore: more tests * chore: more tests * fix linting * fix tests * feat: dropping replication * refactor: moved project delete to service * fix: comment * feat: updateStreamFactory and updateProjectFacotry * deleteProjectFactory + replicateFactory * deleteWorkspaceFactory * fix: selector * fix: tests * fix tests, finished createStreamFactory * feat: simplify changes * fix: remove comment * fix: minor strucutres * fix: moveProjectToRegion * fix: moved branch creation outside of multiregion scope * fix: branch creation * fix: tests * fix: ci tests * fix: removed log form test * fix: on specs, no random regionKeys * review fixes * feat: workspace replciation * fix: mr comments * feat: removed test * fix: worksapce test creation * fix: mr issues * updated mutations * feat: drop workspace random defaults --------- Co-authored-by: Charles Driesler <chuck@speckle.systems>
663 lines
22 KiB
TypeScript
663 lines
22 KiB
TypeScript
/* istanbul ignore file */
|
|
import { expect } from 'chai'
|
|
import assert from 'assert'
|
|
|
|
import {
|
|
createPersonalAccessTokenFactory,
|
|
validateTokenFactory
|
|
} from '@/modules/core/services/tokens'
|
|
|
|
import { beforeEachContext } from '@/test/hooks'
|
|
import { Scopes, Roles, ensureError } from '@speckle/shared'
|
|
import { createRandomEmail } from '@/modules/core/helpers/testHelpers'
|
|
import {
|
|
createBranchFactory,
|
|
getBranchByIdFactory,
|
|
markCommitBranchUpdatedFactory,
|
|
getStreamBranchByNameFactory,
|
|
getPaginatedStreamBranchesPageFactory,
|
|
getStreamBranchCountFactory
|
|
} from '@/modules/core/repositories/branches'
|
|
import { db } from '@/db/knex'
|
|
import {
|
|
getCommitFactory,
|
|
createCommitFactory,
|
|
insertStreamCommitsFactory,
|
|
insertBranchCommitsFactory,
|
|
legacyGetPaginatedStreamCommitsPageFactory,
|
|
getPaginatedBranchCommitsItemsFactory,
|
|
deleteProjectCommitsFactory
|
|
} from '@/modules/core/repositories/commits'
|
|
import {
|
|
createCommitByBranchIdFactory,
|
|
createCommitByBranchNameFactory
|
|
} from '@/modules/core/services/commit/management'
|
|
import {
|
|
getStreamFactory,
|
|
grantStreamPermissionsFactory,
|
|
getUserDeletableStreamsFactory,
|
|
getExplicitProjects
|
|
} from '@/modules/core/repositories/streams'
|
|
import {
|
|
getObjectFactory,
|
|
storeSingleObjectIfNotFoundFactory
|
|
} from '@/modules/core/repositories/objects'
|
|
import {
|
|
deleteServerOnlyInvitesFactory,
|
|
updateAllInviteTargetsFactory,
|
|
deleteAllUserInvitesFactory
|
|
} from '@/modules/serverinvites/repositories/serverInvites'
|
|
import { getEventBus } from '@/modules/shared/services/eventBus'
|
|
import {
|
|
getUserFactory,
|
|
legacyGetUserFactory,
|
|
storeUserFactory,
|
|
countAdminUsersFactory,
|
|
storeUserAclFactory,
|
|
legacyGetUserByEmailFactory,
|
|
updateUserFactory,
|
|
getUserByEmailFactory,
|
|
isLastAdminUserFactory,
|
|
deleteUserRecordFactory,
|
|
updateUserServerRoleFactory,
|
|
searchUsersFactory,
|
|
getUserRoleFactory
|
|
} from '@/modules/core/repositories/users'
|
|
import {
|
|
findEmailFactory,
|
|
createUserEmailFactory,
|
|
ensureNoPrimaryEmailForUserFactory,
|
|
findPrimaryEmailForUserFactory
|
|
} from '@/modules/core/repositories/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'
|
|
import {
|
|
createUserFactory,
|
|
findOrCreateUserFactory,
|
|
updateUserAndNotifyFactory,
|
|
changePasswordFactory,
|
|
validateUserPasswordFactory,
|
|
deleteUserFactory,
|
|
changeUserRoleFactory
|
|
} from '@/modules/core/services/users/management'
|
|
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
|
import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing'
|
|
import { dbLogger } from '@/observability/logging'
|
|
import {
|
|
storeApiTokenFactory,
|
|
storeTokenScopesFactory,
|
|
storeTokenResourceAccessDefinitionsFactory,
|
|
storePersonalApiTokenFactory,
|
|
getUserPersonalAccessTokensFactory,
|
|
revokeUserTokenByIdFactory,
|
|
getApiTokenByIdFactory,
|
|
getTokenScopesByIdFactory,
|
|
getTokenResourceAccessDefinitionsByIdFactory,
|
|
updateApiTokenFactory
|
|
} from '@/modules/core/repositories/tokens'
|
|
import { getTokenAppInfoFactory } from '@/modules/auth/repositories/apps'
|
|
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
|
import { getPaginatedBranchCommitsItemsByNameFactory } from '@/modules/core/services/commit/retrieval'
|
|
import { getPaginatedStreamBranchesFactory } from '@/modules/core/services/branch/retrieval'
|
|
import { createObjectFactory } from '@/modules/core/services/objects/management'
|
|
import { getUserWorkspaceSeatsFactory } from '@/modules/workspacesCore/repositories/workspaces'
|
|
import {
|
|
deleteProjectAndCommitsFactory,
|
|
queryAllProjectsFactory
|
|
} from '@/modules/core/services/projects'
|
|
import { getAllRegisteredTestDbs } from '@/modules/multiregion/tests/helpers'
|
|
import { asMultiregionalOperation, replicateFactory } from '@/modules/shared/command'
|
|
import type {
|
|
ChangeUserPassword,
|
|
CreateValidatedUser,
|
|
DeleteUser,
|
|
UpdateUserAndNotify
|
|
} from '@/modules/core/domain/users/operations'
|
|
import { createTestStream } from '@/test/speckle-helpers/streamHelper'
|
|
import { deleteProjectFactory } from '@/modules/core/repositories/projects'
|
|
|
|
const getServerInfo = getServerInfoFactory({ db })
|
|
const getUser = legacyGetUserFactory({ db })
|
|
const getStream = getStreamFactory({ db })
|
|
const createBranch = createBranchFactory({ db })
|
|
const getCommit = getCommitFactory({ db })
|
|
|
|
const getObject = getObjectFactory({ db })
|
|
const createCommitByBranchId = createCommitByBranchIdFactory({
|
|
createCommit: createCommitFactory({ db }),
|
|
getObject,
|
|
getBranchById: getBranchByIdFactory({ db }),
|
|
insertStreamCommits: insertStreamCommitsFactory({ db }),
|
|
insertBranchCommits: insertBranchCommitsFactory({ db }),
|
|
markCommitBranchUpdated: markCommitBranchUpdatedFactory({ db }),
|
|
emitEvent: getEventBus().emit
|
|
})
|
|
|
|
const createCommitByBranchName = createCommitByBranchNameFactory({
|
|
createCommitByBranchId,
|
|
getStreamBranchByName: getStreamBranchByNameFactory({ db }),
|
|
getBranchById: getBranchByIdFactory({ db })
|
|
})
|
|
|
|
const grantPermissionsStream = grantStreamPermissionsFactory({ db })
|
|
|
|
const createUser: CreateValidatedUser = async (...input) =>
|
|
asMultiregionalOperation(
|
|
async ({ mainDb, allDbs, emit }) => {
|
|
const createUser = createUserFactory({
|
|
getServerInfo,
|
|
findEmail: findEmailFactory({ db: mainDb }),
|
|
storeUser: async (...params) => {
|
|
const [user] = await Promise.all(
|
|
allDbs.map((db) => storeUserFactory({ db })(...params))
|
|
)
|
|
|
|
return user
|
|
},
|
|
countAdminUsers: countAdminUsersFactory({ db: mainDb }),
|
|
storeUserAcl: storeUserAclFactory({ db: mainDb }),
|
|
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
|
createUserEmail: createUserEmailFactory({ db: mainDb }),
|
|
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({
|
|
db: mainDb
|
|
}),
|
|
findEmail: findEmailFactory({ db: mainDb }),
|
|
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
|
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db: mainDb }),
|
|
updateAllInviteTargets: updateAllInviteTargetsFactory({ db: mainDb })
|
|
}),
|
|
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
|
getServerInfo,
|
|
findEmail: findEmailFactory({ db: mainDb }),
|
|
getUser: getUserFactory({ db: mainDb }),
|
|
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory(
|
|
{
|
|
db: mainDb
|
|
}
|
|
),
|
|
renderEmail,
|
|
sendEmail
|
|
})
|
|
}),
|
|
emitEvent: emit
|
|
})
|
|
|
|
return createUser(...input)
|
|
},
|
|
{
|
|
dbs: await getAllRegisteredTestDbs(),
|
|
name: 'create user spec',
|
|
logger: dbLogger
|
|
}
|
|
)
|
|
|
|
const findOrCreateUser = findOrCreateUserFactory({
|
|
createUser,
|
|
findPrimaryEmailForUser: findPrimaryEmailForUserFactory({ db })
|
|
})
|
|
const getUserByEmail = legacyGetUserByEmailFactory({ db })
|
|
const updateUser: UpdateUserAndNotify = async (...input) =>
|
|
asMultiregionalOperation(
|
|
({ mainDb, allDbs, emit }) => {
|
|
const updateUserAndNotify = updateUserAndNotifyFactory({
|
|
getUser: getUserFactory({ db: mainDb }),
|
|
updateUser: async (...params) => {
|
|
const [res] = await Promise.all(
|
|
allDbs.map((db) => updateUserFactory({ db })(...params))
|
|
)
|
|
|
|
return res
|
|
},
|
|
emitEvent: emit
|
|
})
|
|
|
|
return updateUserAndNotify(...input)
|
|
},
|
|
{
|
|
logger: dbLogger,
|
|
name: 'update user and notify spec',
|
|
dbs: await getAllRegisteredTestDbs()
|
|
}
|
|
)
|
|
|
|
const updateUserPassword: ChangeUserPassword = async (...input) =>
|
|
asMultiregionalOperation(
|
|
({ mainDb, allDbs }) => {
|
|
const updateUserPassword = changePasswordFactory({
|
|
getUser: getUserFactory({ db: mainDb }),
|
|
updateUser: async (...params) => {
|
|
const [res] = await Promise.all(
|
|
allDbs.map((db) => updateUserFactory({ db })(...params))
|
|
)
|
|
|
|
return res
|
|
}
|
|
})
|
|
|
|
return updateUserPassword(...input)
|
|
},
|
|
{
|
|
logger: dbLogger,
|
|
name: 'update user password spec',
|
|
dbs: await getAllRegisteredTestDbs()
|
|
}
|
|
)
|
|
|
|
const validateUserPassword = validateUserPasswordFactory({
|
|
getUserByEmail: getUserByEmailFactory({ db })
|
|
})
|
|
|
|
const deleteUser: DeleteUser = async (...input) =>
|
|
asMultiregionalOperation(
|
|
({ mainDb, allDbs, emit }) => {
|
|
const deleteUser = deleteUserFactory({
|
|
deleteProjectAndCommits: deleteProjectAndCommitsFactory({
|
|
// this is a bit of an overhead, we are issuing delete queries to all regions,
|
|
// instead of being selective and clever about figuring out the project DB and only
|
|
// deleting from main and the project db
|
|
deleteProject: replicateFactory(allDbs, deleteProjectFactory),
|
|
deleteProjectCommits: replicateFactory(allDbs, deleteProjectCommitsFactory)
|
|
}),
|
|
logger: dbLogger,
|
|
isLastAdminUser: isLastAdminUserFactory({ db: mainDb }),
|
|
getUserDeletableStreams: getUserDeletableStreamsFactory({ db: mainDb }),
|
|
queryAllProjects: queryAllProjectsFactory({
|
|
getExplicitProjects: getExplicitProjects({ db: mainDb })
|
|
}),
|
|
getUserWorkspaceSeats: getUserWorkspaceSeatsFactory({ db: mainDb }),
|
|
deleteAllUserInvites: deleteAllUserInvitesFactory({ db: mainDb }),
|
|
deleteUserRecord: async (params) => {
|
|
const [res] = await Promise.all(
|
|
allDbs.map((db) => deleteUserRecordFactory({ db })(params))
|
|
)
|
|
|
|
return res
|
|
},
|
|
emitEvent: emit
|
|
})
|
|
|
|
return deleteUser(...input)
|
|
},
|
|
{
|
|
logger: dbLogger,
|
|
name: 'delete user spec',
|
|
dbs: await getAllRegisteredTestDbs()
|
|
}
|
|
)
|
|
|
|
const changeUserRole = changeUserRoleFactory({
|
|
getServerInfo,
|
|
isLastAdminUser: isLastAdminUserFactory({ db }),
|
|
updateUserServerRole: updateUserServerRoleFactory({ db })
|
|
})
|
|
const searchUsers = searchUsersFactory({ db })
|
|
const createPersonalAccessToken = createPersonalAccessTokenFactory({
|
|
storeApiToken: storeApiTokenFactory({ db }),
|
|
storeTokenScopes: storeTokenScopesFactory({ db }),
|
|
storeTokenResourceAccessDefinitions: storeTokenResourceAccessDefinitionsFactory({
|
|
db
|
|
}),
|
|
storePersonalApiToken: storePersonalApiTokenFactory({ db })
|
|
})
|
|
const getUserTokens = getUserPersonalAccessTokensFactory({ db })
|
|
const revokeToken = revokeUserTokenByIdFactory({ db })
|
|
const validateToken = validateTokenFactory({
|
|
revokeUserTokenById: revokeUserTokenByIdFactory({ db }),
|
|
getApiTokenById: getApiTokenByIdFactory({ db }),
|
|
getTokenAppInfo: getTokenAppInfoFactory({ db }),
|
|
getTokenScopesById: getTokenScopesByIdFactory({ db }),
|
|
getUserRole: getUserRoleFactory({ db }),
|
|
getTokenResourceAccessDefinitionsById: getTokenResourceAccessDefinitionsByIdFactory({
|
|
db
|
|
}),
|
|
updateApiToken: updateApiTokenFactory({ db })
|
|
})
|
|
const getCommitsByStreamId = legacyGetPaginatedStreamCommitsPageFactory({ db })
|
|
const getCommitsByBranchName = getPaginatedBranchCommitsItemsByNameFactory({
|
|
getStreamBranchByName: getStreamBranchByNameFactory({ db }),
|
|
getPaginatedBranchCommitsItems: getPaginatedBranchCommitsItemsFactory({ db })
|
|
})
|
|
const getBranchesByStreamId = getPaginatedStreamBranchesFactory({
|
|
getPaginatedStreamBranchesPage: getPaginatedStreamBranchesPageFactory({ db }),
|
|
getStreamBranchCount: getStreamBranchCountFactory({ db })
|
|
})
|
|
const createObject = createObjectFactory({
|
|
storeSingleObjectIfNotFoundFactory: storeSingleObjectIfNotFoundFactory({ db })
|
|
})
|
|
|
|
describe('Actors & Tokens @user-services @multiregion', () => {
|
|
const myTestActor = {
|
|
name: 'Dimitrie Stefanescu',
|
|
email: 'didimitrie@example.org',
|
|
password: 'sn3aky-1337-b1m',
|
|
id: ''
|
|
}
|
|
|
|
before(async () => {
|
|
await beforeEachContext()
|
|
|
|
const actorId = await createUser(myTestActor)
|
|
myTestActor.id = actorId
|
|
})
|
|
|
|
describe('Users @core-users', () => {
|
|
it('Get user by should ignore email casing', async () => {
|
|
await createUser({
|
|
name: 'John Doe',
|
|
password: 'sn3aky-1337-b1m',
|
|
email: 'test@example.org'
|
|
})
|
|
const user = await getUserByEmail({ email: 'TeST@ExamPLE.oRg' })
|
|
expect(user!.email).to.equal('test@example.org')
|
|
})
|
|
|
|
it('Validate password should ignore email casing', async () => {
|
|
expect(
|
|
await validateUserPassword({ email: 'BiLL@GaTES.cOm', password: 'testthebest' })
|
|
)
|
|
})
|
|
|
|
let ballmerUser: { id: string; email: string }
|
|
|
|
it('Find or create should create a user', async () => {
|
|
const newUser: { name: string; email: string; password: string } = {
|
|
name: 'Steve Ballmer Balls',
|
|
email: 'ballmer@example.test',
|
|
password: 'testthebest'
|
|
}
|
|
|
|
ballmerUser = await findOrCreateUser({ user: newUser })
|
|
expect(ballmerUser.id).to.be.a('string')
|
|
const user = await getUser(ballmerUser.id)
|
|
expect(user.verified).to.equal(true)
|
|
})
|
|
|
|
it('Find or create should NOT create a user', async () => {
|
|
const newUser: { name: string; email: string; password: string } = {
|
|
name: 'Steve Ballmer Balls',
|
|
email: 'ballmer@example.test',
|
|
password: 'testthebest'
|
|
}
|
|
|
|
const { id } = await findOrCreateUser({ user: newUser })
|
|
expect(id).to.equal(ballmerUser.id)
|
|
})
|
|
|
|
// Note: deletion is more complicated.
|
|
it('Should delete a user @multiregion', async () => {
|
|
const soloOwnerStream = await createTestStream(
|
|
{
|
|
name: 'Test Stream 01',
|
|
description: 'wonderful test stream',
|
|
isPublic: true
|
|
},
|
|
{
|
|
...ballmerUser,
|
|
name: ''
|
|
}
|
|
)
|
|
const multiOwnerStream = await createTestStream(
|
|
{
|
|
name: 'Test Stream 02',
|
|
description: 'another test stream',
|
|
isPublic: true
|
|
},
|
|
{
|
|
...ballmerUser,
|
|
name: ''
|
|
}
|
|
)
|
|
|
|
await grantPermissionsStream({
|
|
streamId: multiOwnerStream.id,
|
|
userId: myTestActor.id,
|
|
role: Roles.Stream.Owner
|
|
})
|
|
|
|
// create a branch for ballmer on the multiowner stream
|
|
const branch = { name: 'ballmer/dev', id: '', description: null }
|
|
branch.id = (
|
|
await createBranch({
|
|
...branch,
|
|
streamId: multiOwnerStream.id,
|
|
authorId: ballmerUser.id!
|
|
})
|
|
).id
|
|
|
|
const branchSecond = { name: 'steve/jobs', id: '', description: null }
|
|
branchSecond.id = (
|
|
await createBranch({
|
|
...branchSecond,
|
|
streamId: multiOwnerStream.id,
|
|
authorId: myTestActor.id
|
|
})
|
|
).id
|
|
|
|
// create an object and a commit around it on the multiowner stream
|
|
const objId = await createObject({
|
|
streamId: multiOwnerStream.id,
|
|
object: { pie: 'in the sky' }
|
|
})
|
|
const commitId = (
|
|
await createCommitByBranchName({
|
|
streamId: multiOwnerStream.id,
|
|
branchName: 'ballmer/dev',
|
|
message: 'breakfast commit',
|
|
sourceApplication: 'tests',
|
|
objectId: objId,
|
|
authorId: ballmerUser.id!
|
|
})
|
|
).id
|
|
|
|
await deleteUser(ballmerUser.id!)
|
|
|
|
if ((await getStream({ streamId: soloOwnerStream.id })) !== undefined) {
|
|
assert.fail('user stream not deleted')
|
|
}
|
|
|
|
const multiOwnerStreamCopy = await getStream({ streamId: multiOwnerStream.id })
|
|
if (!multiOwnerStreamCopy || multiOwnerStreamCopy.id !== multiOwnerStream.id) {
|
|
assert.fail('shared stream deleted')
|
|
}
|
|
|
|
const branches = await getBranchesByStreamId(multiOwnerStream.id)
|
|
expect(branches.items.length).to.equal(3)
|
|
|
|
const branchCommits = await getCommitsByBranchName({
|
|
streamId: multiOwnerStream.id,
|
|
branchName: 'ballmer/dev',
|
|
limit: 10
|
|
})
|
|
expect(branchCommits.commits.length).to.equal(1)
|
|
|
|
const commit = await getCommit(commitId, { streamId: multiOwnerStream.id })
|
|
expect(commit).to.be.not.null
|
|
|
|
const commitsByStreamId = await getCommitsByStreamId({
|
|
streamId: multiOwnerStream.id
|
|
})
|
|
expect(commitsByStreamId.commits.length).to.equal(1)
|
|
|
|
const user = await getUser(ballmerUser.id!)
|
|
if (user) assert.fail('user not deleted')
|
|
})
|
|
|
|
it('Should not delete the last admin user', async () => {
|
|
try {
|
|
await deleteUser(myTestActor.id)
|
|
assert.fail('boom')
|
|
} catch (err) {
|
|
expect(ensureError(err).message).to.equal(
|
|
'Cannot remove the last admin role from the server'
|
|
)
|
|
}
|
|
})
|
|
|
|
it('Should get a user', async () => {
|
|
const actor = await getUser(myTestActor.id)
|
|
expect(actor).to.not.have.property('passwordDigest')
|
|
})
|
|
|
|
it('Should search and get users', async () => {
|
|
const email = createRandomEmail()
|
|
await createUser({
|
|
name: 'Bill Gates',
|
|
password: 'sn3aky-1337-b1m',
|
|
email
|
|
})
|
|
const { users } = await searchUsers('gates', 20)
|
|
expect(users).to.have.lengthOf(1)
|
|
expect(users[0].name).to.equal('Bill Gates')
|
|
})
|
|
|
|
it('Should not search for archived users unless explicitly asked', async () => {
|
|
const toBeArchivedId = await createUser({
|
|
name: 'Miss Library Lady',
|
|
email: 'will@be.archived',
|
|
password: 'ilikebooks'
|
|
})
|
|
|
|
await createUser({
|
|
name: 'Not in the Library',
|
|
email: 'i@will.survive',
|
|
password: 'nanananananaaaa'
|
|
})
|
|
|
|
await changeUserRole({ userId: toBeArchivedId, role: Roles.Server.ArchivedUser })
|
|
|
|
let { users } = await searchUsers('Library', 20)
|
|
expect(users).to.have.lengthOf(1)
|
|
|
|
users = (await searchUsers('Library', 20, undefined, true)).users
|
|
expect(users).to.have.lengthOf(2)
|
|
})
|
|
|
|
it('Should update a user', async () => {
|
|
const updatedActor = { ...myTestActor }
|
|
updatedActor.name = 'didimitrie'
|
|
|
|
await updateUser(myTestActor.id, updatedActor)
|
|
|
|
const actor = await getUser(myTestActor.id)
|
|
expect(actor.name).to.equal(updatedActor.name)
|
|
})
|
|
|
|
it('Should not update password', async () => {
|
|
const updatedActor = { ...myTestActor }
|
|
updatedActor.password = 'failwhale'
|
|
|
|
await updateUser(myTestActor.id, updatedActor)
|
|
|
|
const match = await validateUserPassword({
|
|
email: myTestActor.email,
|
|
password: 'failwhale'
|
|
})
|
|
expect(match).to.equal(false)
|
|
})
|
|
|
|
it('Should validate user password', async () => {
|
|
const actor = {
|
|
password: 'super-test-200',
|
|
email: 'e@ma.il',
|
|
name: 'Bob Gates'
|
|
}
|
|
|
|
await createUser(actor)
|
|
|
|
const match = await validateUserPassword({
|
|
email: actor.email,
|
|
password: 'super-test-200'
|
|
})
|
|
expect(match).to.equal(true)
|
|
const matchWrong = await validateUserPassword({
|
|
email: actor.email,
|
|
password: 'super-test-2000'
|
|
})
|
|
expect(matchWrong).to.equal(false)
|
|
})
|
|
|
|
it('Should update the password of a user', async () => {
|
|
const id = await createUser({
|
|
name: 'D',
|
|
email: 'tester@mcbester.com',
|
|
password: 'H4!b5at+kWls-8yh4Guq'
|
|
}) // https://mostsecure.pw
|
|
await updateUserPassword({ id, newPassword: 'Hello Dogs and Cats' })
|
|
|
|
const match = await validateUserPassword({
|
|
email: 'tester@mcbester.com',
|
|
password: 'Hello Dogs and Cats'
|
|
})
|
|
expect(match).to.equal(true)
|
|
})
|
|
})
|
|
|
|
describe('API Tokens @core-apitokens', () => {
|
|
let myFirstToken: string
|
|
let pregeneratedToken: string
|
|
let revokedToken: string
|
|
let expireSoonToken: string
|
|
|
|
before(async () => {
|
|
pregeneratedToken = await createPersonalAccessToken(myTestActor.id, 'Whabadub', [
|
|
Scopes.Streams.Read,
|
|
Scopes.Streams.Write,
|
|
Scopes.Profile.Read,
|
|
Scopes.Users.Email
|
|
])
|
|
revokedToken = await createPersonalAccessToken(myTestActor.id, 'Mr. Revoked', [
|
|
Scopes.Streams.Read
|
|
])
|
|
expireSoonToken = await createPersonalAccessToken(
|
|
myTestActor.id,
|
|
'Mayfly',
|
|
[Scopes.Streams.Read],
|
|
1
|
|
) // 1ms lifespan
|
|
})
|
|
|
|
it('Should create a personal api token', async () => {
|
|
const scopes = [Scopes.Streams.Write, Scopes.Profile.Read]
|
|
const name = 'My Test Token'
|
|
|
|
myFirstToken = await createPersonalAccessToken(myTestActor.id, name, scopes)
|
|
expect(myFirstToken).to.have.lengthOf(42)
|
|
})
|
|
|
|
// it( 'Should create an api token for an app', async ( ) => {
|
|
// let test = await createAppToken( { userId: myTestActor.id, appId: 'spklwebapp' } )
|
|
// expect( test ).to.have.lengthOf( 42 )
|
|
// } )
|
|
|
|
it('Should validate a token', async () => {
|
|
const res = await validateToken(pregeneratedToken)
|
|
expect(res).to.have.property('valid')
|
|
expect(res.valid).to.equal(true)
|
|
expect(res).to.have.property('scopes')
|
|
expect(res).to.have.property('userId')
|
|
expect(res).to.have.property('role')
|
|
})
|
|
|
|
it('Should revoke an api token', async () => {
|
|
await revokeToken(revokedToken, myTestActor.id)
|
|
const res = await validateToken(revokedToken)
|
|
expect(res).to.have.property('valid')
|
|
expect(res.valid).to.equal(false)
|
|
})
|
|
|
|
it('Should refuse an expired token', async () => {
|
|
const res = await validateToken(expireSoonToken)
|
|
expect(res.valid).to.equal(false)
|
|
// assert.fail( )
|
|
})
|
|
|
|
it('Should get the tokens of an user', async () => {
|
|
const userTokens = await getUserTokens(myTestActor.id)
|
|
expect(userTokens).to.be.an('array')
|
|
expect(userTokens).to.have.lengthOf(2)
|
|
})
|
|
})
|
|
})
|