diff --git a/dev/tool/src/index.ts b/dev/tool/src/index.ts index 6b6fc1891e..a76e9f8db2 100644 --- a/dev/tool/src/index.ts +++ b/dev/tool/src/index.ts @@ -114,11 +114,13 @@ export function devTool ( .command('create-account ') .description('create user and corresponding account in master database') .requiredOption('-p, --password ', 'user password') + .requiredOption('-f, --first ', 'first name') + .requiredOption('-l, --last ', 'last name') .action(async (email: string, cmd) => { const { mongodbUri } = prepareTools() return await withDatabase(mongodbUri, async (db) => { console.log(`creating account ${cmd.first as string} ${cmd.last as string} (${email})...`) - await createAcc(db, productId, email, cmd.password, true) + await createAcc(db, productId, email, cmd.password, cmd.first, cmd.last, true) }) }) @@ -137,13 +139,11 @@ export function devTool ( program .command('assign-workspace ') .description('assign workspace') - .requiredOption('-f, --first ', 'first name') - .requiredOption('-l, --last ', 'last name') .action(async (email: string, workspace: string, cmd) => { const { mongodbUri } = prepareTools() return await withDatabase(mongodbUri, async (db, client) => { console.log(`assigning user ${email} to ${workspace}...`) - await assignWorkspace(db, productId, email, workspace, cmd.first, cmd.last) + await assignWorkspace(db, productId, email, workspace) }) }) diff --git a/plugins/login-assets/lang/en.json b/plugins/login-assets/lang/en.json index a0e3fd7674..2cc8338a5f 100644 --- a/plugins/login-assets/lang/en.json +++ b/plugins/login-assets/lang/en.json @@ -27,7 +27,12 @@ "WantAnotherWorkspace": "Want to create another workspace?", "ChangeAccount": "Change account", "NotSeeingWorkspace": "Not seeing your workspace?", - "WorkspaceNameRule": "The workspace name can contain lowercase letters, numbers and symbol - (inside name)", + "WorkspaceNameRule": "Workspace name cannot contain special characters except -", + "WorkspaceNameRuleCapital": "The workspace name must contain only lowercase letters", + "WorkspaceNameRuleHyphen": "The workspace name cannot start with a dash (-)", + "WorkspaceNameRuleHyphenEnd": "The workspace name cannot end with a dash (-)", + "WorkspaceNameRuleLengthLow": "Workspace name must be at least 3 characters", + "WorkspaceNameRuleLengthHigh": "Workspace name must be no longer than 63 characters", "ForgotPassword": "Forgot your password?", "KnowPassword": "Know your password?", "Recover": "Recover", diff --git a/plugins/login-assets/lang/ru.json b/plugins/login-assets/lang/ru.json index f38b2611bc..4055db9dd7 100644 --- a/plugins/login-assets/lang/ru.json +++ b/plugins/login-assets/lang/ru.json @@ -27,7 +27,12 @@ "WantAnotherWorkspace": "Хотите создать другое рабочее пространство?", "ChangeAccount": "Сменить пользователя", "NotSeeingWorkspace": "Не видите ваше рабочее пространство?", - "WorkspaceNameRule": "Название рабочего пространства должно состоять из строчных латинских букв, цифр и символа - (внутри имени)", + "WorkspaceNameRule": "Название рабочего пространства не может содержать специальные символы кроме -", + "WorkspaceNameRuleCapital": "Название рабочего пространства должно содержать только строчные буквы", + "WorkspaceNameRuleHyphen": "Название рабочего пространства не может начинаться с символа 'тире' (-)", + "WorkspaceNameRuleHyphenEnd": "Название рабочего пространства не может заканчиваться символом 'тире' (-)", + "WorkspaceNameRuleLengthLow": "Название рабочего пространства должно быть не короче 3 символов", + "WorkspaceNameRuleLengthHigh": "Название рабочего пространства должно быть не длиннее 63 символов", "ForgotPassword": "Забыли пароль?", "KnowPassword": "Знаете пароль?", "Recover": "Восстановить", diff --git a/plugins/login-resources/src/components/CreateWorkspaceForm.svelte b/plugins/login-resources/src/components/CreateWorkspaceForm.svelte index 7527efe48b..3e21a71aa2 100644 --- a/plugins/login-resources/src/components/CreateWorkspaceForm.svelte +++ b/plugins/login-resources/src/components/CreateWorkspaceForm.svelte @@ -25,20 +25,46 @@ import { onMount } from 'svelte' const fields = [ - { id: 'given-name', name: 'first', i18n: login.string.FirstName, short: true }, - { id: 'family-name', name: 'last', i18n: login.string.LastName, short: true }, { name: 'workspace', i18n: login.string.Workspace, - rule: /^[0-9a-z][0-9a-z-]{2,62}[0-9a-z]$/, - ruleDescr: login.string.WorkspaceNameRule + rules: [ + { + rule: /^-/, + notMatch: true, + ruleDescr: login.string.WorkspaceNameRuleHyphen + }, + { + rule: /-$/, + notMatch: true, + ruleDescr: login.string.WorkspaceNameRuleHyphenEnd + }, + { + rule: /[A-Z]/, + notMatch: true, + ruleDescr: login.string.WorkspaceNameRuleCapital + }, + { + rule: /^[0-9a-z-]+$/, + notMatch: false, + ruleDescr: login.string.WorkspaceNameRule + }, + { + rule: /^[0-9a-z-]{3,}$/, + notMatch: false, + ruleDescr: login.string.WorkspaceNameRuleLengthLow + }, + { + rule: /^[0-9a-z-]{3,63}$/, + notMatch: false, + ruleDescr: login.string.WorkspaceNameRuleLengthHigh + } + ] } ] const object = { - workspace: '', - first: '', - last: '' + workspace: '' } let status: Status = OK @@ -58,7 +84,7 @@ func: async () => { status = new Status(Severity.INFO, login.status.ConnectingToServer, {}) - const [loginStatus, result] = await createWorkspace(object.workspace, object.first, object.last) + const [loginStatus, result] = await createWorkspace(object.workspace) status = loginStatus if (result !== undefined) { diff --git a/plugins/login-resources/src/components/Form.svelte b/plugins/login-resources/src/components/Form.svelte index c7973b4d64..bdebb1c3a4 100644 --- a/plugins/login-resources/src/components/Form.svelte +++ b/plugins/login-resources/src/components/Form.svelte @@ -38,8 +38,11 @@ password?: boolean optional?: boolean short?: boolean - rule?: RegExp - ruleDescr?: IntlString + rules?: { + rule: RegExp + notMatch: boolean + ruleDescr: IntlString + }[] } interface Action { @@ -89,12 +92,12 @@ } } } - if (f.rule !== undefined) { - if (!f.rule.test(v)) { - status = new Status(Severity.INFO, login.status.IncorrectValue, { - field: await translate(field.i18n, {}, language) - }) - return false + if (f.rules !== undefined) { + for (const rule of f.rules) { + if (rule.rule.test(v) === rule.notMatch) { + status = new Status(Severity.INFO, rule.ruleDescr, {}) + return false + } } } } @@ -185,11 +188,6 @@ trim(field.name) }} /> - {#if field.ruleDescr} -
-
- {/if} {/each} diff --git a/plugins/login-resources/src/components/Join.svelte b/plugins/login-resources/src/components/Join.svelte index 8ca1985089..51b7b67445 100644 --- a/plugins/login-resources/src/components/Join.svelte +++ b/plugins/login-resources/src/components/Join.svelte @@ -30,8 +30,6 @@ $: fields = page === 'login' ? [ - { id: 'given-name', name: 'first', i18n: login.string.FirstName, short: true }, - { id: 'family-name', name: 'last', i18n: login.string.LastName, short: true }, { id: 'email', name: 'username', i18n: login.string.Email }, { id: 'current-password', @@ -65,7 +63,7 @@ const [loginStatus, result] = page === 'login' - ? await join(object.username, object.password, object.first, object.last, location.query?.inviteId ?? '') + ? await join(object.username, object.password, location.query?.inviteId ?? '') : await signUpJoin( object.username, object.password, diff --git a/plugins/login-resources/src/components/SignupForm.svelte b/plugins/login-resources/src/components/SignupForm.svelte index 482bf19393..102c4c5bc2 100644 --- a/plugins/login-resources/src/components/SignupForm.svelte +++ b/plugins/login-resources/src/components/SignupForm.svelte @@ -22,6 +22,8 @@ import Form from './Form.svelte' const fields = [ + { id: 'given-name', name: 'first', i18n: login.string.FirstName, short: true }, + { id: 'family-name', name: 'last', i18n: login.string.LastName, short: true }, { id: 'email', name: 'username', i18n: login.string.Email }, { id: 'new-password', name: 'password', i18n: login.string.Password, password: true }, { id: 'new-password', name: 'password2', i18n: login.string.PasswordRepeat, password: true } @@ -42,7 +44,7 @@ func: async () => { status = new Status(Severity.INFO, login.status.ConnectingToServer, {}) - const [loginStatus, result] = await signUp(object.username, object.password) + const [loginStatus, result] = await signUp(object.username, object.password, object.first, object.last) status = loginStatus diff --git a/plugins/login-resources/src/plugin.ts b/plugins/login-resources/src/plugin.ts index 2f42747a58..9e2bbd4dda 100644 --- a/plugins/login-resources/src/plugin.ts +++ b/plugins/login-resources/src/plugin.ts @@ -49,6 +49,11 @@ export default mergeIds(loginId, login, { NotSeeingWorkspace: '' as IntlString, ChangeAccount: '' as IntlString, WorkspaceNameRule: '' as IntlString, + WorkspaceNameRuleHyphen: '' as IntlString, + WorkspaceNameRuleHyphenEnd: '' as IntlString, + WorkspaceNameRuleLengthLow: '' as IntlString, + WorkspaceNameRuleLengthHigh: '' as IntlString, + WorkspaceNameRuleCapital: '' as IntlString, ForgotPassword: '' as IntlString, Recover: '' as IntlString, KnowPassword: '' as IntlString, diff --git a/plugins/login-resources/src/utils.ts b/plugins/login-resources/src/utils.ts index bffaa46c30..3d46986f8e 100644 --- a/plugins/login-resources/src/utils.ts +++ b/plugins/login-resources/src/utils.ts @@ -76,7 +76,12 @@ export async function doLogin (email: string, password: string): Promise<[Status } } -export async function signUp (email: string, password: string): Promise<[Status, LoginInfo | undefined]> { +export async function signUp ( + email: string, + password: string, + first: string, + last: string +): Promise<[Status, LoginInfo | undefined]> { const accountsUrl = getMetadata(login.metadata.AccountsUrl) if (accountsUrl === undefined) { @@ -93,7 +98,7 @@ export async function signUp (email: string, password: string): Promise<[Status, const request = { method: 'createAccount', - params: [email, password] + params: [email, password, first, last] } try { @@ -111,11 +116,7 @@ export async function signUp (email: string, password: string): Promise<[Status, } } -export async function createWorkspace ( - workspace: string, - firstName: string, - lastName: string -): Promise<[Status, LoginInfo | undefined]> { +export async function createWorkspace (workspace: string): Promise<[Status, LoginInfo | undefined]> { const accountsUrl = getMetadata(login.metadata.AccountsUrl) if (accountsUrl === undefined) { @@ -142,7 +143,7 @@ export async function createWorkspace ( const request = { method: 'createWorkspace', - params: [workspace, firstName, lastName] + params: [workspace] } try { @@ -425,8 +426,6 @@ export async function getInviteLink (expHours: number = 1, emailMask: string = ' export async function join ( email: string, password: string, - first: string, - last: string, inviteId: string ): Promise<[Status, WorkspaceLoginInfo | undefined]> { const accountsUrl = getMetadata(login.metadata.AccountsUrl) @@ -445,7 +444,7 @@ export async function join ( const request = { method: 'join', - params: [email, password, first, last, inviteId] + params: [email, password, inviteId] } try { diff --git a/server/account/src/__tests__/account.test.ts b/server/account/src/__tests__/account.test.ts index 6407aefb02..42e4e9c7e0 100644 --- a/server/account/src/__tests__/account.test.ts +++ b/server/account/src/__tests__/account.test.ts @@ -87,7 +87,7 @@ describe('server', () => { }) await methods.assignWorkspace(db, '', { method: 'assignWorkspace', - params: ['andrey', workspace, 'firstName', 'lastName'] + params: ['andrey', workspace] }) const request: any = { @@ -140,7 +140,7 @@ describe('server', () => { }) await methods.assignWorkspace(db, '', { method: 'assignWorkspace', - params: ['andrey', workspace, 'first', 'last'] + params: ['andrey', workspace] }) // Check we had one diff --git a/server/account/src/index.ts b/server/account/src/index.ts index 8b5e497a4f..f48583c3df 100644 --- a/server/account/src/index.ts +++ b/server/account/src/index.ts @@ -101,6 +101,8 @@ export interface Account { salt: Binary workspaces: ObjectId[] // Defined for server admins only + first: string + last: string admin?: boolean confirmed?: boolean lastWorkspace?: number @@ -347,14 +349,12 @@ export async function join ( productId: string, email: string, password: string, - firstName: string, - lastName: string, inviteId: ObjectId ): Promise { const invite = await getInvite(db, inviteId) const workspace = await checkInvite(invite, email) console.log(`join attempt:${email}, ${workspace.name}`) - await assignWorkspace(db, productId, email, workspace.name, firstName, lastName) + await assignWorkspace(db, productId, email, workspace.name) const token = (await login(db, productId, email, password)).token const result = await selectWorkspace(db, productId, token, workspace.name) @@ -470,8 +470,8 @@ export async function signUpJoin ( console.log(`signup join:${email} ${first} ${last}`) const invite = await getInvite(db, inviteId) const workspace = await checkInvite(invite, email) - await createAcc(db, productId, email, password, invite?.emailMask === email) - await assignWorkspace(db, productId, email, workspace.name, first, last) + await createAcc(db, productId, email, password, first, last, invite?.emailMask === email) + await assignWorkspace(db, productId, email, workspace.name) const token = (await login(db, productId, email, password)).token const result = await selectWorkspace(db, productId, token, workspace.name) @@ -487,6 +487,8 @@ export async function createAcc ( productId: string, email: string, password: string, + first: string, + last: string, confirmed: boolean = false ): Promise { const salt = randomBytes(32) @@ -506,6 +508,8 @@ export async function createAcc ( email, hash, salt, + first, + last, confirmed, workspaces: [] }) @@ -523,8 +527,15 @@ export async function createAcc ( /** * @public */ -export async function createAccount (db: Db, productId: string, email: string, password: string): Promise { - const account = await createAcc(db, productId, email, password, false) +export async function createAccount ( + db: Db, + productId: string, + email: string, + password: string, + first: string, + last: string +): Promise { + const account = await createAcc(db, productId, email, password, first, last, false) const result = { endpoint: getEndpoint(), @@ -623,14 +634,7 @@ export async function upgradeWorkspace ( */ export const createUserWorkspace = (version: Data, txes: Tx[], migrationOperation: [string, MigrateOperation][]) => - async ( - db: Db, - productId: string, - token: string, - workspace: string, - firstName: string, - lastName: string - ): Promise => { + async (db: Db, productId: string, token: string, workspace: string): Promise => { if (!/^[0-9a-z][0-9a-z-]{2,62}[0-9a-z]$/.test(workspace)) { throw new PlatformError(new Status(Severity.ERROR, platform.status.InvalidId, { id: workspace })) } @@ -678,7 +682,7 @@ export const createUserWorkspace = // Update last workspace time. await db.collection(ACCOUNT_COLLECTION).updateOne({ _id: info._id }, { $set: { lastWorkspace: Date.now() } }) - await assignWorkspace(db, productId, email, workspace, firstName, lastName) + await assignWorkspace(db, productId, email, workspace) await setRole(email, workspace, productId, AccountRole.Owner) const result = { endpoint: getEndpoint(), @@ -787,14 +791,7 @@ export async function setRole (email: string, workspace: string, productId: stri /** * @public */ -export async function assignWorkspace ( - db: Db, - productId: string, - email: string, - workspace: string, - firstName: string, - lastName: string -): Promise { +export async function assignWorkspace (db: Db, productId: string, email: string, workspace: string): Promise { const initWS = getMetadata(toolPlugin.metadata.InitWorkspace) if (initWS !== undefined && initWS === workspace) { throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) @@ -802,7 +799,7 @@ export async function assignWorkspace ( const { workspaceId, accountId } = await getWorkspaceAndAccount(db, productId, email, workspace) const account = await db.collection(ACCOUNT_COLLECTION).findOne({ _id: accountId }) - if (account !== null) await createPersonAccount(account, productId, workspace, firstName, lastName) + if (account !== null) await createPersonAccount(account, productId, workspace) // Add account into workspace. await db.collection(WORKSPACE_COLLECTION).updateOne({ _id: workspaceId }, { $addToSet: { accounts: accountId } }) @@ -836,18 +833,12 @@ async function createEmployee (ops: TxOperations, name: string, email: string): return id } -async function createPersonAccount ( - account: Account, - productId: string, - workspace: string, - firstName: string, - lastName: string -): Promise { +async function createPersonAccount (account: Account, productId: string, workspace: string): Promise { const connection = await connect(getTransactor(), getWorkspaceId(workspace, productId)) try { const ops = new TxOperations(connection, core.account.System) - const name = combineName(firstName, lastName) + const name = combineName(account.first, account.last) // Check if EmployeeAccoun is not exists const existingAccount = await ops.findOne(contact.class.PersonAccount, { email: account.email }) if (existingAccount === undefined) { diff --git a/tests/create-local.sh b/tests/create-local.sh index 0a77956485..ae653c92b6 100755 --- a/tests/create-local.sh +++ b/tests/create-local.sh @@ -10,7 +10,7 @@ export SERVER_SECRET=secret # Create workspace record in accounts node ../dev/tool/bundle.js create-workspace sanity-ws -o SanityTest # Create user record in accounts -node ../dev/tool/bundle.js create-account user1 -p 1234 +node ../dev/tool/bundle.js create-account user1 -f John -l Appleseed -p 1234 node ../dev/tool/bundle.js confirm-email user1 @@ -20,7 +20,7 @@ node ../dev/tool/bundle.js backup-restore ./sanity-ws sanity-ws node ../dev/tool/bundle.js upgrade-workspace sanity-ws # Re-assign user to workspace. -node ../dev/tool/bundle.js assign-workspace user1 sanity-ws -f John -l Appleseed +node ../dev/tool/bundle.js assign-workspace user1 sanity-ws node ../dev/tool/bundle.js configure sanity-ws --enable=* node ../dev/tool/bundle.js configure sanity-ws --list \ No newline at end of file diff --git a/tests/prepare.sh b/tests/prepare.sh index d79b0e293c..3a45d3407e 100755 --- a/tests/prepare.sh +++ b/tests/prepare.sh @@ -9,7 +9,7 @@ docker-compose -p sanity up -d --force-recreate --renew-anon-volumes # Create workspace record in accounts ./tool.sh create-workspace sanity-ws -o SanityTest # Create user record in accounts -./tool.sh create-account user1 -p 1234 +./tool.sh create-account user1 -f John -l Appleseed -p 1234 # Make user the workspace maintainer ./tool.sh set-user-role user1 sanity-ws 1 ./tool.sh confirm-email user1 diff --git a/tests/restore-local.sh b/tests/restore-local.sh index b3a0372719..9b10bd5512 100755 --- a/tests/restore-local.sh +++ b/tests/restore-local.sh @@ -13,7 +13,7 @@ node ../dev/tool/bundle.js backup-restore ./sanity-ws sanity-ws node ../dev/tool/bundle.js upgrade-workspace sanity-ws # Re-assign user to workspace. -node ../dev/tool/bundle.js assign-workspace user1 sanity-ws -f John -l Appleseed +node ../dev/tool/bundle.js assign-workspace user1 sanity-ws node ../dev/tool/bundle.js configure sanity-ws --enable=* node ../dev/tool/bundle.js configure sanity-ws --list \ No newline at end of file diff --git a/tests/restore-workspace.sh b/tests/restore-workspace.sh index dc08080b41..e2853e17f8 100755 --- a/tests/restore-workspace.sh +++ b/tests/restore-workspace.sh @@ -6,7 +6,7 @@ ./tool.sh upgrade-workspace sanity-ws # Re-assign user to workspace. -./tool.sh assign-workspace user1 sanity-ws -f John -l Appleseed +./tool.sh assign-workspace user1 sanity-ws ./tool.sh configure sanity-ws --enable=* ./tool.sh configure sanity-ws --list