mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
Merge branch 'devel' into feat/version-update-procedure
This commit is contained in:
@@ -9,12 +9,10 @@ See out [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
|
||||
|
||||

|
||||
|
||||
## 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
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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")))
|
||||
|
||||
+9
-1
@@ -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;
|
||||
}
|
||||
|
||||
+8
-9
@@ -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}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
+96
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+14
-98
@@ -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,
|
||||
|
||||
+1
-2
@@ -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>
|
||||
|
||||
+2
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -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: [],
|
||||
|
||||
+1
-1
@@ -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>
|
||||
</>
|
||||
|
||||
+2
-2
@@ -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() { }
|
||||
|
||||
Reference in New Issue
Block a user