Merge branch 'devel' into feat/version-update-procedure

This commit is contained in:
Naumov Alexey
2020-10-28 14:14:31 +03:00
21 changed files with 276 additions and 226 deletions
+3 -5
View File
@@ -9,12 +9,10 @@ See out [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
![](https://github.com/dbeaver/cloudbeaver/wiki/images/demo_screenshot_1.png)
## Build CloudBeaver
## Run in Docker
CloudBeaver is the multi-platform server side application.
It can run on Linux, Windows and MacOS X.
[Detailed instructions](https://github.com/dbeaver/cloudbeaver/wiki/Build-and-deploy)
- [Official Docker repository](https://hub.docker.com/r/dbeaver/cloudbeaver)
- [Running instructions](https://github.com/dbeaver/cloudbeaver/wiki/Run-Docker-Container)
## Demo server
@@ -88,7 +88,7 @@ public interface DBWServiceCore extends DBWService {
WebConnectionInfo openConnection(WebSession webSession, WebConnectionConfig connectionConfig) throws DBWebException;
@WebAction
WebConnectionInfo initConnection(WebSession webSession, String connectionId, Map<String, Object> authProperties) throws DBWebException;
WebConnectionInfo initConnection(WebSession webSession, String connectionId, Map<String, Object> authProperties, Boolean saveCredentials) throws DBWebException;
@WebAction
WebConnectionInfo createConnection(WebSession webSession, WebConnectionConfig connectionConfig) throws DBWebException;
@@ -73,7 +73,7 @@ public class WebServiceBindingCore extends WebServiceBindingBase<DBWServiceCore>
.dataFetcher("createConnection", env -> getService(env).createConnection(getWebSession(env), getConnectionConfig(env)))
.dataFetcher("createConnectionFromTemplate", env -> getService(env).createConnectionFromTemplate(getWebSession(env), env.getArgument("templateId")))
.dataFetcher("copyConnectionFromNode", env -> getService(env).copyConnectionFromNode(getWebSession(env), env.getArgument("nodePath")))
.dataFetcher("initConnection", env -> getService(env).initConnection(getWebSession(env), env.getArgument("id"), env.getArgument("credentials")))
.dataFetcher("initConnection", env -> getService(env).initConnection(getWebSession(env), env.getArgument("id"), env.getArgument("credentials"), env.getArgument("saveCredentials")))
.dataFetcher("testConnection", env -> getService(env).testConnection(getWebSession(env), getConnectionConfig(env)))
.dataFetcher("closeConnection", env -> getService(env).closeConnection(getWebSession(env), env.getArgument("id")))
.dataFetcher("deleteConnection", env -> getService(env).deleteConnection(getWebSession(env), env.getArgument("id")))
@@ -220,7 +220,7 @@ public class WebServiceCore implements DBWServiceCore {
}
@Override
public WebConnectionInfo initConnection(WebSession webSession, String connectionId, Map<String, Object> authProperties) throws DBWebException {
public WebConnectionInfo initConnection(WebSession webSession, String connectionId, Map<String, Object> authProperties, Boolean saveCredentials) throws DBWebException {
WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(connectionId);
connectionInfo.setSavedAuthProperties(authProperties);
@@ -239,6 +239,14 @@ public class WebServiceCore implements DBWServiceCore {
} finally {
dataSourceContainer.setSavePassword(oldSavePassword);
}
if (saveCredentials != null && saveCredentials) {
// Save credentials in the datasource
if (!CommonUtils.isEmpty(authProperties)) {
authProperties.forEach((s, o) ->
dataSourceContainer.getConnectionConfiguration().setAuthProperty(s, CommonUtils.toString(o)));
}
dataSourceContainer.setSavePassword(true);
}
return connectionInfo;
}
@@ -10,7 +10,6 @@ import { observer } from 'mobx-react';
import { useCallback } from 'react';
import { useService } from '@cloudbeaver/core-di';
import { Translate } from '@cloudbeaver/core-localization';
import { usePermission } from '@cloudbeaver/core-root';
import { Administration } from '../Administration/Administration';
@@ -26,18 +25,18 @@ export const AdministrationScreen = observer(function AdministrationScreen() {
[administrationScreenService]
);
if (!usePermission(EAdminPermission.admin)) {
return <Translate token='root_permission_denied' />;
}
const accessProvided = usePermission(EAdminPermission.admin);
return (
<>
<AdministrationTopAppBar />
<Administration
configurationWizard={false}
activeScreen={administrationScreenService.activeScreen}
onItemSelect={handleSelect}
/>
{accessProvided && (
<Administration
configurationWizard={false}
activeScreen={administrationScreenService.activeScreen}
onItemSelect={handleSelect}
/>
)}
</>
);
});
@@ -10,13 +10,14 @@ import { computed, observable } from 'mobx';
import { injectable } from '@cloudbeaver/core-di';
import { IExecutor, Executor } from '@cloudbeaver/core-executor';
import { ServerConfigResource } from '@cloudbeaver/core-root';
import { PermissionsService, ServerConfigResource } from '@cloudbeaver/core-root';
import { ScreenService, RouterState } from '@cloudbeaver/core-routing';
import { LocalStorageSaveService } from '@cloudbeaver/core-settings';
import { AdministrationItemService } from '../AdministrationItem/AdministrationItemService';
import { IAdministrationItemRoute } from '../AdministrationItem/IAdministrationItemRoute';
import { IRouteParams } from '../AdministrationItem/IRouteParams';
import { EAdminPermission } from '../EAdminPermission';
const ADMINISTRATION_ITEMS_STATE = 'administration_items_state';
const ADMINISTRATION_INFO = 'administration_mode';
@@ -48,9 +49,11 @@ export class AdministrationScreenService {
return !!this.serverConfigResource.data?.configurationMode;
}
readonly ensurePermissions: IExecutor<void>;
readonly activationEvent: IExecutor<boolean>;
constructor(
private permissionsService: PermissionsService,
private screenService: ScreenService,
private administrationItemService: AdministrationItemService,
private autoSaveService: LocalStorageSaveService,
@@ -61,9 +64,11 @@ export class AdministrationScreenService {
};
this.itemState = new Map();
this.activationEvent = new Executor();
this.ensurePermissions = new Executor();
this.autoSaveService.withAutoSave(this.itemState, ADMINISTRATION_ITEMS_STATE);
this.autoSaveService.withAutoSave(this.info, ADMINISTRATION_INFO);
this.permissionsService.onUpdate.subscribe(() => this.checkPermissions(this.screenService.routerService.state));
}
getScreen(state?: RouterState): IAdministrationItemRoute | null {
@@ -152,4 +157,94 @@ export class AdministrationScreenService {
return this.screenService.isActive(routeName, AdministrationScreenService.screenName)
|| this.screenService.isActive(routeName, AdministrationScreenService.setupName);
}
async handleDeactivate(state: RouterState, nextState: RouterState): Promise<void> {
if (nextState && !this.isAdministrationRouteActive(nextState.name)) {
await this.activationEvent.execute(false);
}
const toScreen = this.getScreen(nextState);
const screen = this.getScreen(state);
if (screen) {
await this.administrationItemService.deActivate(
screen,
this.isConfigurationMode,
screen.item !== toScreen?.item
);
}
if (this.isConfigurationMode
&& !this.screenService.isActive(nextState.name, AdministrationScreenService.setupName)) {
this.navigateToRoot();
}
}
async handleCanActivate(toState: RouterState): Promise<boolean> {
if (!toState.params?.item) {
return false;
}
const fromScreen = this.getScreen(this.screenService.routerService.state);
const screen = this.getScreen(toState);
if (!screen) {
return false;
}
return this.administrationItemService.canActivate(
screen,
this.isConfigurationMode,
screen.item !== fromScreen?.item
);
}
async handleActivate(state: RouterState, prevState?: RouterState): Promise<void> {
if (!await this.checkPermissions(state)) {
return;
}
await this.activationEvent.execute(true);
const screen = this.getScreen(state);
const fromScreen = this.getScreen(prevState);
if (screen) {
await this.administrationItemService.activate(
screen,
this.isConfigurationMode,
screen.item !== fromScreen?.item
);
}
}
private async checkPermissions(state: RouterState) {
if (!this.isAdministrationRouteActive(state.name)) {
return;
}
if (!await this.isAccessProvided(state)) {
this.screenService.navigateToRoot();
return false;
}
return true;
}
private async isAccessProvided(state: RouterState) {
await this.serverConfigResource.load(null);
if (this.isConfigurationMode) {
return true;
}
if (this.screenService.isActive(state.name, AdministrationScreenService.setupName)) {
this.navigateToRoot();
return;
}
await this.ensurePermissions.execute();
if (!await this.permissionsService.hasAsync(EAdminPermission.admin)) {
return false;
}
return true;
}
}
@@ -7,11 +7,9 @@
*/
import { injectable, Bootstrap } from '@cloudbeaver/core-di';
import { PermissionsService, ServerConfigResource } from '@cloudbeaver/core-root';
import { ScreenService, RouterState } from '@cloudbeaver/core-routing';
import { ServerConfigResource } from '@cloudbeaver/core-root';
import { ScreenService } from '@cloudbeaver/core-routing';
import { AdministrationItemService } from '../AdministrationItem/AdministrationItemService';
import { EAdminPermission } from '../EAdminPermission';
import { AdministrationScreen } from './AdministrationScreen';
import { AdministrationScreenService } from './AdministrationScreenService';
import { ConfigurationWizardScreen } from './ConfigurationWizard/ConfigurationWizardScreen';
@@ -20,13 +18,10 @@ import { ConfigurationWizardScreen } from './ConfigurationWizard/ConfigurationWi
export class AdministrationScreenServiceBootstrap extends Bootstrap {
constructor(
private screenService: ScreenService,
private permissionsService: PermissionsService,
private administrationItemService: AdministrationItemService,
private administrationScreenService: AdministrationScreenService,
private serverConfigResource: ServerConfigResource
) {
super();
this.permissionsService.onUpdate.subscribe(() => this.checkPermissions(this.screenService.routerService.state));
}
register(): void {
@@ -36,27 +31,27 @@ export class AdministrationScreenServiceBootstrap extends Bootstrap {
{
name: AdministrationScreenService.screenName,
path: '/admin',
canActivate: () => this.handleCanActivate.bind(this),
canActivate: () => this.administrationScreenService.handleCanActivate.bind(this.administrationScreenService),
},
{
name: AdministrationScreenService.itemRouteName,
path: '/:item',
canActivate: () => this.handleCanActivate.bind(this),
canActivate: () => this.administrationScreenService.handleCanActivate.bind(this.administrationScreenService),
},
{
name: AdministrationScreenService.itemSubRouteName,
path: '/:sub',
canActivate: () => this.handleCanActivate.bind(this),
canActivate: () => this.administrationScreenService.handleCanActivate.bind(this.administrationScreenService),
},
{
name: AdministrationScreenService.itemSubParamRouteName,
path: '/:param',
canActivate: () => this.handleCanActivate.bind(this),
canActivate: () => this.administrationScreenService.handleCanActivate.bind(this.administrationScreenService),
},
],
component: AdministrationScreen,
onActivate: this.handleActivate.bind(this),
onDeactivate: this.handleDeactivate.bind(this),
onActivate: this.administrationScreenService.handleActivate.bind(this.administrationScreenService),
onDeactivate: this.administrationScreenService.handleDeactivate.bind(this.administrationScreenService),
});
this.screenService.create({
@@ -65,27 +60,27 @@ export class AdministrationScreenServiceBootstrap extends Bootstrap {
{
name: AdministrationScreenService.setupName,
path: '/setup',
canActivate: () => this.handleCanActivate.bind(this),
canActivate: () => this.administrationScreenService.handleCanActivate.bind(this.administrationScreenService),
},
{
name: AdministrationScreenService.setupItemRouteName,
path: '/:item',
canActivate: () => this.handleCanActivate.bind(this),
canActivate: () => this.administrationScreenService.handleCanActivate.bind(this.administrationScreenService),
},
{
name: AdministrationScreenService.setupItemSubRouteName,
path: '/:sub',
canActivate: () => this.handleCanActivate.bind(this),
canActivate: () => this.administrationScreenService.handleCanActivate.bind(this.administrationScreenService),
},
{
name: AdministrationScreenService.setupItemSubParamRouteName,
path: '/:param',
canActivate: () => this.handleCanActivate.bind(this),
canActivate: () => this.administrationScreenService.handleCanActivate.bind(this.administrationScreenService),
},
],
component: ConfigurationWizardScreen,
onActivate: this.handleActivate.bind(this),
onDeactivate: this.handleDeactivate.bind(this),
onActivate: this.administrationScreenService.handleActivate.bind(this.administrationScreenService),
onDeactivate: this.administrationScreenService.handleDeactivate.bind(this.administrationScreenService),
});
}
@@ -97,83 +92,4 @@ export class AdministrationScreenServiceBootstrap extends Bootstrap {
this.administrationScreenService.navigateToRoot();
}
}
private async handleDeactivate(state: RouterState, nextState: RouterState) {
if (nextState && !this.administrationScreenService.isAdministrationRouteActive(nextState.name)) {
await this.administrationScreenService.activationEvent.execute(false);
}
const toScreen = this.administrationScreenService.getScreen(nextState);
const screen = this.administrationScreenService.getScreen(state);
if (screen) {
await this.administrationItemService.deActivate(
screen,
this.administrationScreenService.isConfigurationMode,
screen.item !== toScreen?.item
);
}
if (this.administrationScreenService.isConfigurationMode
&& !this.screenService.isActive(state.name, AdministrationScreenService.setupName)) {
this.administrationScreenService.navigateToRoot();
}
}
private async handleCanActivate(toState: RouterState) {
if (!toState.params?.item) {
return false;
}
const fromScreen = this.administrationScreenService.getScreen(this.screenService.routerService.state);
const screen = this.administrationScreenService.getScreen(toState);
if (!screen) {
return false;
}
return this.administrationItemService.canActivate(
screen,
this.administrationScreenService.isConfigurationMode,
screen.item !== fromScreen?.item
);
}
private async handleActivate(state: RouterState, prevState?: RouterState) {
if (!await this.checkPermissions(state)) {
return;
}
await this.administrationScreenService.activationEvent.execute(true);
const screen = this.administrationScreenService.getScreen(state);
const fromScreen = this.administrationScreenService.getScreen(prevState);
if (screen) {
await this.administrationItemService.activate(
screen,
this.administrationScreenService.isConfigurationMode,
screen.item !== fromScreen?.item
);
}
}
private async checkPermissions(state: RouterState) {
if (!await this.isAccessProvided(state)) {
this.screenService.navigateToRoot();
return false;
}
return true;
}
private async isAccessProvided(state: RouterState) {
await this.serverConfigResource.load(null);
if (!await this.permissionsService.hasAsync(EAdminPermission.admin)
&& !this.administrationScreenService.isConfigurationMode) {
return false;
}
if (!this.administrationScreenService.isConfigurationMode
&& this.screenService.isActive(state.name, AdministrationScreenService.setupName)) {
return false;
}
return true;
}
}
@@ -7,6 +7,7 @@
*/
import { injectable, Bootstrap } from '@cloudbeaver/core-di';
import { Executor, IExecutor } from '@cloudbeaver/core-executor';
import { ScreenService } from '@cloudbeaver/core-routing';
import { AppScreen } from './AppScreen';
@@ -14,19 +15,22 @@ import { AppScreen } from './AppScreen';
@injectable()
export class AppScreenService extends Bootstrap {
static screenName = 'app';
readonly activation: IExecutor<void>;
constructor(
private screenService: ScreenService
) {
super();
this.activation = new Executor();
}
register() {
register(): void {
this.screenService.create({
name: AppScreenService.screenName,
routes: [{ name: AppScreenService.screenName, path: '/' }],
component: AppScreen,
root: true,
onActivate: async () => { await this.activation.execute(); },
});
}
@@ -6,69 +6,26 @@
* you may not use this file except in compliance with the License.
*/
import { observable } from 'mobx';
import { injectable } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
import { SessionService } from '@cloudbeaver/core-root';
import { GraphQLService, UserAuthInfo } from '@cloudbeaver/core-sdk';
import { UserAuthInfo } from '@cloudbeaver/core-sdk';
import { AuthProviderService } from './AuthProviderService';
import { UserInfoResource } from './UserInfoResource';
@injectable()
export class AuthInfoService {
@observable private user: UserAuthInfo | null = null;
constructor(
private graphQLService: GraphQLService,
private notificationService: NotificationService,
private authProviderService: AuthProviderService,
private sessionService: SessionService
) {
this.sessionService.session.onDataUpdate.subscribe(this.updateAuthInfo.bind(this));
}
private userInfoResource: UserInfoResource,
) { }
get userInfo(): UserAuthInfo | null {
return this.user;
return this.userInfoResource.data;
}
async login(provider: string, credentials: Record<string, string>): Promise<UserAuthInfo> {
if (this.user) {
throw new Error('User already logged in');
}
const processedCredentials = await this.authProviderService.processCredentials(provider, credentials);
const { user } = await this.graphQLService.sdk.authLogin({
provider,
credentials: processedCredentials,
});
this.user = user;
await this.updateSession();
return this.user;
return this.userInfoResource.login(provider, credentials);
}
async logout(): Promise<void> {
if (this.user) {
await this.graphQLService.sdk.authLogout();
this.user = null;
await this.updateSession();
}
}
async updateAuthInfo(): Promise<UserAuthInfo | null> {
try {
const { user } = await this.graphQLService.sdk.getSessionUser();
this.user = user || null;
} catch (exception) {
this.notificationService.logException(exception, 'Can\'t load session user');
}
return this.user;
}
private async updateSession() {
await this.sessionService.update();
await this.userInfoResource.logout();
}
}
@@ -0,0 +1,71 @@
/*
* 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 { observable } from 'mobx';
import { injectable } from '@cloudbeaver/core-di';
import { SessionResource } from '@cloudbeaver/core-root';
import { CachedDataResource, GraphQLService, UserAuthInfo } from '@cloudbeaver/core-sdk';
import { AuthProviderService } from './AuthProviderService';
@injectable()
export class UserInfoResource extends CachedDataResource<UserAuthInfo | null, void> {
@observable private loaded: boolean;
constructor(
private graphQLService: GraphQLService,
private authProviderService: AuthProviderService,
private sessionResource: SessionResource
) {
super(null);
this.loaded = false;
this.sessionResource.onDataOutdated.subscribe(() => this.markOutdated());
this.sessionResource.onDataUpdate.subscribe(() => this.load());
}
isLoaded(): boolean {
return this.loaded;
}
async login(provider: string, credentials: Record<string, string>): Promise<UserAuthInfo> {
if (this.data) {
throw new Error('User already logged in');
}
const processedCredentials = await this.authProviderService.processCredentials(provider, credentials);
const { user } = await this.graphQLService.sdk.authLogin({
provider,
credentials: processedCredentials,
});
this.data = user;
await this.updateSession();
return this.data;
}
async logout(): Promise<void> {
if (this.data) {
await this.graphQLService.sdk.authLogout();
this.data = null;
await this.updateSession();
}
}
protected async loader(): Promise<UserAuthInfo | null> {
const { user } = await this.graphQLService.sdk.getSessionUser();
this.loaded = true;
return user ?? null;
}
private async updateSession() {
await this.sessionResource.refresh(null);
}
}
@@ -3,4 +3,5 @@ export * from './AuthProviderService';
export * from './AuthProvidersResource';
export * from './RolesManagerService';
export * from './RolesResource';
export * from './UserInfoResource';
export * from './UsersResource';
@@ -43,6 +43,7 @@ import {
AuthProvidersResource,
RolesManagerService,
RolesResource,
UserInfoResource,
UsersResource
} from '@cloudbeaver/core-authentication';
import { BlocksLocaleService } from '@cloudbeaver/core-blocks';
@@ -106,6 +107,7 @@ export const coreManifest: PluginManifest = {
AuthProvidersResource,
RolesManagerService,
RolesResource,
UserInfoResource,
UsersResource,
ServerSettingsService,
ServerConfigResource,
@@ -69,10 +69,9 @@ export const Options = observer(function Options({
}: Props) {
const controller = useController(OptionsController, model);
const translate = useTranslate();
const [focusedRef] = useFocus<HTMLFormElement>({ focusFirstChild: true });
return styled(useStyles(styles))(
<SubmittingForm ref={focusedRef} onChange={controller.onFormChange} onSubmit={onSave}>
<SubmittingForm onChange={controller.onFormChange} onSubmit={onSave}>
<FormBox>
<FormBoxElement>
<FormGroup>
@@ -88,7 +88,8 @@ implements IInitializableController {
return false;
}
for (const property of this.model.connection.authProperties) {
if (property.value !== null && this.model.credentials[property.id!] !== property.value) {
// commented because of case when name changed, password should be reset
if (/* property.value !== null && */this.model.credentials[property.id!] !== property.value) {
return true;
}
}
@@ -75,6 +75,7 @@ export class CreateConnectionController {
id: uuid(),
driverId,
template: false,
saveCredentials: false,
authProperties: [],
properties: {},
} as Partial<AdminConnection> as any;
@@ -86,6 +87,7 @@ export class CreateConnectionController {
id: uuid(),
driverId: database.defaultDriver,
template: false,
saveCredentials: false,
host: database.host,
port: `${database.port}`,
authProperties: [],
@@ -47,7 +47,7 @@ export const ServerConfigurationInfoForm: React.FC<Props> = observer(function Se
required
long
>
{translate('administration_configuration_wizard_configuration_server_session_expire_time')}
{translate('administration_configuration_wizard_configuration_server_session_lifetime')}
</InputField>
</FormGroup>
</>
@@ -142,8 +142,8 @@ export class ServerConfigurationService {
adminName: 'cbadmin',
adminPassword: '',
anonymousAccessEnabled: true,
authenticationEnabled: false,
customConnectionsEnabled: false,
authenticationEnabled: true,
customConnectionsEnabled: true,
sessionExpireTime: 30,
},
navigatorConfig: {
@@ -13,7 +13,7 @@ export default [
['administration_configuration_wizard_configuration_plugins', 'Configuration'],
['administration_configuration_wizard_configuration_server_info', 'Server Info'],
['administration_configuration_wizard_configuration_server_name', 'Server Name'],
['administration_configuration_wizard_configuration_server_session_expire_time', 'Session Expire Time'],
['administration_configuration_wizard_configuration_server_session_lifetime', 'Session lifetime'],
['administration_configuration_wizard_configuration_admin', 'Administrator'],
['administration_configuration_wizard_configuration_admin_name', 'Admin user name'],
['administration_configuration_wizard_configuration_admin_password', 'Admin password'],
@@ -1,4 +1,5 @@
export default [
['administration_configuration_wizard_configuration_server_session_lifetime', 'Время сессии'],
['administration_configuration_wizard_configuration_custom_connections', 'Разрешить пользовательские подключения'],
['administration_configuration_wizard_configuration_custom_connections_description', 'Позволяет пользователям создавать собственные подключения. Иначе, любые подключения могут быть добавлены только со страницы администрирования'],
['administration_server_configuration_save_confirmation_title', 'Изменение настроек сервера'],
@@ -47,7 +47,7 @@ export class AuthMenuService extends Bootstrap {
{
id: 'logout',
order: Number.MAX_SAFE_INTEGER,
isHidden: () => !this.serverService.config.data?.authenticationEnabled || !this.authInfoService.userInfo,
isHidden: () => !this.authInfoService.userInfo,
title: 'authentication_logout',
onClick: this.authenticationService.logout.bind(this.authenticationService),
}
@@ -6,91 +6,87 @@
* you may not use this file except in compliance with the License.
*/
import { AuthInfoService } from '@cloudbeaver/core-authentication';
import { AdministrationScreenService } from '@cloudbeaver/core-administration';
import { AppScreenService } from '@cloudbeaver/core-app';
import { UserInfoResource } from '@cloudbeaver/core-authentication';
import { injectable, Bootstrap } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
import { ServerService, SessionService } from '@cloudbeaver/core-root';
import { ScreenService } from '@cloudbeaver/core-routing';
import { AuthDialogService } from './Dialog/AuthDialogService';
@injectable()
export class AuthenticationService extends Bootstrap {
private authenticating = false;
private authPromise: Promise<void> | null;
constructor(
private screenService: ScreenService,
private appScreenService: AppScreenService,
private serverService: ServerService,
private sessionService: SessionService,
private authDialogService: AuthDialogService,
private authInfoService: AuthInfoService,
private userInfoResource: UserInfoResource,
private notificationService: NotificationService,
private readonly administrationScreenService: AdministrationScreenService
) {
super();
this.authPromise = null;
}
async authUser(provider: string | null = null): Promise<void> {
if (this.authenticating) {
return;
}
this.authenticating = true;
try {
const config = await this.serverService.config.load(null);
if (!config) {
throw new Error('Can\'t configure Authentication');
}
if (!config.authenticationEnabled) {
return;
}
const userInfo = await this.authInfoService.updateAuthInfo();
if (userInfo) {
return;
}
await this.authDialogService.showLoginForm(false, provider);
} finally {
this.authenticating = false;
}
await this.auth(false, provider);
}
async logout(): Promise<void> {
try {
await this.authInfoService.logout();
await this.userInfoResource.logout();
if (!this.administrationScreenService.isConfigurationMode) {
this.screenService.navigateToRoot();
}
} catch (exception) {
this.notificationService.logException(exception, 'Can\'t logout');
}
}
private async auth() {
if (this.authenticating) {
return;
private async auth(persistent: boolean, provider: string | null = null) {
if (this.authPromise) {
return this.authPromise;
}
this.authenticating = true;
this.authPromise = this.wrap(persistent, provider);
try {
const config = await this.serverService.config.load(null);
if (!config) {
throw new Error('Can\'t configure Authentication');
}
if (!config.authenticationEnabled || config.configurationMode) {
return;
}
const userInfo = await this.authInfoService.updateAuthInfo();
if (userInfo) {
return;
}
if (!config.anonymousAccessEnabled) {
await this.authDialogService.showLoginForm(true);
}
await this.authPromise;
} finally {
this.authenticating = false;
this.authPromise = null;
}
}
private async wrap(persistent: boolean, provider: string | null = null) {
const userInfo = await this.userInfoResource.load();
if (userInfo) {
return;
}
await this.authDialogService.showLoginForm(persistent, provider);
}
private async requireAuthentication() {
const config = await this.serverService.config.load(null);
if (!config) {
throw new Error('Can\'t configure Authentication');
}
if (config.anonymousAccessEnabled || !config.authenticationEnabled || config.configurationMode) {
return;
}
await this.auth(true);
}
register(): void | Promise<void> {
this.serverService.config.onDataUpdate.subscribe(this.auth.bind(this));
this.sessionService.session.onDataUpdate.subscribe(this.auth.bind(this));
this.sessionService.session.onDataUpdate.subscribe(() => this.requireAuthentication());
this.appScreenService.activation.addHandler(() => this.requireAuthentication());
this.administrationScreenService.ensurePermissions.addHandler(async () => await this.auth(false));
}
load() { }