mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
chore(authentication): some improvements
CB-109
This commit is contained in:
@@ -4,5 +4,8 @@
|
||||
"authentication_create_user_dialog_create": "Create",
|
||||
"authentication_user_name": "User name",
|
||||
"authentication_user_password": "User password",
|
||||
"authentication_user_role": "User role"
|
||||
"authentication_user_password_repeat": "Repeat password",
|
||||
"authentication_user_role": "User role",
|
||||
"authentication_user_passwords_not_match": "Passwords don't match",
|
||||
"authentication_user_user_created": "User was created successfully"
|
||||
}
|
||||
@@ -4,5 +4,8 @@
|
||||
"authentication_create_user_dialog_create": "Создать",
|
||||
"authentication_user_name": "Имя",
|
||||
"authentication_user_password": "Пароль",
|
||||
"authentication_user_role": "Роль"
|
||||
"authentication_user_password_repeat": "Повторите пароль",
|
||||
"authentication_user_role": "Роль",
|
||||
"authentication_user_passwords_not_match": "Пароли не совпадают",
|
||||
"authentication_user_user_created": "Пользователь создан успешно"
|
||||
}
|
||||
+32
-9
@@ -11,7 +11,7 @@ import { useCallback } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import {
|
||||
SubmittingForm, ErrorMessage, InputField
|
||||
SubmittingForm, ErrorMessage, InputField, Checkbox
|
||||
} from '@dbeaver/core/blocks';
|
||||
import { useController } from '@dbeaver/core/di';
|
||||
import { DialogComponent, CommonDialogWrapper } from '@dbeaver/core/dialogs';
|
||||
@@ -37,7 +37,6 @@ const styles = composes(
|
||||
}
|
||||
SubmittingForm {
|
||||
overflow: auto;
|
||||
margin: auto;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@@ -47,9 +46,13 @@ const styles = composes(
|
||||
flex-direction: column;
|
||||
}
|
||||
create-form {
|
||||
flex: 2;
|
||||
flex-direction: column;
|
||||
padding: 18px 24px;
|
||||
}
|
||||
fill {
|
||||
flex: 1;
|
||||
}
|
||||
ErrorMessage {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
@@ -70,8 +73,12 @@ export const CreateUserDialog: DialogComponent<null, null> = observer(
|
||||
(value: string) => controller.credentials.password = value,
|
||||
[]
|
||||
);
|
||||
const handlePasswordRepeatChange = useCallback(
|
||||
(value: string) => controller.credentials.passwordRepeat = value,
|
||||
[]
|
||||
);
|
||||
const handleRoleChange = useCallback(
|
||||
(value: string) => controller.credentials.roles = value,
|
||||
(roleId: string, value: boolean) => controller.credentials.roles.set(roleId, value),
|
||||
[]
|
||||
);
|
||||
|
||||
@@ -82,6 +89,7 @@ export const CreateUserDialog: DialogComponent<null, null> = observer(
|
||||
footer={(
|
||||
<CreateUserDialogFooter
|
||||
isCreating={controller.isCreating}
|
||||
onCancel={props.rejectDialog}
|
||||
onCreate={controller.create}
|
||||
/>
|
||||
)}
|
||||
@@ -115,18 +123,33 @@ export const CreateUserDialog: DialogComponent<null, null> = observer(
|
||||
</group>
|
||||
<group as="div">
|
||||
<InputField
|
||||
type='role'
|
||||
name='text'
|
||||
value={controller.credentials.roles}
|
||||
placeholder={controller.roles.join(' ')}
|
||||
onChange={handleRoleChange}
|
||||
type='password'
|
||||
name='password_repeat'
|
||||
value={controller.credentials.passwordRepeat}
|
||||
onChange={handlePasswordRepeatChange}
|
||||
disabled={controller.isCreating}
|
||||
mod='surface'
|
||||
>
|
||||
{translate('authentication_user_role')}
|
||||
{translate('authentication_user_password_repeat')}
|
||||
</InputField>
|
||||
</group>
|
||||
{controller.roles.map((role, i) => (
|
||||
<group as="div" key={role.roleId}>
|
||||
<Checkbox
|
||||
type='checkbox'
|
||||
name='role'
|
||||
checkboxLabel={role.roleName || role.roleId}
|
||||
onChange={checked => handleRoleChange(role.roleId, checked)}
|
||||
checked={controller.credentials.roles.get(role.roleId)}
|
||||
disabled={controller.isCreating}
|
||||
mod='surface'
|
||||
>
|
||||
{i === 0 && translate('authentication_user_role')}
|
||||
</Checkbox>
|
||||
</group>
|
||||
))}
|
||||
</create-form>
|
||||
<fill as="div"/>
|
||||
</SubmittingForm>
|
||||
{controller.error.responseMessage && (
|
||||
<ErrorMessage
|
||||
|
||||
+11
-10
@@ -24,11 +24,12 @@ export class CreateUserDialogController implements IInitializableController, IDe
|
||||
@observable credentials = {
|
||||
login: '',
|
||||
password: '',
|
||||
roles: '',
|
||||
passwordRepeat: '',
|
||||
roles: new Map<string, boolean>(),
|
||||
};
|
||||
|
||||
@computed get roles() {
|
||||
return this.rolesManagerService.roles.data.map(role => role.roleId);
|
||||
return this.rolesManagerService.roles.data;
|
||||
}
|
||||
readonly error = new GQLErrorCatcher();
|
||||
private isDistructed = false;
|
||||
@@ -54,6 +55,10 @@ export class CreateUserDialogController implements IInitializableController, IDe
|
||||
if (this.isCreating) {
|
||||
return;
|
||||
}
|
||||
if (this.credentials.password !== this.credentials.passwordRepeat) {
|
||||
this.notificationService.logError({ title: 'authentication_user_passwords_not_match' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.isCreating = true;
|
||||
let isUserCreated = false;
|
||||
@@ -61,17 +66,13 @@ export class CreateUserDialogController implements IInitializableController, IDe
|
||||
const user = await this.usersManagerService.create(this.credentials.login, false);
|
||||
isUserCreated = !!user;
|
||||
await this.usersManagerService.updateCredentials(user.userId, { password: this.credentials.password });
|
||||
if (this.credentials.roles) {
|
||||
const roles = this.credentials.roles
|
||||
.trim()
|
||||
.replace(/\s+/g, ' ')
|
||||
.split(' ');
|
||||
|
||||
for (const role of roles) {
|
||||
await this.usersManagerService.grantRole(user.userId, role);
|
||||
for (const [roleId, checked] of this.credentials.roles) {
|
||||
if (checked) {
|
||||
await this.usersManagerService.grantRole(user.userId, roleId);
|
||||
}
|
||||
}
|
||||
await this.usersManagerService.users.refresh(user.userId);
|
||||
this.notificationService.logInfo({ title: 'authentication_user_user_created' });
|
||||
this.close();
|
||||
} catch (exception) {
|
||||
if (isUserCreated) {
|
||||
|
||||
+15
@@ -21,22 +21,37 @@ const styles = css`
|
||||
margin: auto;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
fill {
|
||||
flex: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
export type Props = {
|
||||
isCreating: boolean;
|
||||
onCancel(): void;
|
||||
onCreate(): void;
|
||||
}
|
||||
|
||||
export const CreateUserDialogFooter = observer(
|
||||
function CreateUserDialogFooter({
|
||||
isCreating,
|
||||
onCancel,
|
||||
onCreate,
|
||||
}: Props) {
|
||||
const translate = useTranslate();
|
||||
|
||||
return styled(styles)(
|
||||
<controls as="div">
|
||||
<Button
|
||||
type="button"
|
||||
mod={['outlined']}
|
||||
onClick={onCancel}
|
||||
disabled={isCreating}
|
||||
>
|
||||
{translate('ui_processing_cancel')}
|
||||
</Button>
|
||||
<fill as="div"/>
|
||||
<Button
|
||||
type="button"
|
||||
mod={['unelevated']}
|
||||
|
||||
@@ -63,7 +63,7 @@ export function Snackbar({
|
||||
<notification as="div" {...use({ mounted, closing })} >
|
||||
<notification-header as="div">
|
||||
{type && <NotificationMark type={type} />}
|
||||
<message as="div">{text}</message>
|
||||
<message as="div">{translate(text)}</message>
|
||||
{!closeAfter && onClose && (
|
||||
<IconButton onClick={onClose} name="cross" viewBox="0 0 16 16" />
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import styled, { css, use } from 'reshadow';
|
||||
|
||||
import { useStyles } from '@dbeaver/core/theming';
|
||||
|
||||
const styles = css`
|
||||
label {
|
||||
composes: theme-typography--body1 from global;
|
||||
}
|
||||
field {
|
||||
display: flex;
|
||||
flex: auto;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
|
||||
& label {
|
||||
width: 230px;
|
||||
text-align: right;
|
||||
padding: 0 12px;
|
||||
line-height: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
& input {
|
||||
flex: 1;
|
||||
margin: 0 12px;
|
||||
height: 26px;
|
||||
}
|
||||
& checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> & {
|
||||
checkboxLabel: string;
|
||||
mod?: 'surface';
|
||||
onChange?(value: boolean): any;
|
||||
}
|
||||
|
||||
export function Checkbox({
|
||||
checkboxLabel,
|
||||
children,
|
||||
className,
|
||||
mod,
|
||||
onChange = () => {},
|
||||
...rest
|
||||
}: Props) {
|
||||
|
||||
return styled(useStyles(styles))(
|
||||
<field as="div" className={className}>
|
||||
<label>{children}</label>
|
||||
<checkbox as='div'>
|
||||
<input onChange={e => onChange(e.target.checked)} {...rest} {...use({ mod })} />
|
||||
<checkbox-label as='div'>{checkboxLabel}</checkbox-label>
|
||||
</checkbox>
|
||||
</field>
|
||||
);
|
||||
}
|
||||
@@ -22,7 +22,7 @@ const styles = css`
|
||||
padding: 12px 0;
|
||||
|
||||
& label {
|
||||
width: 150px;
|
||||
width: 230px;
|
||||
text-align: right;
|
||||
padding: 0 12px;
|
||||
line-height: 16px;
|
||||
|
||||
@@ -12,6 +12,7 @@ export * from './Split';
|
||||
export * from './Table';
|
||||
export * from './Tabs';
|
||||
export * from './Button';
|
||||
export * from './Checkbox';
|
||||
export * from './ErrorMessage';
|
||||
export * from './IconButton';
|
||||
export * from './IconOrImage';
|
||||
|
||||
Reference in New Issue
Block a user