mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
CB-6271 add timeout of inaction in configuration mode (#3289)
* CB-6271 add timeout of inaction in configuration mode * CB-6271 add timeout of inaction in configuration mode * CB-6271 add timeout of inaction in configuration mode * CB-6271 add timeout of inaction in configuration mode * CB-6271 fix insecure default configuration set timeout to 2 mins * CB-6271 fix insecure default configuration set timeout to one min * CB-6271 handle configuration error properly * CB-6271 catch unhandled error on validation step * CB-6271 add return type * CB-6271 fix insecure default configuration set timeout to one min --------- Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com> Co-authored-by: naumov <iamemptyhuh@gmail.com>
This commit is contained in:
co-authored by
Daria Marutkina
naumov
parent
6c44f8c432
commit
34f4063114
@@ -40,6 +40,7 @@ Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.model.utils,
|
||||
io.cloudbeaver.registry,
|
||||
io.cloudbeaver.server,
|
||||
io.cloudbeaver.server.filters,
|
||||
io.cloudbeaver.service,
|
||||
io.cloudbeaver.service.admin,
|
||||
io.cloudbeaver.service.security,
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 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.server.filters;
|
||||
|
||||
import graphql.execution.instrumentation.InstrumentationContext;
|
||||
import graphql.execution.instrumentation.InstrumentationState;
|
||||
import graphql.execution.instrumentation.SimpleInstrumentationContext;
|
||||
import graphql.execution.instrumentation.SimplePerformantInstrumentation;
|
||||
import graphql.execution.instrumentation.parameters.InstrumentationValidationParameters;
|
||||
import graphql.validation.ValidationError;
|
||||
import io.cloudbeaver.model.app.ServletApplication;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
public class ServerConfigurationTimeLimitFilter extends SimplePerformantInstrumentation {
|
||||
private static final Log log = Log.getLog(ServerConfigurationTimeLimitFilter.class);
|
||||
|
||||
private static final int MINUTES_OF_INACTION_BEFORE_DISABLING_REQUEST_PROCESSING = 60;
|
||||
private final ServletApplication application;
|
||||
|
||||
public ServerConfigurationTimeLimitFilter(ServletApplication application) {
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstrumentationContext<List<ValidationError>> beginValidation(
|
||||
InstrumentationValidationParameters parameters,
|
||||
InstrumentationState state
|
||||
) {
|
||||
boolean isOutOfTime = System.currentTimeMillis() - application.getApplicationStartTime()
|
||||
> Duration.ofMinutes(MINUTES_OF_INACTION_BEFORE_DISABLING_REQUEST_PROCESSING).toMillis();
|
||||
if (application.isConfigurationMode() && isOutOfTime) {
|
||||
log.warn("Server configuration time has expired. A server restart is required to continue.");
|
||||
ValidationError error = ValidationError.newValidationError()
|
||||
.description("Server configuration time has expired. A server restart is required to continue.")
|
||||
.build();
|
||||
return new SimpleInstrumentationContext<>() {
|
||||
@Override
|
||||
public void onCompleted(List<ValidationError> result, Throwable t) {
|
||||
result.clear();
|
||||
result.add(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
return super.beginValidation(parameters, state);
|
||||
}
|
||||
}
|
||||
+9
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2025 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.
|
||||
@@ -21,6 +21,7 @@ import io.cloudbeaver.registry.WebServiceRegistry;
|
||||
import io.cloudbeaver.server.CBApplication;
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
|
||||
import io.cloudbeaver.server.filters.ServerConfigurationTimeLimitFilter;
|
||||
import io.cloudbeaver.server.servlets.CBImageServlet;
|
||||
import io.cloudbeaver.server.servlets.CBStaticServlet;
|
||||
import io.cloudbeaver.server.servlets.WebStatusServlet;
|
||||
@@ -115,7 +116,13 @@ public class CBJettyServer {
|
||||
|
||||
servletContextHandler.addServlet(new ServletHolder("status", new WebStatusServlet()), "/status");
|
||||
|
||||
servletContextHandler.addServlet(new ServletHolder("graphql", new GraphQLEndpoint()), serverConfiguration.getServicesURI() + "gql/*");
|
||||
servletContextHandler.addServlet(
|
||||
new ServletHolder(
|
||||
"graphql",
|
||||
new GraphQLEndpoint(new ServerConfigurationTimeLimitFilter(application))
|
||||
),
|
||||
serverConfiguration.getServicesURI() + "gql/*"
|
||||
);
|
||||
servletContextHandler.addEventListener(new CBServerContextListener(application));
|
||||
|
||||
// Add extensions from services
|
||||
|
||||
+7
-4
@@ -19,7 +19,7 @@ package io.cloudbeaver.server.graphql;
|
||||
import com.google.gson.*;
|
||||
import graphql.*;
|
||||
import graphql.execution.*;
|
||||
import graphql.execution.instrumentation.SimplePerformantInstrumentation;
|
||||
import graphql.execution.instrumentation.Instrumentation;
|
||||
import graphql.language.SourceLocation;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import graphql.schema.GraphQLSchema;
|
||||
@@ -51,7 +51,10 @@ import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class GraphQLEndpoint extends HttpServlet {
|
||||
@@ -73,13 +76,13 @@ public class GraphQLEndpoint extends HttpServlet {
|
||||
.create();
|
||||
private GraphQLBindingContext bindingContext;
|
||||
|
||||
public GraphQLEndpoint() {
|
||||
public GraphQLEndpoint(Instrumentation instrumentation) {
|
||||
GraphQLSchema schema = buildSchema();
|
||||
|
||||
PropertyDataFetcherHelper.setUseLambdaFactory(false);
|
||||
graphQL = GraphQL
|
||||
.newGraphQL(schema)
|
||||
.instrumentation(new SimplePerformantInstrumentation())
|
||||
.instrumentation(instrumentation)
|
||||
.queryExecutionStrategy(new WebExecutionStrategy())
|
||||
.mutationExecutionStrategy(new WebExecutionStrategy())
|
||||
.build();
|
||||
|
||||
+14
-10
@@ -157,17 +157,21 @@ export class ConfigurationWizardService {
|
||||
}
|
||||
|
||||
private async finish() {
|
||||
for (const step of this.steps) {
|
||||
if (step?.configurationWizardOptions?.onConfigurationFinish) {
|
||||
await step.configurationWizardOptions.onConfigurationFinish();
|
||||
try {
|
||||
for (const step of this.steps) {
|
||||
if (step?.configurationWizardOptions?.onConfigurationFinish) {
|
||||
await step.configurationWizardOptions.onConfigurationFinish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.administrationScreenService.clearItemsState();
|
||||
this.administrationScreenService.navigateToRoot();
|
||||
this.notificationService.logSuccess({
|
||||
title: 'administration_configuration_wizard_finish_success_title',
|
||||
message: 'administration_configuration_wizard_finish_success_message',
|
||||
});
|
||||
this.administrationScreenService.clearItemsState();
|
||||
this.administrationScreenService.navigateToRoot();
|
||||
this.notificationService.logSuccess({
|
||||
title: 'administration_configuration_wizard_finish_success_title',
|
||||
message: 'administration_configuration_wizard_finish_success_message',
|
||||
});
|
||||
} catch (exception: any) {
|
||||
this.notificationService.logException(exception, 'core_administration_configuration_wizard_finish_fail_title');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,5 @@ export default [
|
||||
['administration_configuration_wizard_title', 'Initial Server Configuration'],
|
||||
['administration_configuration_wizard_finish_success_title', 'Server configured'],
|
||||
['administration_configuration_wizard_finish_success_message', 'You can log-in as administrator in order to set up additional parameters.'],
|
||||
['core_administration_configuration_wizard_finish_fail_title', 'Failed to configure server'],
|
||||
];
|
||||
|
||||
@@ -14,4 +14,5 @@ export default [
|
||||
'administration_configuration_wizard_finish_success_message',
|
||||
"Vous pouvez vous connecter en tant qu'administrateur pour configurer des paramètres supplémentaires.",
|
||||
],
|
||||
['core_administration_configuration_wizard_finish_fail_title', 'Failed to configure server'],
|
||||
];
|
||||
|
||||
@@ -4,4 +4,5 @@ export default [
|
||||
['administration_configuration_wizard_title', 'Configurazione Iniziale del Server'],
|
||||
['administration_configuration_wizard_finish_success_title', 'Server configured'],
|
||||
['administration_configuration_wizard_finish_success_message', 'You can log-in as administrator in order to set up additional parameters.'],
|
||||
['core_administration_configuration_wizard_finish_fail_title', 'Failed to configure server'],
|
||||
];
|
||||
|
||||
@@ -4,4 +4,5 @@ export default [
|
||||
['administration_configuration_wizard_title', 'Начальная настройка сервера'],
|
||||
['administration_configuration_wizard_finish_success_title', 'Сервер настроен'],
|
||||
['administration_configuration_wizard_finish_success_message', 'Вы можете войти администратором чтобы настроить дополнительные параметры.'],
|
||||
['core_administration_configuration_wizard_finish_fail_title', 'Не удалось настроить сервер'],
|
||||
];
|
||||
|
||||
@@ -11,4 +11,5 @@ export default [
|
||||
['administration_configuration_wizard_title', '初始化服务器配置'],
|
||||
['administration_configuration_wizard_finish_success_title', '服务器已完成配置'],
|
||||
['administration_configuration_wizard_finish_success_message', '您可以使用管理员登录后进行更多参数配置。'],
|
||||
['core_administration_configuration_wizard_finish_fail_title', 'Failed to configure server'],
|
||||
];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2020-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -40,7 +40,7 @@ export abstract class FormPart<TPartState, TFormState = any> implements IFormPar
|
||||
|
||||
this.formState.submitTask.addHandler(executorHandlerFilter(() => this.isLoaded(), this.save.bind(this)));
|
||||
this.formState.formatTask.addHandler(executorHandlerFilter(() => this.isLoaded(), this.format.bind(this)));
|
||||
this.formState.validationTask.addHandler(executorHandlerFilter(() => this.isLoaded(), this.validate.bind(this)));
|
||||
this.formState.validationTask.addHandler(executorHandlerFilter(() => this.isLoaded(), this.handleValidation.bind(this)));
|
||||
|
||||
makeObservable<this, 'loaded' | 'loading' | 'setInitialState'>(this, {
|
||||
initialState: observable,
|
||||
@@ -146,6 +146,15 @@ export abstract class FormPart<TPartState, TFormState = any> implements IFormPar
|
||||
this.setState(toJS(this.initialState));
|
||||
}
|
||||
|
||||
private async handleValidation(data: IFormState<TFormState>, contexts: IExecutionContextProvider<IFormState<TFormState>>): Promise<void> {
|
||||
try {
|
||||
this.exception = null;
|
||||
await this.validate(data, contexts);
|
||||
} catch (exception: any) {
|
||||
this.exception = exception;
|
||||
}
|
||||
}
|
||||
|
||||
protected setInitialState(initialState: TPartState) {
|
||||
this.initialState = initialState;
|
||||
|
||||
|
||||
+15
-2
@@ -11,6 +11,7 @@ import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
import { CommonDialogService, DialogueStateResult } from '@cloudbeaver/core-dialogs';
|
||||
import { SessionDataResource } from '@cloudbeaver/core-root';
|
||||
import { formValidationContext } from '@cloudbeaver/core-ui';
|
||||
import { getFirstException } from '@cloudbeaver/core-utils';
|
||||
|
||||
import { ADMINISTRATION_SERVER_CONFIGURATION_ITEM } from './ServerConfiguration/ADMINISTRATION_SERVER_CONFIGURATION_ITEM.js';
|
||||
import { ServerConfigurationFormStateManager } from './ServerConfiguration/ServerConfigurationFormStateManager.js';
|
||||
@@ -71,8 +72,20 @@ export class ConfigurationWizardPagesBootstrapService extends Bootstrap {
|
||||
return true;
|
||||
},
|
||||
onConfigurationFinish: async () => {
|
||||
await this.serverConfigurationFormStateManager.formState?.save();
|
||||
await this.sessionDataResource.refresh();
|
||||
const state = this.serverConfigurationFormStateManager.formState;
|
||||
if (state) {
|
||||
const saved = await state.save();
|
||||
|
||||
if (!saved) {
|
||||
const error = getFirstException(state.exception);
|
||||
|
||||
if (error) {
|
||||
throw getFirstException(error);
|
||||
}
|
||||
}
|
||||
|
||||
await this.sessionDataResource.refresh();
|
||||
}
|
||||
},
|
||||
onLoad: () => {
|
||||
this.serverConfigurationFormStateManager.create();
|
||||
|
||||
Reference in New Issue
Block a user