From f059daf6931e547644321fc9e8ed9bb27531715a Mon Sep 17 00:00:00 2001 From: Alexey Date: Wed, 7 Aug 2024 19:16:57 +0300 Subject: [PATCH 1/6] CB-4990 feat: add authentication timeout (#2829) * CB-4990 feat: add authentication timeout * CB-4990 fix: review --------- Co-authored-by: Evgenia Bezborodova <139753579+EvgeniaBzzz@users.noreply.github.com> --- .../src/UserInfoResource.ts | 2 + .../src/TaskScheduler/TimeoutError.ts | 15 +++++++ .../packages/core-executor/src/whileTask.ts | 39 +++++++++++++++---- 3 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 webapp/packages/core-executor/src/TaskScheduler/TimeoutError.ts diff --git a/webapp/packages/core-authentication/src/UserInfoResource.ts b/webapp/packages/core-authentication/src/UserInfoResource.ts index 8ec9d23768..132327173a 100644 --- a/webapp/packages/core-authentication/src/UserInfoResource.ts +++ b/webapp/packages/core-authentication/src/UserInfoResource.ts @@ -140,6 +140,8 @@ export class UserInfoResource extends CachedDataResource( callback: (value: T) => Promise | boolean, task: () => Promise, interval: number, cancelMessage?: string, + timeout?: number, ): ITask { let resolve: (value: T | PromiseLike) => void; let reject: (reason?: any) => void; + let fulfilled = false; + + let intervalTimeoutId: NodeJS.Timeout | null = null; + let timeoutId: NodeJS.Timeout | null = null; + let activeTask: Promise | null = null; + let stopped = false; const lockPromise = new Promise((_resolve, _reject) => { resolve = _resolve; reject = _reject; - }); + }).finally(() => { + if (intervalTimeoutId !== null) { + clearTimeout(intervalTimeoutId); + } - let timeoutId: NodeJS.Timeout | null; - let activeTask: Promise | null; - let stopped = false; + if (timeoutId !== null) { + clearTimeout(timeoutId); + } + + fulfilled = true; + }); function stop() { - if (timeoutId) { - clearTimeout(timeoutId); + if (intervalTimeoutId !== null) { + clearTimeout(intervalTimeoutId); } stopped = true; } async function cancelTask(exception?: any) { + if (fulfilled) { + return; + } + stop(); if (activeTask instanceof Task) { @@ -44,6 +63,10 @@ export function whileTask( reject(exception); } + if (timeout !== undefined) { + timeoutId = setTimeout(() => cancelTask(new TimeoutError('Task timeout exceeded')), timeout); + } + function runTask() { activeTask = task(); activeTask @@ -56,7 +79,7 @@ export function whileTask( if (state) { resolve(value); } else if (!stopped) { - timeoutId = setTimeout(runTask, interval); + intervalTimeoutId = setTimeout(runTask, interval); } }) .catch(cancelTask); @@ -67,6 +90,6 @@ export function whileTask( runTask(); return lockPromise; }, - () => cancelTask(new Error(cancelMessage ?? 'Task was cancelled')), + () => cancelTask(new CancelError(cancelMessage ?? 'Task was cancelled')), ); } From de85721c8fc438bb3dc5c11522ae15eda6bf9882 Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Wed, 7 Aug 2024 18:17:15 +0200 Subject: [PATCH 2/6] chore: Update exception message in SpreadsheetBootstrap (#2832) Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com> --- .../plugin-data-spreadsheet-new/src/SpreadsheetBootstrap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/SpreadsheetBootstrap.ts b/webapp/packages/plugin-data-spreadsheet-new/src/SpreadsheetBootstrap.ts index 93a97d7654..72a8c94f45 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/SpreadsheetBootstrap.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/SpreadsheetBootstrap.ts @@ -33,7 +33,7 @@ export class SpreadsheetBootstrap extends Bootstrap { exceptionsCatcherService: ExceptionsCatcherService, ) { super(); - exceptionsCatcherService.ignore('ResizeObserver loop limit exceeded'); // Produces by react-data-grid + exceptionsCatcherService.ignore('ResizeObserver loop completed with undelivered notifications.'); // Produces by react-data-grid } register(): void | Promise { From 8e0e0c9193ad07334777d0752362f1184c6cb6d3 Mon Sep 17 00:00:00 2001 From: alex <48489896+devnaumov@users.noreply.github.com> Date: Thu, 8 Aug 2024 12:33:07 +0200 Subject: [PATCH 3/6] CB-5474 remove special behaviour for long names (#2834) --- .../src/SQLEditor/useSqlDialectAutocompletion.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/webapp/packages/plugin-sql-editor-new/src/SQLEditor/useSqlDialectAutocompletion.ts b/webapp/packages/plugin-sql-editor-new/src/SQLEditor/useSqlDialectAutocompletion.ts index 88b48f65a0..36ef3c6c4e 100644 --- a/webapp/packages/plugin-sql-editor-new/src/SQLEditor/useSqlDialectAutocompletion.ts +++ b/webapp/packages/plugin-sql-editor-new/src/SQLEditor/useSqlDialectAutocompletion.ts @@ -12,7 +12,7 @@ import { useService } from '@cloudbeaver/core-di'; import { LocalizationService } from '@cloudbeaver/core-localization'; import { GlobalConstants } from '@cloudbeaver/core-utils'; import type { Compartment, Completion, CompletionConfig, CompletionContext, CompletionResult, Extension } from '@cloudbeaver/plugin-codemirror6'; -import { type ISQLEditorData, SqlEditorSettingsService, type SQLProposal } from '@cloudbeaver/plugin-sql-editor'; +import { type ISQLEditorData, type SQLProposal } from '@cloudbeaver/plugin-sql-editor'; const codemirrorComplexLoader = createComplexLoader(() => import('@cloudbeaver/plugin-codemirror6')); @@ -22,13 +22,11 @@ type SqlCompletion = Completion & { const CLOSE_CHARACTERS = /[\s()[\]{};:>,=\\*]/; const COMPLETION_WORD = /[\w*]*/; -const COMPLETION_WORD_LONG_PROPOSALS = /[\w.*]*/; export function useSqlDialectAutocompletion(data: ISQLEditorData): [Compartment, Extension] { const { closeCompletion, useEditorAutocompletion } = useComplexLoader(codemirrorComplexLoader); const localizationService = useService(LocalizationService); - const sqlEditorSettingsService = useService(SqlEditorSettingsService); - const optionsRef = useObjectRef({ data, sqlEditorSettingsService }); + const optionsRef = useObjectRef({ data }); const [config] = useState(() => { function getOptionsFromProposals(explicit: boolean, word: string, proposals: SQLProposal[]): SqlCompletion[] { @@ -70,7 +68,7 @@ export function useSqlDialectAutocompletion(data: ISQLEditorData): [Compartment, return null; } - const word = context.matchBefore(optionsRef.sqlEditorSettingsService.longNameProposals ? COMPLETION_WORD_LONG_PROPOSALS : COMPLETION_WORD); + const word = context.matchBefore(COMPLETION_WORD); if (word === null) { return null; From 62393937fc3c8cfa1e5bb8c7f458469649e054b3 Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Thu, 8 Aug 2024 15:46:13 +0200 Subject: [PATCH 4/6] CB-5364 adds context menu for data-viewer (#2810) * CB-5364 adds context menu for data-viewer * chore: reverts changes * CB-5364 feat: adds context menu for object viewer --------- Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com> --- .../Table/CellFormatter.tsx | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/ObjectPropertyTable/Table/CellFormatter.tsx b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/ObjectPropertyTable/Table/CellFormatter.tsx index 774ad86797..5a62873aa6 100644 --- a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/ObjectPropertyTable/Table/CellFormatter.tsx +++ b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/ObjectPropertyTable/Table/CellFormatter.tsx @@ -8,7 +8,7 @@ import { observer } from 'mobx-react-lite'; import { useContext, useState } from 'react'; -import { getComputed, Icon, s, useMouse, useS, useStateDelay } from '@cloudbeaver/core-blocks'; +import { getComputed, Icon, s, useMouse, useMouseContextMenu, useS, useStateDelay } from '@cloudbeaver/core-blocks'; import { ConnectionInfoResource, DATA_CONTEXT_CONNECTION } from '@cloudbeaver/core-connections'; import { useDataContextLink } from '@cloudbeaver/core-data-context'; import { useService } from '@cloudbeaver/core-di'; @@ -35,6 +35,7 @@ export const Menu = observer(function Menu({ value, node }) { const mouse = useMouse(); const [menuOpened, switchState] = useState(false); const connection = connectionsInfoResource.getConnectionForNode(node.id); + const mouseContextMenu = useMouseContextMenu(); useDataContextLink(menu.context, (context, id) => { context.set(DATA_CONTEXT_NAV_NODE, node, id); @@ -60,21 +61,25 @@ export const Menu = observer(function Menu({ value, node }) { return !menu.available; }); + function contextMenuOpenHandler(event: React.MouseEvent) { + mouseContextMenu.handleContextMenuOpen(event); + } + + function valueFieldClickHandler(event: React.MouseEvent) { + event.preventDefault(); + } + return ( -
-
-
- {value} + +
+
+
+ {value} +
+ {!menuEmpty && }
- {!menuEmpty && ( - -
- -
-
- )}
-
+ ); }); From 36c089ed066fde784fc3b4d80532f689dc4a24ca Mon Sep 17 00:00:00 2001 From: Alexander Skoblikov Date: Thu, 8 Aug 2024 22:12:50 +0300 Subject: [PATCH 5/6] CB-5375 auto migration to postgres DB for default CB EE configuration (#2809) * CB-5375 auto migration to postgres DB for default CB EE configuration * CB-5375 fix test * CB-5375 move legacy db * CB-5474 async qm data migration * CB-5375 comment unused field --------- Co-authored-by: Evgenia Bezborodova <139753579+EvgeniaBzzz@users.noreply.github.com> --- .../src/io/cloudbeaver/DBWebException.java | 1 + .../cloudbeaver/model/app/WebApplication.java | 4 ++ .../schema/service.core.graphqls | 1 + .../DBWebExceptionServerNotInitialized.java | 27 +++++++++++ .../src/io/cloudbeaver/WebAction.java | 2 + .../io/cloudbeaver/server/CBApplication.java | 28 ++++++++++- .../CBServerConfigurationController.java | 9 ++-- .../service/WebServiceBindingBase.java | 7 ++- .../service/core/DBWServiceCore.java | 2 +- .../META-INF/MANIFEST.MF | 2 +- .../EmbeddedSecurityControllerFactory.java | 47 ++++++++++++++----- .../service/security/db/CBDatabase.java | 20 ++++++-- .../security/db/WebDatabaseConfig.java | 7 +-- .../test/platform/SQLQueryTranslatorTest.java | 6 ++- 14 files changed, 134 insertions(+), 29 deletions(-) create mode 100644 server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebExceptionServerNotInitialized.java diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/DBWebException.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/DBWebException.java index e0e278a13f..f4cc8e50ea 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/DBWebException.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/DBWebException.java @@ -38,6 +38,7 @@ public class DBWebException extends DBException implements GraphQLError { public static final String ERROR_CODE_SESSION_EXPIRED = "sessionExpired"; public static final String ERROR_CODE_ACCESS_DENIED = "accessDenied"; + public static final String ERROR_CODE_SERVER_NOT_INITIALIZED = "serverNotInitialized"; public static final String ERROR_CODE_LICENSE_DENIED = "licenseRequired"; public static final String ERROR_CODE_IDENT_REQUIRED = "identRequired"; public static final String ERROR_CODE_AUTH_REQUIRED = "authRequired"; diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebApplication.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebApplication.java index 6e14a4e705..6e5a2ccf49 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebApplication.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebApplication.java @@ -42,6 +42,10 @@ public interface WebApplication extends DBPApplication { boolean isConfigurationMode(); + default boolean isInitializationMode() { + return false; + } + WebAppConfiguration getAppConfiguration(); WebServerConfiguration getServerConfiguration(); diff --git a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls index 622b05fa23..7bdc0106e7 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls @@ -148,6 +148,7 @@ type ServerConfig { localHostAddress: String configurationMode: Boolean! + # initializationMode: Boolean! @since(version: "24.1.5") developmentMode: Boolean! redirectOnFederatedAuth: Boolean! distributed: Boolean! diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebExceptionServerNotInitialized.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebExceptionServerNotInitialized.java new file mode 100644 index 0000000000..610d7c414e --- /dev/null +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebExceptionServerNotInitialized.java @@ -0,0 +1,27 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudbeaver; + +/** + * "Access denied" exception + */ +public class DBWebExceptionServerNotInitialized extends DBWebException { + + public DBWebExceptionServerNotInitialized(String message) { + super(message, ERROR_CODE_SERVER_NOT_INITIALIZED); + } +} diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebAction.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebAction.java index b0aa55ea64..873864f142 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebAction.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebAction.java @@ -33,4 +33,6 @@ boolean authRequired() default true; + boolean initializationRequired() default true; + } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java index ff1d0d6017..32cc1ac346 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java @@ -69,6 +69,7 @@ import java.security.Policy; import java.security.ProtectionDomain; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * This class controls all aspects of the application's execution @@ -108,6 +109,8 @@ public static CBApplication getInstance() { private WebSessionManager sessionManager; + private final Map initActions = new ConcurrentHashMap<>(); + public CBApplication() { this.homeDirectory = new File(initHomeFolder()); } @@ -552,7 +555,8 @@ public synchronized void finishConfiguration( reloadConfiguration(credentialsProvider); } - public synchronized void reloadConfiguration(@Nullable SMCredentialsProvider credentialsProvider) throws DBException { + public synchronized void reloadConfiguration(@Nullable SMCredentialsProvider credentialsProvider) + throws DBException { // Re-load runtime configuration try { Path runtimeAppConfigPath = getServerConfigurationController().getRuntimeAppConfigPath(); @@ -717,7 +721,10 @@ public Class getPlatformUIClass() { return CBPlatformUI.class; } - public void saveProductConfiguration(SMCredentialsProvider credentialsProvider, Map productConfiguration) throws DBException { + public void saveProductConfiguration( + SMCredentialsProvider credentialsProvider, + Map productConfiguration + ) throws DBException { getServerConfigurationController().saveProductConfiguration(productConfiguration); flushConfiguration(credentialsProvider); sendConfigChangedEvent(credentialsProvider); @@ -750,4 +757,21 @@ private void refreshDisabledDriversConfig() { public boolean isEnvironmentVariablesAccessible() { return getAppConfiguration().isSystemVariablesResolvingEnabled(); } + + @Override + public boolean isInitializationMode() { + return !initActions.isEmpty(); + } + + public void addInitAction(@NotNull String actionId, @NotNull String description) { + initActions.put(actionId, description); + } + + public void removeInitAction(@NotNull String actionId) { + initActions.remove(actionId); + } + + public Map getInitActions() { + return Map.copyOf(initActions); + } } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerConfigurationController.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerConfigurationController.java index 40d925de13..b4233baf3f 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerConfigurationController.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerConfigurationController.java @@ -330,7 +330,7 @@ protected GsonBuilder getGsonBuilder() { .registerTypeAdapter(PasswordPolicyConfiguration.class, smPasswordPoliceConfigCreator); } - protected void saveRuntimeConfig(SMCredentialsProvider credentialsProvider) throws DBException { + public synchronized void saveRuntimeConfig(SMCredentialsProvider credentialsProvider) throws DBException { saveRuntimeConfig( serverConfiguration, appConfiguration, @@ -338,7 +338,7 @@ protected void saveRuntimeConfig(SMCredentialsProvider credentialsProvider) thro ); } - protected void saveRuntimeConfig( + protected synchronized void saveRuntimeConfig( @NotNull CBServerConfig serverConfig, @NotNull CBAppConfig appConfig, SMCredentialsProvider credentialsProvider @@ -350,7 +350,7 @@ protected void saveRuntimeConfig( writeRuntimeConfig(getRuntimeAppConfigPath(), configurationProperties); } - private void writeRuntimeConfig(Path runtimeConfigPath, Map configurationProperties) + private synchronized void writeRuntimeConfig(Path runtimeConfigPath, Map configurationProperties) throws DBException { if (Files.exists(runtimeConfigPath)) { ContentUtils.makeFileBackup(runtimeConfigPath); @@ -370,7 +370,8 @@ private void writeRuntimeConfig(Path runtimeConfigPath, Map conf } - public void updateServerUrl(@NotNull SMCredentialsProvider credentialsProvider, @Nullable String newPublicUrl) throws DBException { + public synchronized void updateServerUrl(@NotNull SMCredentialsProvider credentialsProvider, + @Nullable String newPublicUrl) throws DBException { getServerConfiguration().setServerURL(newPublicUrl); } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java index ac61c1f9c0..7a85a39e97 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java @@ -250,6 +250,12 @@ private void checkServicePermissions(Method method, WebActionSet actionSet) thro } private void checkActionPermissions(@NotNull Method method, @NotNull WebAction webAction) throws DBWebException { + CBApplication application = CBApplication.getInstance(); + if (application.isInitializationMode() && webAction.initializationRequired()) { + String message = "Server initialization in progress: " + + String.join(",", application.getInitActions().values()) + ".\nDo not restart the server."; + throw new DBWebExceptionServerNotInitialized(message); + } String[] reqPermissions = webAction.requirePermissions(); if (reqPermissions.length == 0 && !webAction.authRequired()) { return; @@ -258,7 +264,6 @@ private void checkActionPermissions(@NotNull Method method, @NotNull WebAction w if (session == null) { throw new DBWebExceptionAccessDenied("No open session - anonymous access restricted"); } - CBApplication application = CBApplication.getInstance(); if (!application.isConfigurationMode()) { if (webAction.authRequired() && !session.isAuthorizedInSecurityManager()) { log.debug("Anonymous access to " + method.getName() + " restricted"); 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 cabced5886..48f78fbe7a 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 @@ -38,7 +38,7 @@ */ public interface DBWServiceCore extends DBWService { - @WebAction(authRequired = false) + @WebAction(authRequired = false, initializationRequired = false) WebServerConfig getServerConfig() throws DBWebException; @WebAction(authRequired = false) diff --git a/server/bundles/io.cloudbeaver.service.security/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.service.security/META-INF/MANIFEST.MF index 7dd72ecd6c..a7d81b179c 100644 --- a/server/bundles/io.cloudbeaver.service.security/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.service.security/META-INF/MANIFEST.MF @@ -13,7 +13,7 @@ Require-Bundle: org.jkiss.dbeaver.model;visibility:=reexport, org.jkiss.dbeaver.model.sql, org.jkiss.dbeaver.model.sql.jdbc, org.jkiss.dbeaver.registry;visibility:=reexport, - org.jkiss.bundle.apache.dbcp, + org.jkiss.bundle.apache.dbcp;visibility:=reexport, io.cloudbeaver.model Export-Package: io.cloudbeaver.auth.provider.local, io.cloudbeaver.auth.provider.rp, diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/EmbeddedSecurityControllerFactory.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/EmbeddedSecurityControllerFactory.java index 6b5992918c..6c7b581b13 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/EmbeddedSecurityControllerFactory.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/EmbeddedSecurityControllerFactory.java @@ -16,10 +16,12 @@ */ package io.cloudbeaver.service.security; +import io.cloudbeaver.auth.NoAuthCredentialsProvider; import io.cloudbeaver.model.app.WebAuthApplication; import io.cloudbeaver.service.security.db.CBDatabase; import io.cloudbeaver.service.security.db.WebDatabaseConfig; import io.cloudbeaver.service.security.internal.ClearAuthAttemptInfoJob; +import org.jkiss.code.NotNull; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.model.auth.SMCredentialsProvider; @@ -36,7 +38,7 @@ public static CBDatabase getDbInstance() { /** * Create new security controller instance with custom configuration */ - public CBEmbeddedSecurityController createSecurityService( + public CBEmbeddedSecurityController createSecurityService( T application, WebDatabaseConfig databaseConfig, SMCredentialsProvider credentialsProvider, @@ -45,32 +47,53 @@ public CBEmbeddedSecurityController createSecurityService( if (DB_INSTANCE == null) { synchronized (EmbeddedSecurityControllerFactory.class) { if (DB_INSTANCE == null) { - DB_INSTANCE = new CBDatabase(application, databaseConfig); + DB_INSTANCE = createAndInitDatabaseInstance( + application, + databaseConfig, + smConfig + ); } } - var securityController = createEmbeddedSecurityController( - application, DB_INSTANCE, credentialsProvider, smConfig - ); - //FIXME circular dependency - DB_INSTANCE.setAdminSecurityController(securityController); - DB_INSTANCE.initialize(); + if (application.isLicenseRequired()) { // delete expired auth info job in enterprise products - new ClearAuthAttemptInfoJob(securityController).schedule(); + new ClearAuthAttemptInfoJob(createEmbeddedSecurityController( + application, DB_INSTANCE, new NoAuthCredentialsProvider(), smConfig + )).schedule(); } - return securityController; } return createEmbeddedSecurityController( application, DB_INSTANCE, credentialsProvider, smConfig ); } - protected CBEmbeddedSecurityController createEmbeddedSecurityController( + protected @NotNull CBDatabase createAndInitDatabaseInstance( + @NotNull T application, + @NotNull WebDatabaseConfig databaseConfig, + @NotNull SMControllerConfiguration smConfig + ) throws DBException { + var database = new CBDatabase(application, databaseConfig); + var securityController = createEmbeddedSecurityController( + application, database, new NoAuthCredentialsProvider(), smConfig + ); + //FIXME circular dependency + database.setAdminSecurityController(securityController); + try { + database.initialize(); + } catch (DBException e) { + database.shutdown(); + throw e; + } + + return database; + } + + protected CBEmbeddedSecurityController createEmbeddedSecurityController( T application, CBDatabase database, SMCredentialsProvider credentialsProvider, SMControllerConfiguration smConfig ) { - return new CBEmbeddedSecurityController(application, database, credentialsProvider, smConfig); + return new CBEmbeddedSecurityController(application, database, credentialsProvider, smConfig); } } diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java index ea1a858221..0c373781e4 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java @@ -475,7 +475,7 @@ public void close() throws SQLException { // Persistence - private void validateInstancePersistentState(Connection connection) throws IOException, SQLException, DBException { + protected void validateInstancePersistentState(Connection connection) throws IOException, SQLException, DBException { try (JDBCTransaction txn = new JDBCTransaction(connection)) { checkInstanceRecord(connection); var defaultTeamId = application.getAppConfiguration().getDefaultUserTeam(); @@ -587,10 +587,24 @@ public SQLDialect getDialect() { } public static boolean isDefaultH2Configuration(WebDatabaseConfig databaseConfiguration) { - var v1DefaultUrl = "jdbc:h2:/opt/cloudbeaver/workspace/.data/" + V1_DB_NAME; - var v2DefaultUrl = "jdbc:h2:/opt/cloudbeaver/workspace/.data/" + V2_DB_NAME; + var workspace = WebAppUtils.getWebApplication().getWorkspaceDirectory(); + var v1Path = workspace.resolve(".data").resolve(V1_DB_NAME); + var v2Path = workspace.resolve(".data").resolve(V2_DB_NAME); + var v1DefaultUrl = "jdbc:h2:" + v1Path; + var v2DefaultUrl = "jdbc:h2:" + v2Path; return v1DefaultUrl.equals(databaseConfiguration.getUrl()) || v2DefaultUrl.equals(databaseConfiguration.getUrl()); } + protected WebDatabaseConfig getDatabaseConfiguration() { + return databaseConfiguration; + } + + protected WebApplication getApplication() { + return application; + } + + protected SMAdminController getAdminSecurityController() { + return adminSecurityController; + } } diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/WebDatabaseConfig.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/WebDatabaseConfig.java index fb7243462a..27bf86f04f 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/WebDatabaseConfig.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/WebDatabaseConfig.java @@ -63,6 +63,7 @@ public String getUser() { return user; } + @Override public String getPassword() { return password; @@ -85,15 +86,15 @@ public String getSchema() { return schema; } - void setPassword(String password) { + public void setPassword(String password) { this.password = password; } - void setSchema(String schema) { + public void setSchema(String schema) { this.schema = schema; } - void setUser(String user) { + public void setUser(String user) { this.user = user; } } diff --git a/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/test/platform/SQLQueryTranslatorTest.java b/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/test/platform/SQLQueryTranslatorTest.java index 3c06bbce97..416aaa8656 100644 --- a/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/test/platform/SQLQueryTranslatorTest.java +++ b/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/test/platform/SQLQueryTranslatorTest.java @@ -162,8 +162,10 @@ public void createTableWithAutoincrement() throws DBException { Map expectedSqlByDialect = new HashMap<>(); expectedSqlByDialect.put(new H2SQLDialect(), basicSql); expectedSqlByDialect.put(new PostgreDialect(), "CREATE SEQUENCE CB_TEST_TYPES_AUTOINC_COLUMN;\n" + - "CREATE TABLE CB_TEST_TYPES (AUTOINC_COLUMN BIGINT NOT NULL DEFAULT NEXTVAL('CB_TEST_TYPES_AUTOINC_COLUMN'));" + - "\n"); + "CREATE TABLE CB_TEST_TYPES (AUTOINC_COLUMN BIGINT NOT NULL DEFAULT NEXTVAL" + + "('CB_TEST_TYPES_AUTOINC_COLUMN'));\n" + + "ALTER SEQUENCE CB_TEST_TYPES_AUTOINC_COLUMN OWNED BY CB_TEST_TYPES.AUTOINC_COLUMN;\n" + ); expectedSqlByDialect.put(new MySQLDialect(), basicSql); expectedSqlByDialect.put( From 8d14e42797a0869f032d0e314317e3ed48f9d6b9 Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Fri, 9 Aug 2024 13:30:32 +0200 Subject: [PATCH 6/6] CB-4068 adds driver libraries resource (#2826) Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com> --- webapp/packages/core-connections/src/DBDriverResource.ts | 1 - .../core-sdk/src/queries/connections/driverLibraries.gql | 9 +++++++++ .../core-sdk/src/queries/connections/driverList.gql | 1 - .../core-sdk/src/queries/fragments/DatabaseDriver.gql | 6 ------ 4 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 webapp/packages/core-sdk/src/queries/connections/driverLibraries.gql diff --git a/webapp/packages/core-connections/src/DBDriverResource.ts b/webapp/packages/core-connections/src/DBDriverResource.ts index 351a63f5f2..68af6b6ee8 100644 --- a/webapp/packages/core-connections/src/DBDriverResource.ts +++ b/webapp/packages/core-connections/src/DBDriverResource.ts @@ -109,7 +109,6 @@ export class DBDriverResource extends CachedMapResource