Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Export-Package: io.cloudbeaver,
io.cloudbeaver.server.servlets,
io.cloudbeaver.server.websockets,
io.cloudbeaver.service,
io.cloudbeaver.service.core,
io.cloudbeaver.service.navigator,
io.cloudbeaver.service.sql
Import-Package: org.slf4j
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 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.
Expand Down Expand Up @@ -37,4 +37,9 @@

String[] requireGlobalPermissions() default {};

/**
* Whether this action may be invoked while the server is being configured.
*/
boolean configurationModeAllowed() default false;

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.session.WebSessionProvider;
import io.cloudbeaver.server.WebAppUtils;
import io.cloudbeaver.server.WebApplication;
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
import io.cloudbeaver.server.graphql.GraphQLLoggerUtil;
import io.cloudbeaver.service.security.SMUtils;
Expand Down Expand Up @@ -83,6 +84,11 @@
return apiInterface.cast(proxyImpl);
}

@NotNull
protected WebApplication getApplication() {
return WebAppUtils.getWebPlatform().getApplication();
}

@Nullable
public static TypeDefinitionRegistry loadSchemaDefinition(@NotNull Class<?> theClass, @Nullable String schemaPath) {
if (schemaPath == null) {
Expand Down Expand Up @@ -194,15 +200,21 @@
this.env = env;
}

@Nullable
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
public Object invoke(@NotNull Object proxy, @NotNull Method method, @Nullable Object[] args) throws Throwable {
try {
try {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(impl, args);
}
Comment on lines +208 to +210
WebAction webAction = method.getAnnotation(WebAction.class);
WebApplication application = getApplication();
checkConfigurationModeAccess(webAction, application);
Comment thread
yagudin10 marked this conversation as resolved.
WebActionSet actionSet = method.getDeclaringClass().getAnnotation(WebActionSet.class);
if (actionSet != null) {
checkServicePermissions(actionSet);
}
WebAction webAction = method.getAnnotation(WebAction.class);
if (webAction != null) {
checkActionPermissions(method, webAction);
}
Expand Down Expand Up @@ -291,7 +303,17 @@
}
}

private void checkConfigurationModeAccess(
@Nullable WebAction webAction,
@NotNull WebApplication application
) throws DBWebExceptionAccessDenied {
if (application.isConfigurationMode() &&
(webAction == null || !webAction.configurationModeAllowed())) {
throw new DBWebExceptionAccessDenied("Action is not available in server configuration mode");
}
}

private void checkServicePermissions(WebActionSet actionSet) throws DBWebException {

Check warning on line 316 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'WebActionSet' is missing a nullability annotation. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java:316:46: warning: Reference type 'WebActionSet' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)
String[] features = actionSet.requireFeatures();
ServletApplication servletApplication = ServletAppUtils.getServletApplication();
for (String feature : features) {
Expand All @@ -303,7 +325,7 @@
}

private void checkActionPermissions(@NotNull Method method, @NotNull WebAction webAction) throws DBWebException {
var application = WebAppUtils.getWebPlatform().getApplication();
var application = getApplication();
if (application.isInitializationMode() && webAction.initializationRequired()) {
String message = "Server initialization in progress: "
+ String.join(",", application.getInitActions().values()) + ".\nDo not restart the server.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
*/
public interface DBWServiceCore extends DBWService {

@WebAction(authRequired = false, initializationRequired = false)
@NotNull
@WebAction(authRequired = false, initializationRequired = false, configurationModeAllowed = true)
WebServerConfig getServerConfig(@Nullable WebSession webSession) throws DBWebException;

/**
Expand All @@ -48,13 +49,14 @@
WebPropertyInfo[] getSystemInformationProperties(@NotNull WebSession webSession);

@WebAction
WebGroupPropertiesInfo<ProductSettingDescriptor> getProductSettings(@NotNull WebSession webSession);

Check warning on line 52 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'WebGroupPropertiesInfo' is missing a nullability annotation. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java:52:5: warning: Reference type 'WebGroupPropertiesInfo' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)

@WebAction
List<WebDatabaseDriverInfo> getDriverList(@NotNull WebSession webSession, String driverId) throws DBWebException;
@NotNull
@WebAction(configurationModeAllowed = true)
List<WebDatabaseDriverInfo> getDriverList(@NotNull WebSession webSession, @Nullable String driverId) throws DBWebException;

@WebAction
List<WebDatabaseAuthModel> getAuthModels(@NotNull WebSession webSession);

Check warning on line 59 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'List' is missing a nullability annotation. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java:59:5: warning: Reference type 'List' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)

@WebAction
List<WebNetworkHandlerDescriptor> getNetworkHandlers(@NotNull WebSession webSession);
Expand All @@ -67,23 +69,26 @@
@Nullable List<String> projectIds) throws DBWebException;

@WebAction(authRequired = false)
List<WebConnectionFolderInfo> getConnectionFolders(

Check warning on line 72 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'List' is missing a nullability annotation. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java:72:5: warning: Reference type 'List' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)
@NotNull WebSession webSession, @Nullable String projectId, @Nullable String id) throws DBWebException;

@WebAction(authRequired = false)
@NotNull
@WebAction(authRequired = false, configurationModeAllowed = true)
String[] getSessionPermissions(@NotNull WebSession webSession) throws DBWebException;

///////////////////////////////////////////
// Session

@WebAction(authRequired = false)
@NotNull
@WebAction(authRequired = false, configurationModeAllowed = true)
WebSession openSession(
@NotNull WebSession webSession,
@Nullable String defaultLocale,
@NotNull HttpServletRequest servletRequest,
@NotNull HttpServletResponse servletResponse) throws DBWebException;

@WebAction(authRequired = false)
@NotNull
@WebAction(authRequired = false, configurationModeAllowed = true)
WebSession getSessionState(@NotNull WebSession webSession) throws DBWebException;

@WebAction
Expand All @@ -104,8 +109,8 @@
@WebAction(authRequired = false)
boolean refreshSessionConnections(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws DBWebException;

@WebAction
boolean changeSessionLanguage(@NotNull WebSession webSession, String locale) throws DBWebException;
@WebAction(configurationModeAllowed = true)
boolean changeSessionLanguage(@NotNull WebSession webSession, @Nullable String locale) throws DBWebException;

///////////////////////////////////////////
// Connections
Expand Down Expand Up @@ -240,10 +245,15 @@
///////////////////////////////////////////
// Async tasks

@WebAction(authRequired = false)
WebAsyncTaskInfo getAsyncTaskInfo(WebSession webSession, String taskId, Boolean removeOnFinish) throws DBWebException;
@NotNull
@WebAction(authRequired = false, configurationModeAllowed = true)
WebAsyncTaskInfo getAsyncTaskInfo(
@NotNull WebSession webSession,
@NotNull String taskId,
@Nullable Boolean removeOnFinish
) throws DBWebException;

@WebAction(authRequired = false)
boolean cancelAsyncTask(WebSession webSession, String taskId) throws DBWebException;
@WebAction(authRequired = false, configurationModeAllowed = true)
boolean cancelAsyncTask(@NotNull WebSession webSession, @NotNull String taskId) throws DBWebException;

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 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.
Expand Down Expand Up @@ -132,7 +132,8 @@ boolean deleteUserCredentials(
////////////////////////////////////////////////////////////////////
// Features

@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
@NotNull
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN, configurationModeAllowed = true)
List<DBWFeatureSet> listFeatureSets(@NotNull WebSession webSession) throws DBWebException;

////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -160,9 +161,10 @@ WebAuthProviderConfiguration saveAuthProviderConfiguration(
////////////////////////////////////////////////////////////////////
// Server configuration

@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN, configurationModeAllowed = true)
boolean configureServer(@NotNull WebSession webSession, @NotNull Map<String, Object> params) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)

@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN, configurationModeAllowed = true)
boolean setDefaultNavigatorSettings(@NotNull WebSession webSession, @NotNull DBNBrowseSettings settings) throws DBWebException;

@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,12 @@ WebLogoutInfo authLogout(
@Nullable String configurationId
) throws DBWebException;

@WebAction(authRequired = false)
@Nullable
@WebAction(authRequired = false, configurationModeAllowed = true)
WebUserInfo activeUser(@NotNull WebSession webSession) throws DBWebException;

@WebAction(authRequired = false)
@NotNull
@WebAction(authRequired = false, configurationModeAllowed = true)
WebAuthProviderInfo[] getAuthProviders(@NotNull HttpServletRequest request) throws DBWebException;

@WebAction()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import io.cloudbeaver.test.platform.fs.FileSystemSecurityTest;
import io.cloudbeaver.test.platform.sql.*;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;
Expand All @@ -45,6 +46,7 @@
RMNIOTest.class,
LocalResourceControllerTest.class,
NoSessionTest.class,
ConfigurationModeAccessTest.class,
FileSystemSecurityTest.class,
WebSessionTest.class,
WebSessionProjectTest.class,
Expand All @@ -64,6 +66,7 @@ public class CEServerTestSuite {
@BeforeAll
public static void startServer() throws Exception {
CEAppStarter.startServerIfNotStarted();
Assertions.assertFalse(CEAppStarter.getTestApp().isConfigurationMode());
}

@AfterAll
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.test.platform;

import graphql.GraphQLContext;
import graphql.schema.DataFetchingEnvironment;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.DBWebExceptionAccessDenied;
import io.cloudbeaver.DBWebExceptionServerNotInitialized;
import io.cloudbeaver.server.WebApplication;
import io.cloudbeaver.service.DBWBindingContext;
import io.cloudbeaver.service.WebServiceBindingBase;
import io.cloudbeaver.service.core.DBWServiceCore;
import org.jkiss.code.NotNull;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.Map;

public class ConfigurationModeAccessTest {

@Test
public void configurationModeAccessIsDeniedByDefault() {
DBWServiceCore service = createService(false);
DBWebExceptionAccessDenied exception = Assertions.assertThrows(
DBWebExceptionAccessDenied.class,
() -> service.getUserConnections(null, null, null, null)
);
Assertions.assertEquals("Action is not available in server configuration mode", exception.getMessage());
Assertions.assertThrows(
DBWebExceptionAccessDenied.class,
() -> service.createConnection(null, null, Map.of())
);
}

@Test
public void explicitlyAllowedActionIsAvailableInConfigurationMode() {
DBWServiceCore service = createService(false);
Assertions.assertDoesNotThrow(() -> service.getServerConfig(null));
}

@Test
public void objectMethodsAreAvailableInConfigurationMode() {
DBWServiceCore service = createService(false);
Assertions.assertAll(
() -> Assertions.assertDoesNotThrow(service::toString),
() -> Assertions.assertDoesNotThrow(service::hashCode),
() -> Assertions.assertDoesNotThrow(() -> service.equals(service))
);
}

@Test
public void initializationCheckIsAppliedToConfigurationModeActions() {
DBWServiceCore service = createService(true);
Assertions.assertThrows(
DBWebExceptionServerNotInitialized.class,
() -> service.getDriverList(null, null)
);
}

@NotNull
private static DBWServiceCore createService(boolean initializationMode) {
DataFetchingEnvironment environment = Mockito.mock(DataFetchingEnvironment.class);
Mockito.when(environment.getGraphQlContext()).thenReturn(GraphQLContext.newContext().build());

WebApplication application = Mockito.mock(WebApplication.class);
Mockito.when(application.isConfigurationMode()).thenReturn(true);
Mockito.when(application.isInitializationMode()).thenReturn(initializationMode);
Mockito.when(application.getInitActions()).thenReturn(Map.of());

return new TestBinding(Mockito.mock(DBWServiceCore.class), application).getServiceProxy(environment);
}

private static class TestBinding extends WebServiceBindingBase<DBWServiceCore> {
private final WebApplication application;

private TestBinding(@NotNull DBWServiceCore service, @NotNull WebApplication application) {
super(DBWServiceCore.class, service, null);
this.application = application;
}

@NotNull
@Override
protected WebApplication getApplication() {
return application;
}

@NotNull
private DBWServiceCore getServiceProxy(@NotNull DataFetchingEnvironment environment) {
return getService(environment);
}

@Override
public void bindWiring(@NotNull DBWBindingContext model) throws DBWebException {
// No GraphQL wiring is needed to exercise the service proxy.
}
}
}
Loading