diff --git a/README.md b/README.md index 5b1b5caa11..1e2a5c3d37 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java index e3b1a166f0..0dc49325f9 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java @@ -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 authProperties) throws DBWebException; + WebConnectionInfo initConnection(WebSession webSession, String connectionId, Map authProperties, Boolean saveCredentials) throws DBWebException; @WebAction WebConnectionInfo createConnection(WebSession webSession, WebConnectionConfig connectionConfig) throws DBWebException; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java index 664664381f..69ddff7e10 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java @@ -73,7 +73,7 @@ public class WebServiceBindingCore extends WebServiceBindingBase .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"))) diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java index e328f8b22f..ad44651d6a 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java @@ -220,7 +220,7 @@ public class WebServiceCore implements DBWServiceCore { } @Override - public WebConnectionInfo initConnection(WebSession webSession, String connectionId, Map authProperties) throws DBWebException { + public WebConnectionInfo initConnection(WebSession webSession, String connectionId, Map 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; } diff --git a/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreen.tsx b/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreen.tsx index 4c3b7d61ef..095ed2cb6f 100644 --- a/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreen.tsx +++ b/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreen.tsx @@ -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 ; - } + const accessProvided = usePermission(EAdminPermission.admin); return ( <> - + {accessProvided && ( + + )} ); }); diff --git a/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreenService.ts b/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreenService.ts index 6aed544112..1148d892aa 100644 --- a/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreenService.ts +++ b/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreenService.ts @@ -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; readonly activationEvent: IExecutor; 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 { + 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 { + 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 { + 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; + } } diff --git a/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreenServiceBootstrap.ts b/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreenServiceBootstrap.ts index ffbede3f40..e844171eeb 100644 --- a/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreenServiceBootstrap.ts +++ b/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreenServiceBootstrap.ts @@ -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; - } } diff --git a/webapp/packages/core-app/src/AppScreen/AppScreenService.ts b/webapp/packages/core-app/src/AppScreen/AppScreenService.ts index 6189b3e9e6..3c225cbfaa 100644 --- a/webapp/packages/core-app/src/AppScreen/AppScreenService.ts +++ b/webapp/packages/core-app/src/AppScreen/AppScreenService.ts @@ -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; 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(); }, }); } diff --git a/webapp/packages/core-authentication/src/AuthInfoService.ts b/webapp/packages/core-authentication/src/AuthInfoService.ts index b8a11c9e8d..eb038448d9 100644 --- a/webapp/packages/core-authentication/src/AuthInfoService.ts +++ b/webapp/packages/core-authentication/src/AuthInfoService.ts @@ -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): Promise { - 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 { - if (this.user) { - await this.graphQLService.sdk.authLogout(); - this.user = null; - await this.updateSession(); - } - } - - async updateAuthInfo(): Promise { - 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(); } } diff --git a/webapp/packages/core-authentication/src/UserInfoResource.ts b/webapp/packages/core-authentication/src/UserInfoResource.ts new file mode 100644 index 0000000000..da8611342f --- /dev/null +++ b/webapp/packages/core-authentication/src/UserInfoResource.ts @@ -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 { + @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): Promise { + 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 { + if (this.data) { + await this.graphQLService.sdk.authLogout(); + this.data = null; + await this.updateSession(); + } + } + + protected async loader(): Promise { + const { user } = await this.graphQLService.sdk.getSessionUser(); + this.loaded = true; + + return user ?? null; + } + + private async updateSession() { + await this.sessionResource.refresh(null); + } +} diff --git a/webapp/packages/core-authentication/src/index.ts b/webapp/packages/core-authentication/src/index.ts index a5234cb6b3..5d67bb70cb 100644 --- a/webapp/packages/core-authentication/src/index.ts +++ b/webapp/packages/core-authentication/src/index.ts @@ -3,4 +3,5 @@ export * from './AuthProviderService'; export * from './AuthProvidersResource'; export * from './RolesManagerService'; export * from './RolesResource'; +export * from './UserInfoResource'; export * from './UsersResource'; diff --git a/webapp/packages/core-bootstrap/src/manifest.ts b/webapp/packages/core-bootstrap/src/manifest.ts index 494cad54fe..7ef00e666a 100644 --- a/webapp/packages/core-bootstrap/src/manifest.ts +++ b/webapp/packages/core-bootstrap/src/manifest.ts @@ -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, diff --git a/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/Options/Options.tsx b/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/Options/Options.tsx index 4f0a96b2a2..3e6190e66c 100644 --- a/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/Options/Options.tsx +++ b/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/Options/Options.tsx @@ -69,10 +69,9 @@ export const Options = observer(function Options({ }: Props) { const controller = useController(OptionsController, model); const translate = useTranslate(); - const [focusedRef] = useFocus({ focusFirstChild: true }); return styled(useStyles(styles))( - + diff --git a/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/Options/OptionsController.ts b/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/Options/OptionsController.ts index ccaf7d579f..f36c0685ed 100644 --- a/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/Options/OptionsController.ts +++ b/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/Options/OptionsController.ts @@ -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; } } diff --git a/webapp/packages/core-connections/src/Administration/Connections/CreateConnection/CreateConnectionController.tsx b/webapp/packages/core-connections/src/Administration/Connections/CreateConnection/CreateConnectionController.tsx index e4e454dafa..5e472c77fe 100644 --- a/webapp/packages/core-connections/src/Administration/Connections/CreateConnection/CreateConnectionController.tsx +++ b/webapp/packages/core-connections/src/Administration/Connections/CreateConnection/CreateConnectionController.tsx @@ -75,6 +75,7 @@ export class CreateConnectionController { id: uuid(), driverId, template: false, + saveCredentials: false, authProperties: [], properties: {}, } as Partial 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: [], diff --git a/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/Form/ServerConfigurationInfoForm.tsx b/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/Form/ServerConfigurationInfoForm.tsx index 3d8772e945..43d01f165e 100644 --- a/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/Form/ServerConfigurationInfoForm.tsx +++ b/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/Form/ServerConfigurationInfoForm.tsx @@ -47,7 +47,7 @@ export const ServerConfigurationInfoForm: React.FC = observer(function Se required long > - {translate('administration_configuration_wizard_configuration_server_session_expire_time')} + {translate('administration_configuration_wizard_configuration_server_session_lifetime')} diff --git a/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/ServerConfigurationService.ts b/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/ServerConfigurationService.ts index 8b7ef80d05..130e9397a9 100644 --- a/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/ServerConfigurationService.ts +++ b/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/ServerConfigurationService.ts @@ -142,8 +142,8 @@ export class ServerConfigurationService { adminName: 'cbadmin', adminPassword: '', anonymousAccessEnabled: true, - authenticationEnabled: false, - customConnectionsEnabled: false, + authenticationEnabled: true, + customConnectionsEnabled: true, sessionExpireTime: 30, }, navigatorConfig: { diff --git a/webapp/packages/plugin-administration/src/locales/en.ts b/webapp/packages/plugin-administration/src/locales/en.ts index 7cec562dfc..389dd2acf7 100644 --- a/webapp/packages/plugin-administration/src/locales/en.ts +++ b/webapp/packages/plugin-administration/src/locales/en.ts @@ -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'], diff --git a/webapp/packages/plugin-administration/src/locales/ru.ts b/webapp/packages/plugin-administration/src/locales/ru.ts index 60797e13b7..da4ff661f9 100644 --- a/webapp/packages/plugin-administration/src/locales/ru.ts +++ b/webapp/packages/plugin-administration/src/locales/ru.ts @@ -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', 'Изменение настроек сервера'], diff --git a/webapp/packages/plugin-authentication/src/AuthMenuService.ts b/webapp/packages/plugin-authentication/src/AuthMenuService.ts index a68a24b782..6f1dfaed7b 100644 --- a/webapp/packages/plugin-authentication/src/AuthMenuService.ts +++ b/webapp/packages/plugin-authentication/src/AuthMenuService.ts @@ -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), } diff --git a/webapp/packages/plugin-authentication/src/AuthenticationService.ts b/webapp/packages/plugin-authentication/src/AuthenticationService.ts index b5ab04e183..36a6df9a1e 100644 --- a/webapp/packages/plugin-authentication/src/AuthenticationService.ts +++ b/webapp/packages/plugin-authentication/src/AuthenticationService.ts @@ -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 | 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 { - 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 { 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 { - 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() { }