Account fix (#3637)

This commit is contained in:
Denis Bykhov 2023-08-26 15:37:26 +06:00 committed by GitHub
parent 9a161ed71b
commit f56d65dc20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 111 additions and 82 deletions

View File

@ -114,11 +114,13 @@ export function devTool (
.command('create-account <email>')
.description('create user and corresponding account in master database')
.requiredOption('-p, --password <password>', 'user password')
.requiredOption('-f, --first <first>', 'first name')
.requiredOption('-l, --last <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 <email> <workspace>')
.description('assign workspace')
.requiredOption('-f, --first <first>', 'first name')
.requiredOption('-l, --last <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)
})
})

View File

@ -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",

View File

@ -27,7 +27,12 @@
"WantAnotherWorkspace": "Хотите создать другое рабочее пространство?",
"ChangeAccount": "Сменить пользователя",
"NotSeeingWorkspace": "Не видите ваше рабочее пространство?",
"WorkspaceNameRule": "Название рабочего пространства должно состоять из строчных латинских букв, цифр и символа - (внутри имени)",
"WorkspaceNameRule": "Название рабочего пространства не может содержать специальные символы кроме -",
"WorkspaceNameRuleCapital": "Название рабочего пространства должно содержать только строчные буквы",
"WorkspaceNameRuleHyphen": "Название рабочего пространства не может начинаться с символа 'тире' (-)",
"WorkspaceNameRuleHyphenEnd": "Название рабочего пространства не может заканчиваться символом 'тире' (-)",
"WorkspaceNameRuleLengthLow": "Название рабочего пространства должно быть не короче 3 символов",
"WorkspaceNameRuleLengthHigh": "Название рабочего пространства должно быть не длиннее 63 символов",
"ForgotPassword": "Забыли пароль?",
"KnowPassword": "Знаете пароль?",
"Recover": "Восстановить",

View File

@ -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]$/,
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<any> = 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) {

View File

@ -38,8 +38,11 @@
password?: boolean
optional?: boolean
short?: boolean
rule?: RegExp
ruleDescr?: IntlString
rules?: {
rule: RegExp
notMatch: boolean
ruleDescr: IntlString
}[]
}
interface Action {
@ -89,15 +92,15 @@
}
}
}
if (f.rule !== undefined) {
if (!f.rule.test(v)) {
status = new Status(Severity.INFO, login.status.IncorrectValue, {
field: await translate(field.i18n, {}, language)
})
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
}
}
}
}
status = OK
return true
}
@ -185,11 +188,6 @@
trim(field.name)
}}
/>
{#if field.ruleDescr}
<div class="hint">
<Label label={field.ruleDescr} />
</div>
{/if}
</div>
{/each}

View File

@ -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,

View File

@ -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

View File

@ -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,

View File

@ -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 {

View File

@ -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

View File

@ -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<WorkspaceLoginInfo> {
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<Account> {
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<LoginInfo> {
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<LoginInfo> {
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<Version>, txes: Tx[], migrationOperation: [string, MigrateOperation][]) =>
async (
db: Db,
productId: string,
token: string,
workspace: string,
firstName: string,
lastName: string
): Promise<LoginInfo> => {
async (db: Db, productId: string, token: string, workspace: string): Promise<LoginInfo> => {
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<void> {
export async function assignWorkspace (db: Db, productId: string, email: string, workspace: string): Promise<void> {
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>(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<void> {
async function createPersonAccount (account: Account, productId: string, workspace: string): Promise<void> {
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) {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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