Fix password bugs (#2235)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev
2022-07-14 08:45:51 +06:00
committed by GitHub
parent e6aa55250a
commit 9645b5947e
15 changed files with 178 additions and 66 deletions
+15
View File
@@ -24,6 +24,21 @@
"cwd": "${workspaceRoot}/server/server",
"protocol": "inspector"
},
{
"name": "Debug Account",
"type": "node",
"request": "launch",
"args": ["src/index.ts"],
"env": {
"MONGO_URL": "mongodb://localhost:27017",
"SERVER_SECRET": "secret",
"TRANSACTOR_URL": "ws:/localhost:3333"
},
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
"sourceMaps": true,
"cwd": "${workspaceRoot}/pods/account",
"protocol": "inspector"
},
{
"type": "node",
"request": "launch",
+8 -2
View File
@@ -1,6 +1,13 @@
# Changelog
## 0.6.30 (upcoming)
## 0.6.31 (upcoming)
Core:
- Fix password change settings
- Fix settings collapse
## 0.6.30
Core:
@@ -38,7 +45,6 @@ Tracker:
- Roadmap
- Context menus (Priority/Status/Assignee)
Chunter:
- Reactions on messages
+14 -1
View File
@@ -23,9 +23,10 @@ import {
dropWorkspace,
getAccount,
getWorkspace,
setRole,
listAccounts,
listWorkspaces,
replacePassword,
setRole,
upgradeWorkspace
} from '@anticrm/account'
import { setMetadata } from '@anticrm/platform'
@@ -94,6 +95,18 @@ program
})
})
program
.command('reset-account <email>')
.description('create user and corresponding account in master database')
.option('-p, --password <password>', 'new user password')
.action(async (email: string, cmd) => {
const { mongodbUri } = prepareTools()
return await withDatabase(mongodbUri, async (db) => {
console.log(`update account ${email} ${cmd.first as string} ${cmd.last as string}...`)
await replacePassword(db, email, cmd.password)
})
})
program
.command('assign-workspace <email> <workspace>')
.description('assign workspace')
+2 -1
View File
@@ -121,8 +121,9 @@
{/if}
{#if format === 'password'}
<input
id="userPassword"
bind:this={input}
type="passsword"
type="Password"
bind:value
placeholder={phTraslate}
{style}
+8 -4
View File
@@ -13,18 +13,18 @@
// limitations under the License.
//
import login from '@anticrm/login'
import {
getMetadata,
OK,
PlatformError,
Request,
Response,
getMetadata,
OK,
serialize,
Status,
unknownError,
unknownStatus
} from '@anticrm/platform'
import login from '@anticrm/login'
import { fetchMetadataLocalStorage, getCurrentLocation, navigate } from '@anticrm/ui'
export interface WorkspaceLoginInfo extends LoginInfo {
@@ -437,7 +437,7 @@ export async function changePassword (oldPassword: string, password: string): Pr
params: [oldPassword, password]
}
await fetch(accountsUrl, {
const response = await fetch(accountsUrl, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + token,
@@ -445,6 +445,10 @@ export async function changePassword (oldPassword: string, password: string): Pr
},
body: serialize(request)
})
const resp = await response.json()
if (resp.error !== undefined) {
throw new PlatformError(resp.error)
}
}
export async function leaveWorkspace (email: string): Promise<void> {
+2 -1
View File
@@ -53,6 +53,7 @@
"User": "User",
"Maintainer": "Maintainer",
"Owner": "Owner",
"Role": "Role"
"Role": "Role",
"FailedToSave": "Failed to update password"
}
}
+2 -1
View File
@@ -53,6 +53,7 @@
"User": "Пользователь",
"Maintainer": "Maintainer",
"Owner": "Владелец",
"Role": "Роль"
"Role": "Роль",
"FailedToSave": "Не удалось обновить пароль"
}
}
@@ -17,12 +17,15 @@
import presentation from '@anticrm/presentation'
import { Button, EditBox, Icon, Label } from '@anticrm/ui'
import { changePassword } from '@anticrm/login-resources'
import Error from './icons/Error.svelte'
import plugin from '../plugin'
let oldPassword: string = ''
let password: string = ''
let password2: string = ''
let label = presentation.string.Save
let saved = false
let error = false
$: disabled =
password.length === 0 || oldPassword.length === 0 || oldPassword === password || password !== password2 || saved
@@ -37,8 +40,16 @@
console.log(e)
label = presentation.string.Save
saved = false
error = true
}
}
function updateSaved (p1: string, p2: string, p3: string): void {
saved = false
label = presentation.string.Save
error = false
}
$: updateSaved(oldPassword, password, password2)
</script>
<div class="antiComponent">
@@ -48,6 +59,12 @@
</div>
<div class="flex-row-stretch flex-grow p-10">
<div class="flex-grow flex-col">
{#if error}
<div class="flex-row-center gap-2">
<Icon icon={Error} size={'medium'} />
<Label label={plugin.string.FailedToSave} />
</div>
{/if}
<div class="flex-grow flex-col">
<div>
<EditBox
@@ -32,6 +32,7 @@
import { EmployeeAccount } from '@anticrm/contact'
import { setMetadata } from '@anticrm/platform'
export let visibileNav = true
let category: SettingsCategory | undefined
let categoryId: string = ''
@@ -86,44 +87,54 @@
</script>
<div class="flex h-full">
<div class="antiPanel-navigator filled indent">
<div class="antiNav-header">
<span class="fs-title overflow-label">
<Label label={setting.string.Settings} />
</span>
{#if visibileNav}
<div class="antiPanel-navigator filled indent">
<div class="antiNav-header">
<span class="fs-title overflow-label">
<Label label={setting.string.Settings} />
</span>
</div>
{#each categories as category, i}
{#if i > 0 && categories[i - 1].group !== category.group}
<div class="antiNav-divider short line" />
{/if}
<CategoryElement
icon={category.icon}
label={category.label}
selected={category.name === categoryId}
expandable={category._id === setting.ids.Setting}
on:click={() => {
selectCategory(category.name)
}}
/>
{/each}
<div class="signout">
<CategoryElement icon={setting.icon.Signout} label={setting.string.Signout} on:click={signOut} />
<CategoryElement
icon={login.icon.InviteWorkspace}
label={setting.string.InviteWorkspace}
on:click={inviteWorkspace}
/>
<CategoryElement
icon={setting.icon.SelectWorkspace}
label={setting.string.SelectWorkspace}
on:click={selectWorkspace}
/>
</div>
</div>
{#each categories as category, i}
{#if i > 0 && categories[i - 1].group !== category.group}
<div class="antiNav-divider short line" />
{/if}
<CategoryElement
icon={category.icon}
label={category.label}
selected={category.name === categoryId}
expandable={category._id === setting.ids.Setting}
on:click={() => {
selectCategory(category.name)
}}
/>
{/each}
<div class="signout">
<CategoryElement icon={setting.icon.Signout} label={setting.string.Signout} on:click={signOut} />
<CategoryElement
icon={login.icon.InviteWorkspace}
label={setting.string.InviteWorkspace}
on:click={inviteWorkspace}
/>
<CategoryElement
icon={setting.icon.SelectWorkspace}
label={setting.string.SelectWorkspace}
on:click={selectWorkspace}
/>
</div>
</div>
{/if}
<div class="antiPanel-component border-left filled">
{#if category}
<Component is={category.component} />
<Component
is={category.component}
props={{
visibileNav,
onFirstCategory: () => {
selectCategory(categories[0].name)
}
}}
/>
{/if}
</div>
</div>
@@ -17,7 +17,7 @@
import { AccountRole, getCurrentAccount } from '@anticrm/core'
import { createQuery } from '@anticrm/presentation'
import setting, { SettingsCategory } from '@anticrm/setting'
import { Component, getCurrentLocation, Label, location, navigate } from '@anticrm/ui'
import ui, { Button, Component, getCurrentLocation, IconBack, Label, location, navigate } from '@anticrm/ui'
import { onDestroy } from 'svelte'
import CategoryElement from './CategoryElement.svelte'
@@ -27,6 +27,9 @@
let categories: SettingsCategory[] = []
const account = getCurrentAccount() as EmployeeAccount
export let visibileNav = true
export let onFirstCategory: () => void
const settingsQuery = createQuery()
settingsQuery.query(
setting.class.WorkspaceSettingCategory,
@@ -58,23 +61,34 @@
</script>
<div class="flex h-full">
<div class="antiPanel-navigator filled indent">
<div class="antiNav-header">
<span class="fs-title overflow-label">
<Label label={setting.string.WorkspaceSetting} />
</span>
{#if visibileNav}
<div class="antiPanel-navigator filled indent">
<div class="antiNav-header">
<div class="flex-row-center gap-2">
<Button
kind={'link'}
icon={IconBack}
label={ui.string.Back}
on:click={() => onFirstCategory()}
size={'small'}
/>
<span class="fs-title overflow-label">
<Label label={setting.string.WorkspaceSetting} />
</span>
</div>
</div>
{#each categories as category}
<CategoryElement
icon={category.icon}
label={category.label}
selected={category.name === categoryId}
on:click={() => {
selectCategory(category.name)
}}
/>
{/each}
</div>
{#each categories as category}
<CategoryElement
icon={category.icon}
label={category.label}
selected={category.name === categoryId}
on:click={() => {
selectCategory(category.name)
}}
/>
{/each}
</div>
{/if}
<div class="antiPanel-component border-left filled">
{#if category}
@@ -0,0 +1,9 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="10" cy="10" r="7.275" stroke="#EE7A7A" stroke-width="1.2" />
<path
d="M9.51371 11.6902L9.51636 11.7875H9.61367H10.4137H10.511L10.5136 11.6902L10.6886 5.27772L10.6914 5.17499H10.5887H9.43867H9.33591L9.33871 5.27772L9.51371 11.6902ZM10.0012 14.375C10.4929 14.375 10.9012 13.9812 10.9012 13.475C10.9012 12.9687 10.4929 12.575 10.0012 12.575C9.50947 12.575 9.10117 12.9687 9.10117 13.475C9.10117 13.9812 9.50947 14.375 10.0012 14.375Z"
fill="#EE7A7A"
stroke="#EE7A7A"
stroke-width="0.2"
/>
</svg>

After

Width:  |  Height:  |  Size: 623 B

+2 -1
View File
@@ -47,6 +47,7 @@ export default mergeIds(settingId, setting, {
User: '' as IntlString,
Maintainer: '' as IntlString,
Owner: '' as IntlString,
Role: '' as IntlString
Role: '' as IntlString,
FailedToSave: '' as IntlString
}
})
@@ -12,6 +12,7 @@
let templates: MessageTemplate[] = []
let selected: Ref<MessageTemplate> | undefined
let newTemplate: Data<MessageTemplate> | undefined = undefined
let loading = true
query.query(templatesPlugin.class.MessageTemplate, {}, (t) => {
templates = t
@@ -19,6 +20,7 @@
selected = undefined
newTemplate = undefined
}
loading = false
})
const Mode = {
@@ -79,7 +81,9 @@
<div class="ac-column">
<div id="create-template" class="flex-between trans-title mb-3">
<Label label={templatesPlugin.string.TemplatesHeader} />
<CircleButton icon={IconAdd} on:click={addTemplate} />
{#if !loading}
<CircleButton icon={IconAdd} on:click={addTemplate} />
{/if}
</div>
<div class="flex-col overflow-y-auto">
@@ -488,14 +488,14 @@
}}
>
{#if currentApplication && currentApplication.component}
<Component is={currentApplication.component} props={{ currentSpace }} />
<Component is={currentApplication.component} props={{ currentSpace, visibileNav }} />
{:else if specialComponent}
<Component
is={specialComponent.component}
props={{ model: navigatorModel, ...specialComponent.componentProps, currentSpace }}
props={{ model: navigatorModel, ...specialComponent.componentProps, currentSpace, visibileNav }}
/>
{:else if currentView?.component !== undefined}
<Component is={currentView.component} props={{ ...currentView.componentProps, currentView }} />
<Component is={currentView.component} props={{ ...currentView.componentProps, currentView, visibileNav }} />
{:else}
<SpaceView {currentSpace} {currentView} {createItemDialog} {createItemLabel} />
{/if}
+15
View File
@@ -543,6 +543,21 @@ export async function changePassword (db: Db, token: string, oldPassword: string
await db.collection(ACCOUNT_COLLECTION).updateOne({ _id: account._id }, { $set: { salt, hash } })
}
/**
* @public
*/
export async function replacePassword (db: Db, email: string, password: string): Promise<void> {
const account = await getAccount(db, email)
if (account === null) {
throw new PlatformError(new Status(Severity.ERROR, accountPlugin.status.InvalidPassword, { account: email }))
}
const salt = randomBytes(32)
const hash = hashWithSalt(password, salt)
await db.collection(ACCOUNT_COLLECTION).updateOne({ _id: account._id }, { $set: { salt, hash } })
}
/**
* @public
*/