mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-13 03:40:48 +00:00
account tests
Signed-off-by: Andrey Platov <andrey@hardcoreeng.com>
This commit is contained in:
parent
6011a75e05
commit
40950b5ca9
157
server/account/src/__tests__/account.test.ts
Normal file
157
server/account/src/__tests__/account.test.ts
Normal file
@ -0,0 +1,157 @@
|
||||
//
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { MongoClient, Db } from 'mongodb'
|
||||
import { methods, getAccount, getWorkspace } from '..'
|
||||
import { randomBytes } from 'crypto'
|
||||
const DB_NAME = 'test_accounts'
|
||||
|
||||
describe('server', () => {
|
||||
const dbUri = process.env.MONGODB_URI ?? 'mongodb://localhost:27017'
|
||||
let conn: MongoClient
|
||||
let db: Db
|
||||
let workspace: string = 'ws-' + randomBytes(8).toString('hex')
|
||||
|
||||
beforeAll(async () => {
|
||||
conn = await MongoClient.connect(dbUri)
|
||||
})
|
||||
beforeEach(async () => {
|
||||
const olddb = conn.db(DB_NAME)
|
||||
await olddb.dropDatabase()
|
||||
db = conn.db(DB_NAME)
|
||||
await db.collection('account').createIndex({ email: 1 }, { unique: true })
|
||||
await db.collection('workspace').createIndex({ workspace: 1 }, { unique: true })
|
||||
})
|
||||
|
||||
it('should create workspace', async () => {
|
||||
const request: any = {
|
||||
method: 'createWorkspace',
|
||||
params: [workspace, 'ООО Рога и Копыта']
|
||||
}
|
||||
|
||||
const result = await methods.createWorkspace(db, request)
|
||||
expect(result.result).toBeDefined()
|
||||
workspace = result.result as string
|
||||
})
|
||||
|
||||
it('should create account', async () => {
|
||||
const request: any = {
|
||||
method: 'createAccount',
|
||||
params: ['andrey2', '123']
|
||||
}
|
||||
|
||||
const result = await methods.createAccount(db, request)
|
||||
expect(result.result).toBeDefined()
|
||||
})
|
||||
|
||||
it('should not create, duplicate account', async () => {
|
||||
await methods.createAccount(db, {
|
||||
method: 'createAccount',
|
||||
params: ['andrey', '123']
|
||||
})
|
||||
|
||||
const request: any = {
|
||||
method: 'createAccount',
|
||||
params: ['andrey', '123']
|
||||
}
|
||||
|
||||
const result = await methods.createAccount(db, request)
|
||||
expect(result.error).toBeDefined()
|
||||
})
|
||||
|
||||
it('should login', async () => {
|
||||
await methods.createAccount(db, {
|
||||
method: 'createAccount',
|
||||
params: ['andrey', '123']
|
||||
})
|
||||
await methods.createWorkspace(db, {
|
||||
method: 'createWorkspace',
|
||||
params: [workspace, 'ООО Рога и Копыта']
|
||||
})
|
||||
await methods.assignWorkspace(db, {
|
||||
method: 'assignWorkspace',
|
||||
params: ['andrey', workspace]
|
||||
})
|
||||
|
||||
const request: any = {
|
||||
method: 'login',
|
||||
params: ['andrey', '123', workspace]
|
||||
}
|
||||
|
||||
const result = await methods.login(db, request)
|
||||
expect(result.result).toBeDefined()
|
||||
})
|
||||
|
||||
it('should not login, wrong password', async () => {
|
||||
const request: any = {
|
||||
method: 'login',
|
||||
params: ['andrey', '123555', workspace]
|
||||
}
|
||||
|
||||
const result = await methods.login(db, request)
|
||||
expect(result.error).toBeDefined()
|
||||
})
|
||||
|
||||
it('should not login, unknown user', async () => {
|
||||
const request: any = {
|
||||
method: 'login',
|
||||
params: ['andrey1', '123555', workspace]
|
||||
}
|
||||
|
||||
const result = await methods.login(db, request)
|
||||
expect(result.error).toBeDefined()
|
||||
})
|
||||
|
||||
it('should not login, wrong workspace', async () => {
|
||||
const request: any = {
|
||||
method: 'login',
|
||||
params: ['andrey', '123', 'non-existent-workspace']
|
||||
}
|
||||
|
||||
const result = await methods.login(db, request)
|
||||
expect(result.error).toBeDefined()
|
||||
})
|
||||
|
||||
it('do remove workspace', async () => {
|
||||
await methods.createAccount(db, {
|
||||
method: 'createAccount',
|
||||
params: ['andrey', '123']
|
||||
})
|
||||
await methods.createWorkspace(db, {
|
||||
method: 'createWorkspace',
|
||||
params: [workspace, 'ООО Рога и Копыта']
|
||||
})
|
||||
await methods.assignWorkspace(db, {
|
||||
method: 'assignWorkspace',
|
||||
params: ['andrey', workspace]
|
||||
})
|
||||
|
||||
// Check we had one
|
||||
expect((await getAccount(db, 'andrey'))?.workspaces.length).toEqual(1)
|
||||
expect((await getWorkspace(db, workspace))?.accounts.length).toEqual(1)
|
||||
|
||||
await methods.removeWorkspace(db, {
|
||||
method: 'removeWorkspace',
|
||||
params: ['andrey', workspace]
|
||||
})
|
||||
expect((await getAccount(db, 'andrey'))?.workspaces.length).toEqual(0)
|
||||
expect((await getWorkspace(db, workspace))?.accounts.length).toEqual(0)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await conn.close()
|
||||
})
|
||||
})
|
@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Plugin, StatusCode } from '@anticrm/platform'
|
||||
import { PlatformError, Severity, Status, plugin } from '@anticrm/platform'
|
||||
import type { Plugin, StatusCode, Request, Response } from '@anticrm/platform'
|
||||
import { PlatformError, Severity, Status, plugin, unknownStatus } from '@anticrm/platform'
|
||||
import { Binary, Db, ObjectId } from 'mongodb'
|
||||
import { pbkdf2Sync } from 'crypto'
|
||||
import { pbkdf2Sync, randomBytes } from 'crypto'
|
||||
import { encode } from 'jwt-simple'
|
||||
|
||||
const WORKSPACE_COLLECTION = 'workspace'
|
||||
@ -37,12 +37,18 @@ export const accountId = 'account' as Plugin
|
||||
const accountPlugin = plugin(accountId, {
|
||||
status: {
|
||||
AccountNotFound: '' as StatusCode<{account: string}>,
|
||||
WorkspaceNotFound: '' as StatusCode<{workspace: string}>,
|
||||
InvalidPassword: '' as StatusCode<{account: string}>,
|
||||
AccountAlreadyExists: '' as StatusCode<{account: string}>,
|
||||
WorkspaceAlreadyExists: '' as StatusCode<{workspace: string}>,
|
||||
Forbidden: '' as StatusCode
|
||||
}
|
||||
})
|
||||
|
||||
interface Account {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Account {
|
||||
_id: ObjectId
|
||||
email: string
|
||||
hash: Binary
|
||||
@ -50,7 +56,10 @@ interface Account {
|
||||
workspaces: ObjectId[]
|
||||
}
|
||||
|
||||
interface Workspace {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Workspace {
|
||||
_id: ObjectId
|
||||
workspace: string
|
||||
organisation: string
|
||||
@ -66,7 +75,10 @@ export interface LoginInfo {
|
||||
endpoint: string
|
||||
}
|
||||
|
||||
type AccountInfo = Omit<Account, 'hash' | 'salt'>
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type AccountInfo = Omit<Account, 'hash' | 'salt'>
|
||||
|
||||
function hashWithSalt (password: string, salt: Buffer): Buffer {
|
||||
return pbkdf2Sync(password, salt, 1000, 32, 'sha256')
|
||||
@ -76,11 +88,20 @@ function verifyPassword (password: string, hash: Buffer, salt: Buffer): boolean
|
||||
return Buffer.compare(hash, hashWithSalt(password, salt)) === 0
|
||||
}
|
||||
|
||||
async function getAccount (db: Db, email: string): Promise<Account | null> {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function getAccount (db: Db, email: string): Promise<Account | null> {
|
||||
return await db.collection(ACCOUNT_COLLECTION).findOne<Account>({ email })
|
||||
}
|
||||
|
||||
async function getWorkspace (db: Db, workspace: string): Promise<Workspace | null> {
|
||||
/**
|
||||
* @public
|
||||
* @param db -
|
||||
* @param workspace -
|
||||
* @returns
|
||||
*/
|
||||
export async function getWorkspace (db: Db, workspace: string): Promise<Workspace | null> {
|
||||
return await db.collection(WORKSPACE_COLLECTION).findOne<Workspace>({
|
||||
workspace
|
||||
})
|
||||
@ -137,4 +158,106 @@ export async function login (db: Db, email: string, password: string, workspace:
|
||||
throw new PlatformError(new Status(Severity.ERROR, accountPlugin.status.Forbidden, {}))
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function createAccount (db: Db, email: string, password: string): Promise<AccountInfo> {
|
||||
const salt = randomBytes(32)
|
||||
const hash = hashWithSalt(password, salt)
|
||||
|
||||
const account = await getAccount(db, email)
|
||||
if (account !== null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, accountPlugin.status.AccountAlreadyExists, { account: email }))
|
||||
}
|
||||
|
||||
const insert = await db.collection(ACCOUNT_COLLECTION).insertOne({
|
||||
email,
|
||||
hash,
|
||||
salt,
|
||||
workspaces: []
|
||||
})
|
||||
|
||||
return {
|
||||
_id: insert.insertedId,
|
||||
email,
|
||||
workspaces: []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function createWorkspace (db: Db, workspace: string, organisation: string): Promise<string> {
|
||||
if ((await getWorkspace(db, workspace)) !== null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, accountPlugin.status.WorkspaceAlreadyExists, { workspace }))
|
||||
}
|
||||
return await db
|
||||
.collection(WORKSPACE_COLLECTION)
|
||||
.insertOne({
|
||||
workspace,
|
||||
organisation
|
||||
})
|
||||
.then((e) => e.insertedId.toHexString())
|
||||
}
|
||||
|
||||
async function getWorkspaceAndAccount (db: Db, email: string, workspace: string): Promise<{ accountId: ObjectId, workspaceId: ObjectId }> {
|
||||
const wsPromise = await getWorkspace(db, workspace)
|
||||
if (wsPromise === null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, accountPlugin.status.WorkspaceNotFound, { workspace }))
|
||||
}
|
||||
const workspaceId = wsPromise._id
|
||||
const account = await getAccount(db, email)
|
||||
if (account === null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, accountPlugin.status.AccountNotFound, { account: email }))
|
||||
}
|
||||
const accountId = account._id
|
||||
return { accountId, workspaceId }
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function assignWorkspace (db: Db, email: string, workspace: string): Promise<void> {
|
||||
const { workspaceId, accountId } = await getWorkspaceAndAccount(db, email, workspace)
|
||||
// Add account into workspace.
|
||||
await db.collection(WORKSPACE_COLLECTION).updateOne({ _id: workspaceId }, { $push: { accounts: accountId } })
|
||||
|
||||
// Add workspace to account
|
||||
await db.collection(ACCOUNT_COLLECTION).updateOne({ _id: accountId }, { $push: { workspaces: workspaceId } })
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function removeWorkspace (db: Db, email: string, workspace: string): Promise<void> {
|
||||
const { workspaceId, accountId } = await getWorkspaceAndAccount(db, email, workspace)
|
||||
|
||||
// Add account into workspace.
|
||||
await db.collection(WORKSPACE_COLLECTION).updateOne({ _id: workspaceId }, { $pull: { accounts: accountId } })
|
||||
|
||||
// Add account a workspace
|
||||
await db.collection(ACCOUNT_COLLECTION).updateOne({ _id: accountId }, { $pull: { workspaces: workspaceId } })
|
||||
}
|
||||
|
||||
function wrap (f: (db: Db, ...args: any[]) => Promise<any>) {
|
||||
return async function (db: Db, request: Request<any[]>): Promise<Response<any>> {
|
||||
return await f(db, ...request.params)
|
||||
.then((result) => ({ id: request.id, result }))
|
||||
.catch((err) => ({ error: unknownStatus(err) }))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const methods = {
|
||||
login: wrap(login),
|
||||
getAccountInfo: wrap(getAccountInfo),
|
||||
createAccount: wrap(createAccount),
|
||||
createWorkspace: wrap(createWorkspace),
|
||||
assignWorkspace: wrap(assignWorkspace),
|
||||
removeWorkspace: wrap(removeWorkspace)
|
||||
// updateAccount: wrap(updateAccount)
|
||||
}
|
||||
|
||||
export default accountPlugin
|
||||
|
Loading…
Reference in New Issue
Block a user