mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
feat(plugin-authentication): CB-999 user password change (public)
This commit is contained in:
@@ -6,6 +6,7 @@ export * from './TopNavBar/SettingsMenu/SettingsMenuService';
|
||||
export * from './TopNavBar/ConnectionSchemaManager/ConnectionSchemaManagerService';
|
||||
export * from './TopNavBar/TopNavBarService';
|
||||
export * from './TopNavBar/AdministrationTopAppBarBootstrapService';
|
||||
export * from './TopNavBar/shared/topMenuStyles';
|
||||
export * from './shared/NodesManager/ConnectionDialogsService';
|
||||
export * from './shared/NodesManager/extensions/IObjectCatalogProvider';
|
||||
export * from './shared/NodesManager/extensions/IObjectCatalogSetter';
|
||||
|
||||
@@ -39,6 +39,10 @@ export class AuthProviderService {
|
||||
return provider.get();
|
||||
}
|
||||
|
||||
hashValue(value: string): string {
|
||||
return md5(value).toUpperCase();
|
||||
}
|
||||
|
||||
async processCredentials(providerId: string, credentials: Record<string, any>): Promise<Record<string, any>> {
|
||||
const provider = await this.authProvidersResource.load(providerId);
|
||||
|
||||
@@ -49,9 +53,7 @@ export class AuthProviderService {
|
||||
const credentialsProcessed = { ...credentials };
|
||||
for (const parameter of provider.credentialParameters) {
|
||||
if (parameter.encryption === 'hash' && parameter.id in credentialsProcessed) {
|
||||
const md5Hash = md5(credentialsProcessed[parameter.id])
|
||||
.toUpperCase();
|
||||
credentialsProcessed[parameter.id] = md5Hash;
|
||||
credentialsProcessed[parameter.id] = this.hashValue(credentialsProcessed[parameter.id]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +61,7 @@ export class AuthProviderService {
|
||||
}
|
||||
}
|
||||
|
||||
interface IAuthProviderContext{
|
||||
interface IAuthProviderContext {
|
||||
get: () => boolean;
|
||||
auth: () => void;
|
||||
}
|
||||
|
||||
@@ -145,6 +145,13 @@ export class UsersResource extends CachedMapResource<string, AdminUser, UserReso
|
||||
});
|
||||
}
|
||||
|
||||
async updateLocalPassword(oldPassword: string, newPassword: string): Promise<void> {
|
||||
await this.graphQLService.sdk.authChangeLocalPassword({
|
||||
oldPassword: this.authProviderService.hashValue(oldPassword),
|
||||
newPassword: this.authProviderService.hashValue(newPassword),
|
||||
});
|
||||
}
|
||||
|
||||
async delete(key: ResourceKey<string>): Promise<void> {
|
||||
await ResourceKeyUtils.forEachAsync(key, async key => {
|
||||
if (this.isActiveUser(key)) {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
query authChangeLocalPassword($oldPassword: String!, $newPassword: String!) {
|
||||
authChangeLocalPassword(oldPassword: $oldPassword, newPassword: $newPassword)
|
||||
}
|
||||
@@ -19,6 +19,7 @@ export interface Scalars {
|
||||
export interface Query {
|
||||
activeUser?: Maybe<UserInfo>;
|
||||
allConnections: ConnectionInfo[];
|
||||
authChangeLocalPassword: Scalars['Boolean'];
|
||||
authLogin: UserAuthToken;
|
||||
authLogout?: Maybe<Scalars['Boolean']>;
|
||||
authModels: DatabaseAuthModel[];
|
||||
@@ -76,6 +77,11 @@ export interface QueryAllConnectionsArgs {
|
||||
id?: Maybe<Scalars['ID']>;
|
||||
}
|
||||
|
||||
export interface QueryAuthChangeLocalPasswordArgs {
|
||||
oldPassword: Scalars['String'];
|
||||
newPassword: Scalars['String'];
|
||||
}
|
||||
|
||||
export interface QueryAuthLoginArgs {
|
||||
provider: Scalars['ID'];
|
||||
credentials: Scalars['Object'];
|
||||
@@ -996,6 +1002,13 @@ export type AsyncTaskCancelMutationVariables = Exact<{
|
||||
|
||||
export interface AsyncTaskCancelMutation { result: Mutation['asyncTaskCancel'] }
|
||||
|
||||
export type AuthChangeLocalPasswordQueryVariables = Exact<{
|
||||
oldPassword: Scalars['String'];
|
||||
newPassword: Scalars['String'];
|
||||
}>;
|
||||
|
||||
export type AuthChangeLocalPasswordQuery = Pick<Query, 'authChangeLocalPassword'>;
|
||||
|
||||
export type AuthLoginQueryVariables = Exact<{
|
||||
provider: Scalars['ID'];
|
||||
credentials: Scalars['Object'];
|
||||
@@ -1856,6 +1869,11 @@ export const AsyncTaskCancelDocument = `
|
||||
result: asyncTaskCancel(id: $taskId)
|
||||
}
|
||||
`;
|
||||
export const AuthChangeLocalPasswordDocument = `
|
||||
query authChangeLocalPassword($oldPassword: String!, $newPassword: String!) {
|
||||
authChangeLocalPassword(oldPassword: $oldPassword, newPassword: $newPassword)
|
||||
}
|
||||
`;
|
||||
export const AuthLoginDocument = `
|
||||
query authLogin($provider: ID!, $credentials: Object!, $linkUser: Boolean, $customIncludeOriginDetails: Boolean!) {
|
||||
authToken: authLogin(
|
||||
@@ -2640,6 +2658,9 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper =
|
||||
asyncTaskCancel(variables: AsyncTaskCancelMutationVariables): Promise<AsyncTaskCancelMutation> {
|
||||
return withWrapper(() => client.request<AsyncTaskCancelMutation>(AsyncTaskCancelDocument, variables));
|
||||
},
|
||||
authChangeLocalPassword(variables: AuthChangeLocalPasswordQueryVariables): Promise<AuthChangeLocalPasswordQuery> {
|
||||
return withWrapper(() => client.request<AuthChangeLocalPasswordQuery>(AuthChangeLocalPasswordDocument, variables));
|
||||
},
|
||||
authLogin(variables: AuthLoginQueryVariables): Promise<AuthLoginQuery> {
|
||||
return withWrapper(() => client.request<AuthLoginQuery>(AuthLoginDocument, variables));
|
||||
},
|
||||
|
||||
@@ -16,7 +16,8 @@ import { ServerConfigurationService } from '@cloudbeaver/plugin-administration';
|
||||
import { AuthenticationProviders } from './Administration/ServerConfiguration/AuthenticationProviders';
|
||||
import { AuthenticationService } from './AuthenticationService';
|
||||
import { AuthDialogService } from './Dialog/AuthDialogService';
|
||||
import { UserInfo } from './UserInfo';
|
||||
import { UserMenu } from './UserMenu/UserMenu';
|
||||
import { UserMenuService } from './UserMenu/UserMenuService';
|
||||
|
||||
@injectable()
|
||||
export class PluginBootstrap extends Bootstrap {
|
||||
@@ -28,6 +29,7 @@ export class PluginBootstrap extends Bootstrap {
|
||||
private settingsMenuService: SettingsMenuService,
|
||||
private topNavService: TopNavService,
|
||||
private administrationTopAppBarService: AdministrationTopAppBarService,
|
||||
private userMenuService: UserMenuService,
|
||||
private readonly serverConfigurationService: ServerConfigurationService
|
||||
) {
|
||||
super();
|
||||
@@ -55,9 +57,9 @@ export class PluginBootstrap extends Bootstrap {
|
||||
onClick: this.authenticationService.logout.bind(this.authenticationService),
|
||||
}
|
||||
);
|
||||
|
||||
this.topNavService.placeholder.add(UserInfo, 4);
|
||||
this.administrationTopAppBarService.placeholder.add(UserInfo, 4);
|
||||
this.userMenuService.register();
|
||||
this.topNavService.placeholder.add(UserMenu, 4);
|
||||
this.administrationTopAppBarService.placeholder.add(UserMenu, 4);
|
||||
this.serverConfigurationService.configurationContainer.add(AuthenticationProviders, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,32 +12,25 @@ import styled, { css } from 'reshadow';
|
||||
import { AuthInfoService } from '@cloudbeaver/core-authentication';
|
||||
import { IconOrImage } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { composes, useStyles } from '@cloudbeaver/core-theming';
|
||||
import { useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
const styles = composes(
|
||||
css`
|
||||
user {
|
||||
composes: theme-ripple from global;
|
||||
}
|
||||
`,
|
||||
css`
|
||||
user {
|
||||
height: 100%;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
IconOrImage {
|
||||
display: block;
|
||||
width: 24px;
|
||||
}
|
||||
user-name {
|
||||
display: block;
|
||||
line-height: initial;
|
||||
margin-left: 8px;
|
||||
}
|
||||
`
|
||||
);
|
||||
const styles = css`
|
||||
user {
|
||||
height: 100%;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
IconOrImage {
|
||||
display: block;
|
||||
width: 24px;
|
||||
}
|
||||
user-name {
|
||||
display: block;
|
||||
line-height: initial;
|
||||
margin-left: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const UserInfo = observer(function UserInfo() {
|
||||
const authInfoService = useService(AuthInfoService);
|
||||
@@ -48,11 +41,11 @@ export const UserInfo = observer(function UserInfo() {
|
||||
}
|
||||
|
||||
return styled(style)(
|
||||
<user as='div'>
|
||||
<user-icon as="div">
|
||||
<user>
|
||||
<user-icon>
|
||||
<IconOrImage icon='user' viewBox='0 0 28 28' />
|
||||
</user-icon>
|
||||
<user-name as='div'>{authInfoService.userInfo.displayName || authInfoService.userInfo.userId}</user-name>
|
||||
<user-name>{authInfoService.userInfo.displayName || authInfoService.userInfo.userId}</user-name>
|
||||
</user>
|
||||
);
|
||||
});
|
||||
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { observable } from 'mobx';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { BASE_CONTAINERS_STYLES, Container, ErrorMessage, Group, InputFieldNew, SubmittingForm, useFocus } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { CommonDialogService, CommonDialogWrapper, DialogComponent, DialogComponentProps } from '@cloudbeaver/core-dialogs';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
import { ErrorDetailsDialog } from '@cloudbeaver/core-notifications';
|
||||
import { GQLError, GQLErrorCatcher } from '@cloudbeaver/core-sdk';
|
||||
import { composes, useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { ChangeUserPasswordDialogFooter } from './ChangeUserPasswordDialogFooter';
|
||||
|
||||
const styles = composes(
|
||||
css`
|
||||
ErrorMessage {
|
||||
composes: theme-background-secondary theme-text-on-secondary from global;
|
||||
}
|
||||
`,
|
||||
css`
|
||||
CommonDialogWrapper {
|
||||
min-width: 500px;
|
||||
}
|
||||
ErrorMessage {
|
||||
flex: 1;
|
||||
}
|
||||
`);
|
||||
|
||||
interface IPayload {
|
||||
change: (oldPassword: string, newPassword: string) => Promise<void>;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
oldPassword: string;
|
||||
newPassword: string;
|
||||
repeatedPassword: string;
|
||||
error: GQLErrorCatcher;
|
||||
customError: string;
|
||||
submitting: boolean;
|
||||
}
|
||||
|
||||
export const ChangeUserPasswordDialog: DialogComponent<IPayload, null> = observer(
|
||||
function ChangeUserPasswordDialog({
|
||||
payload,
|
||||
resolveDialog,
|
||||
rejectDialog,
|
||||
}: DialogComponentProps<IPayload, null>) {
|
||||
const [focusedRef] = useFocus<HTMLInputElement>({});
|
||||
const commonDialogService = useService(CommonDialogService);
|
||||
const notificationService = useService(NotificationService);
|
||||
const style = useStyles(styles, BASE_CONTAINERS_STYLES);
|
||||
const translate = useTranslate();
|
||||
const [state] = useState<IState>(() => observable({
|
||||
oldPassword: '', newPassword: '', repeatedPassword: '', error: new GQLErrorCatcher(), customError: '', submitting: false,
|
||||
}));
|
||||
const formFilled
|
||||
= state.newPassword.length > 0 && state.repeatedPassword.length > 0 && state.oldPassword.length > 0;
|
||||
const disabled = state.submitting;
|
||||
|
||||
const onShowDetails = useCallback(() => {
|
||||
commonDialogService.open(ErrorDetailsDialog, (state.error.exception as GQLError));
|
||||
}, [commonDialogService, state.error]);
|
||||
|
||||
const change = useCallback(async () => {
|
||||
state.error.clear();
|
||||
|
||||
if (state.newPassword !== state.repeatedPassword) {
|
||||
state.customError = 'authentication_user_passwords_not_match';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
state.submitting = true;
|
||||
await payload.change(state.oldPassword, state.newPassword);
|
||||
notificationService.logSuccess({ title: 'authentication_user_password_change_success' });
|
||||
resolveDialog();
|
||||
} catch (exeption) {
|
||||
if (!state.error.catch(exeption)) {
|
||||
state.customError = exeption.message;
|
||||
}
|
||||
} finally {
|
||||
state.submitting = false;
|
||||
}
|
||||
}, [state, payload, resolveDialog, notificationService]);
|
||||
|
||||
useEffect(() => {
|
||||
focusedRef.current?.focus();
|
||||
}, []);
|
||||
|
||||
return styled(style)(
|
||||
<CommonDialogWrapper
|
||||
title={translate('authentication_user_password_change_dialog_title')}
|
||||
footer={(
|
||||
<ChangeUserPasswordDialogFooter
|
||||
submitting={state.submitting}
|
||||
formFilled={formFilled}
|
||||
onCancel={rejectDialog}
|
||||
onChange={change}
|
||||
>
|
||||
{(state.error.responseMessage || state.customError) && (
|
||||
<ErrorMessage
|
||||
text={state.error.responseMessage || translate(state.customError)}
|
||||
hasDetails={state.error.hasDetails}
|
||||
onShowDetails={onShowDetails}
|
||||
/>
|
||||
)}
|
||||
</ChangeUserPasswordDialogFooter>
|
||||
)}
|
||||
onReject={rejectDialog}
|
||||
>
|
||||
<SubmittingForm onSubmit={change}>
|
||||
<Container>
|
||||
<Group small gap center>
|
||||
<InputFieldNew
|
||||
ref={focusedRef}
|
||||
type='password'
|
||||
name='oldPassword'
|
||||
state={state}
|
||||
disabled={disabled}
|
||||
mapValue={(value => value.trim())}
|
||||
small
|
||||
required
|
||||
>
|
||||
{translate('authentication_user_old_password')}
|
||||
</InputFieldNew>
|
||||
<InputFieldNew
|
||||
type='password'
|
||||
name='newPassword'
|
||||
autoComplete='new-password'
|
||||
state={state}
|
||||
disabled={disabled}
|
||||
mapValue={(value => value.trim())}
|
||||
small
|
||||
required
|
||||
>
|
||||
{translate('authentication_user_new_password')}
|
||||
</InputFieldNew>
|
||||
<InputFieldNew
|
||||
type='password'
|
||||
name='repeatedPassword'
|
||||
state={state}
|
||||
disabled={disabled}
|
||||
mapValue={(value => value.trim())}
|
||||
small
|
||||
required
|
||||
>
|
||||
{translate('authentication_user_password_repeat')}
|
||||
</InputFieldNew>
|
||||
</Group>
|
||||
</Container>
|
||||
</SubmittingForm>
|
||||
</CommonDialogWrapper>
|
||||
);
|
||||
}
|
||||
);
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { observer } from 'mobx-react-lite';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { Button } from '@cloudbeaver/core-blocks';
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
|
||||
const styles = css`
|
||||
footer-container {
|
||||
display: flex;
|
||||
width: min-content;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
footer-container > :not(:first-child) {
|
||||
margin-left: 16px;
|
||||
}
|
||||
Button {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
`;
|
||||
|
||||
export interface Props {
|
||||
onChange: () => void;
|
||||
onCancel: () => void;
|
||||
formFilled: boolean;
|
||||
submitting: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ChangeUserPasswordDialogFooter: React.FC<Props> = observer(function ChangeUserPasswordDialogFooter({
|
||||
onChange,
|
||||
onCancel,
|
||||
formFilled,
|
||||
submitting,
|
||||
className,
|
||||
children,
|
||||
}) {
|
||||
const translate = useTranslate();
|
||||
|
||||
return styled(styles)(
|
||||
<footer-container className={className}>
|
||||
{children}
|
||||
<Button disabled={submitting} type="button" mod={['outlined']} onClick={onCancel}>
|
||||
{translate('ui_processing_cancel')}
|
||||
</Button>
|
||||
<Button disabled={submitting || !formFilled} loading={submitting} type="button" mod={['unelevated']} onClick={onChange}>
|
||||
{translate('authentication_user_password_change_dialog_submit')}
|
||||
</Button>
|
||||
</footer-container>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { observer } from 'mobx-react-lite';
|
||||
import styled from 'reshadow';
|
||||
|
||||
import { topMenuStyles } from '@cloudbeaver/core-app';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { MenuTrigger } from '@cloudbeaver/core-dialogs';
|
||||
import { useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { UserInfo } from '../UserInfo';
|
||||
import { UserMenuService } from './UserMenuService';
|
||||
import { userMenuStyles } from './userMenuStyles';
|
||||
|
||||
export const UserMenu = observer(function UserMenu() {
|
||||
const userMenuService = useService(UserMenuService);
|
||||
const style = useStyles(userMenuStyles);
|
||||
|
||||
return styled(style)(
|
||||
<MenuTrigger panel={userMenuService.getMenu()} style={[topMenuStyles, userMenuStyles]}>
|
||||
<UserInfo />
|
||||
</MenuTrigger>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { AuthInfoService, AUTH_PROVIDER_LOCAL_ID, UsersResource } from '@cloudbeaver/core-authentication';
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { CommonDialogService, IComputedMenuItemOptions, IMenuPanel, StaticMenu } from '@cloudbeaver/core-dialogs';
|
||||
|
||||
import { ChangeUserPasswordDialog } from './ChangeUserPasswordDialog/ChangeUserPasswordDialog';
|
||||
|
||||
@injectable()
|
||||
export class UserMenuService {
|
||||
static userMenuToken = 'userMenu';
|
||||
|
||||
private menu = new StaticMenu();
|
||||
|
||||
getUserMenuToken() {
|
||||
return UserMenuService.userMenuToken;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private authInfoService: AuthInfoService,
|
||||
private commonDialogService: CommonDialogService,
|
||||
private usersResource: UsersResource,
|
||||
) { }
|
||||
|
||||
register(): void {
|
||||
this.menu.addRootPanel(this.getUserMenuToken());
|
||||
this.addMenuItem(
|
||||
this.getUserMenuToken(),
|
||||
{
|
||||
id: 'changePassword',
|
||||
order: 0,
|
||||
isHidden: () => !this.authInfoService.userInfo?.linkedAuthProviders.includes(AUTH_PROVIDER_LOCAL_ID),
|
||||
title: 'authentication_user_password_change_menu_title',
|
||||
onClick: async () => {
|
||||
await this.commonDialogService.open(ChangeUserPasswordDialog, {
|
||||
change: (oldPassword: string, newPassword: string) => this.usersResource.updateLocalPassword(
|
||||
oldPassword, newPassword
|
||||
),
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getMenu(): IMenuPanel {
|
||||
return this.menu.getMenu(this.getUserMenuToken());
|
||||
}
|
||||
|
||||
addMenuItem(panelId: string, options: IComputedMenuItemOptions): void {
|
||||
this.menu.addMenuItem(panelId, options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { css } from 'reshadow';
|
||||
|
||||
export const userMenuStyles = css`
|
||||
MenuTrigger {
|
||||
padding: 0 16px !important;
|
||||
height: 48px;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
outline: none !important;
|
||||
}
|
||||
`;
|
||||
@@ -27,6 +27,8 @@ export default [
|
||||
['authentication_administration_user_create_failed', 'Error creating new user'],
|
||||
['authentication_user_name', 'User name'],
|
||||
['authentication_user_credentials', 'Credentials'],
|
||||
['authentication_user_old_password', 'Old password'],
|
||||
['authentication_user_new_password', 'New password'],
|
||||
['authentication_user_password', 'User password'],
|
||||
['authentication_user_password_repeat', 'Repeat password'],
|
||||
['authentication_user_role', 'User role'],
|
||||
@@ -36,6 +38,10 @@ export default [
|
||||
['authentication_user_passwords_not_match', "Passwords don't match"],
|
||||
['authentication_user_login_already_exists', 'The user with this name already exists'],
|
||||
['authentication_user_login_cant_be_used', 'Sorry, that name cannot be used'],
|
||||
['authentication_user_password_change_dialog_title', 'Change password (local)'],
|
||||
['authentication_user_password_change_menu_title', 'Change local password'],
|
||||
['authentication_user_password_change_success', 'Password was changed successfully'],
|
||||
['authentication_user_password_change_dialog_submit', 'Change'],
|
||||
['administration_configuration_wizard_configuration_admin', 'Administrator Credentials'],
|
||||
['administration_configuration_wizard_configuration_admin_name', 'Login'],
|
||||
['administration_configuration_wizard_configuration_admin_password', 'Password'],
|
||||
|
||||
@@ -25,6 +25,8 @@ export default [
|
||||
['authentication_administration_user_create_failed', 'Не удалось создать пользователя'],
|
||||
['authentication_user_name', 'Имя'],
|
||||
['authentication_user_credentials', 'Учетные данные'],
|
||||
['authentication_user_old_password', 'Старый пароль'],
|
||||
['authentication_user_new_password', 'Новый пароль'],
|
||||
['authentication_user_password', 'Пароль'],
|
||||
['authentication_user_password_repeat', 'Повторите пароль'],
|
||||
['authentication_user_role', 'Роль'],
|
||||
@@ -34,4 +36,8 @@ export default [
|
||||
['authentication_user_passwords_not_match', 'Пароли не совпадают'],
|
||||
['authentication_user_login_already_exists', 'Пользователь с таким именем уже существует'],
|
||||
['authentication_user_login_cant_be_used', 'Данное имя нельзя использовать'],
|
||||
['authentication_user_password_change_dialog_title', 'Изменение пароля (локального)'],
|
||||
['authentication_user_password_change_menu_title', 'Изменить локальный пароль'],
|
||||
['authentication_user_password_change_success', 'Пароль успешно изменен'],
|
||||
['authentication_user_password_change_dialog_submit', 'Изменить'],
|
||||
];
|
||||
|
||||
@@ -18,6 +18,7 @@ import { AuthenticationLocaleService } from './AuthenticationLocaleService';
|
||||
import { AuthenticationService } from './AuthenticationService';
|
||||
import { AuthDialogService } from './Dialog/AuthDialogService';
|
||||
import { PluginBootstrap } from './PluginBootstrap';
|
||||
import { UserMenuService } from './UserMenu/UserMenuService';
|
||||
|
||||
export const manifest: PluginManifest = {
|
||||
info: {
|
||||
@@ -35,5 +36,6 @@ export const manifest: PluginManifest = {
|
||||
ServerConfigurationAuthenticationBootstrap,
|
||||
UserFormService,
|
||||
UserFormBaseBootstrap,
|
||||
UserMenuService,
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user