Skip to content
Merged
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
5 changes: 5 additions & 0 deletions config/template/cloudbeaver-base.conf
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@
value: true
},

dbUserPasswordChangeEnabled: {
env: "CLOUDBEAVER_APP_DB_USER_PASSWORD_CHANGE_ENABLED",
value: false
},

resourceQuotas: {
resourceManagerFileSizeLimit: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_RESOURCE_MANAGER_FILE_SIZE_LIMIT",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public class WebConnectionInfo {
private static final String FEATURE_RESTRICT_METADATA_EDIT = "restrictMetadataEdit";

private static final String TOOL_SESSION_MANAGER = "sessionManager";

private final WebSession session;
private final DBPDataSourceContainer dataSourceContainer;
private WebServerError connectError;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@

boolean isAdminCredentialsSaveEnabled();

default boolean isDbUserPasswordChangeEnabled() {
return false;
}

default String[] getDisabledBetaFeatures() {

Check warning on line 43 in server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebAppConfiguration.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'String[]' is missing a nullability annotation. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebAppConfiguration.java:43:13: warning: Reference type 'String[]' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)
return new String[0];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public class CBAppConfig extends BaseWebAppConfiguration implements ServletAuthC
private boolean forwardProxy;
private boolean publicCredentialsSaveEnabled;
private boolean adminCredentialsSaveEnabled;
private boolean dbUserPasswordChangeEnabled;
private boolean linkExternalCredentialsWithUser;

private boolean redirectOnFederatedAuth;
Expand Down Expand Up @@ -80,6 +81,7 @@ public CBAppConfig() {
this.supportsCustomConnections = true;
this.publicCredentialsSaveEnabled = true;
this.adminCredentialsSaveEnabled = true;
this.dbUserPasswordChangeEnabled = false;
this.redirectOnFederatedAuth = false;
this.enabledDrivers = new String[0];
this.disabledDrivers = new String[0];
Expand All @@ -104,6 +106,7 @@ public CBAppConfig(CBAppConfig src) {
this.supportsCustomConnections = src.supportsCustomConnections;
this.publicCredentialsSaveEnabled = src.publicCredentialsSaveEnabled;
this.adminCredentialsSaveEnabled = src.adminCredentialsSaveEnabled;
this.dbUserPasswordChangeEnabled = src.dbUserPasswordChangeEnabled;
this.redirectOnFederatedAuth = src.redirectOnFederatedAuth;
this.enabledDrivers = src.enabledDrivers;
this.disabledDrivers = src.disabledDrivers;
Expand Down Expand Up @@ -154,6 +157,14 @@ public void setPublicCredentialsSaveEnabled(boolean publicCredentialsSaveEnabled
this.publicCredentialsSaveEnabled = publicCredentialsSaveEnabled;
}

public boolean isDbUserPasswordChangeEnabled() {
return dbUserPasswordChangeEnabled;
}

public void setDbUserPasswordChangeEnabled(boolean dbUserPasswordChangeEnabled) {
this.dbUserPasswordChangeEnabled = dbUserPasswordChangeEnabled;
}

public boolean isAdminCredentialsSaveEnabled() {
return adminCredentialsSaveEnabled;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class AdminServerConfig {
private final boolean customConnectionsEnabled;
private final boolean publicCredentialsSaveEnabled;
private final boolean adminCredentialsSaveEnabled;
private final boolean dbUserPasswordChangeEnabled;
private final List<String> enabledFeatures;
private final List<String> enabledAuthProviders;
private final String[] enabledDrivers;
Expand Down Expand Up @@ -75,6 +76,11 @@ public AdminServerConfig(@NotNull Map<String, Object> params) {
"adminCredentialsSaveEnabled",
appConfig.isAdminCredentialsSaveEnabled()
);
this.dbUserPasswordChangeEnabled = JSONUtils.getBoolean(
params,
"dbUserPasswordChangeEnabled",
appConfig.isDbUserPasswordChangeEnabled()
);
this.resourceManagerEnabled = JSONUtils.getBoolean(params, "resourceManagerEnabled", appConfig.isResourceManagerEnabled());
this.secretManagerEnabled = JSONUtils.getBoolean(params, "secretManagerEnabled", appConfig.isSecretManagerEnabled());

Expand Down Expand Up @@ -163,6 +169,10 @@ public boolean isAdminCredentialsSaveEnabled() {
return adminCredentialsSaveEnabled;
}

public boolean isDbUserPasswordChangeEnabled() {
return dbUserPasswordChangeEnabled;
}

public long getSessionExpireTime() {
return sessionExpireTime;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,11 @@ protected Map<String, Object> collectConfigurationProperties(
appConfigProperties,
"adminCredentialsSaveEnabled",
appConfig.isAdminCredentialsSaveEnabled());
copyConfigValue(
oldAppConfig,
appConfigProperties,
"dbUserPasswordChangeEnabled",
appConfig.isDbUserPasswordChangeEnabled());
copyConfigValue(
oldAppConfig, appConfigProperties, "enableReverseProxyAuth", appConfig.isEnabledReverseProxyAuth());
copyConfigValue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ protected void populateConfigurations(@NotNull I input, @NotNull C serverConfig,
appConfig.setSupportsCustomConnections(input.isCustomConnectionsEnabled());
appConfig.setPublicCredentialsSaveEnabled(input.isPublicCredentialsSaveEnabled());
appConfig.setAdminCredentialsSaveEnabled(input.isAdminCredentialsSaveEnabled());
appConfig.setDbUserPasswordChangeEnabled(input.isDbUserPasswordChangeEnabled());
Comment thread
TobyTheHutt marked this conversation as resolved.
updateDisabledFeaturesConfig(appConfig, input.getEnabledFeatures());
// custom logic for enabling embedded drivers
updateDisabledDriversConfig(appConfig, input.getDisabledDrivers());
Expand Down
15 changes: 15 additions & 0 deletions server/bundles/io.cloudbeaver.server/schema/service.core.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ type ServerConfig {
"Defines is it is possible to save global database credentials"
adminCredentialsSaveEnabled: Boolean!

"Enables database user password changes."
dbUserPasswordChangeEnabled: Boolean! @since(version: "26.2.2")

"Defines if the server requires a license"
licenseRequired: Boolean!
"Defines if the server license is valid"
Expand Down Expand Up @@ -902,6 +905,18 @@ extend type Mutation {
"Test connection configuration. Returns remote server version"
testConnection( config: ConnectionConfig!, projectId: ID): ConnectionInfo!

"""
Change the DB user password for this connection.
The caller MUST have PERMISSION_PROJECT_DATASOURCES_EDIT on the connection's project.
Driver exceptions propagate to the caller when the database rejects the change.
"""
changeConnectionUserPassword(
projectId: ID,
connectionId: ID!,
oldPassword: String!,
newPassword: String!
): Boolean! @since(version: "26.2.2")

"Test network handler connectivity"
testNetworkHandler(projectId: ID, connectionId: ID, config: NetworkHandlerConfigInput! ): NetworkEndpointInfo!

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ public boolean isAdminCredentialsSaveEnabled() {
return application.getAppConfiguration().isAdminCredentialsSaveEnabled();
}

@Property
public boolean isDbUserPasswordChangeEnabled() {
return application.getAppConfiguration().isDbUserPasswordChangeEnabled();
}

@Property
public boolean isLicenseRequired() {
return application.isLicenseRequired();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,15 @@ WebConnectionInfo testConnection(
@NotNull Map<String, Object> connectionConfig
) throws DBWebException;

@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATASOURCES_EDIT})
Comment thread
TobyTheHutt marked this conversation as resolved.
boolean changeConnectionUserPassword(
@NotNull WebSession webSession,
@Nullable @WebObjectId String projectId,
@NotNull String connectionId,
@WebParameterSecure @NotNull String oldPassword,
@WebParameterSecure @NotNull String newPassword
) throws DBWebException;

@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATASOURCES_EDIT})
WebNetworkEndpointInfo testNetworkHandler(
@NotNull WebSession webSession,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@ public void bindWiring(DBWBindingContext model) throws DBWebException {
.dataFetcher("testConnection", env -> getService(env).testConnection(
getWebSession(env), getProjectReference(env), getArgumentVal(env, "config")
))
.dataFetcher("changeConnectionUserPassword", env -> getService(env).changeConnectionUserPassword(
getWebSession(env),
getProjectReference(env),
getArgumentVal(env, "connectionId"),
getArgumentVal(env, "oldPassword"),
getArgumentVal(env, "newPassword")
))
.dataFetcher("testNetworkHandler", env -> getService(env).testNetworkHandler(
getWebSession(env),
getProjectReference(env),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,14 @@
import jakarta.servlet.http.HttpServletResponse;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.eclipse.core.runtime.IAdaptable;

Check warning on line 39 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Wrong lexicographical order for 'org.eclipse.core.runtime.IAdaptable' import. Should be before 'org.jkiss.code.Nullable'. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java:39:1: warning: Wrong lexicographical order for 'org.eclipse.core.runtime.IAdaptable' import. Should be before 'org.jkiss.code.Nullable'. (com.puppycrawl.tools.checkstyle.checks.imports.CustomImportOrderCheck)
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBConstants;
import org.jkiss.dbeaver.model.DBPDataSource;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.access.DBAUserPasswordManager;
import org.jkiss.dbeaver.model.DBPDataSourceFolder;

Check warning on line 46 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Wrong lexicographical order for 'org.jkiss.dbeaver.model.DBPDataSourceFolder' import. Should be before 'org.jkiss.dbeaver.model.access.DBAUserPasswordManager'. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java:46:1: warning: Wrong lexicographical order for 'org.jkiss.dbeaver.model.DBPDataSourceFolder' import. Should be before 'org.jkiss.dbeaver.model.access.DBAUserPasswordManager'. (com.puppycrawl.tools.checkstyle.checks.imports.CustomImportOrderCheck)
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.auth.SMObjectType;
Expand Down Expand Up @@ -641,8 +644,160 @@
return testDataSource;
}

@Override
public boolean changeConnectionUserPassword(
Comment thread
TobyTheHutt marked this conversation as resolved.
@NotNull WebSession webSession,
@Nullable String projectId,
@NotNull String connectionId,
@NotNull String oldPassword,
@NotNull String newPassword
) throws DBWebException {
DBPDataSourceContainer container = null;
try {
requireServerFlagEnabled();
container = resolveContainer(webSession, projectId, connectionId);
ensureConnected(webSession, container);
DBAUserPasswordManager manager = resolveUserPasswordManager(container);
String userName = resolveUserName(container);
applyPasswordChange(webSession, projectId, connectionId, manager, userName, oldPassword, newPassword);
persistNewPassword(webSession, container, projectId, connectionId, newPassword);
WebDataSourceUtils.disconnectDataSource(webSession, container, true);
log.info("changeConnectionUserPassword: succeeded " + formatPasswordChangeLogContext(webSession, projectId, connectionId));
return true;
} catch (DBWebException e) {
throw e;
} catch (Throwable t) {
log.error("changeConnectionUserPassword: unexpected error " + formatPasswordChangeLogContext(webSession, projectId, connectionId), t);

Check warning on line 670 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Line is longer than 140 characters (found 146). Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java:670:0: warning: Line is longer than 140 characters (found 146). (com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck)
throw new DBWebException("Password change failed", t);
}
}

private void requireServerFlagEnabled() throws DBWebException {
if (WebAppUtils.getWebApplication().getAppConfiguration().isDbUserPasswordChangeEnabled()) {
return;
}
throw new DBWebException("Password change is disabled by the administrator.");
}

@NotNull
private DBPDataSourceContainer resolveContainer(
@NotNull WebSession webSession,
@Nullable String projectId,
@NotNull String connectionId
) throws DBWebException {
DBPDataSourceContainer container = null;
try {
container = WebDataSourceUtils.getLocalOrGlobalDataSource(webSession, projectId, connectionId);
} catch (DBWebException ignored) {

Check warning on line 691 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Empty catch block. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java:691:42: warning: Empty catch block. (com.puppycrawl.tools.checkstyle.checks.blocks.EmptyCatchBlockCheck)
}
if (container != null) {
return container;
}
throw new DBWebException("Connection not found.");
}

private void ensureConnected(
@NotNull WebSession webSession,
@NotNull DBPDataSourceContainer container
) throws DBWebException {
if (container.isConnected()) {
return;
}
try {
container.connect(webSession.getProgressMonitor(), true, false);
} catch (Exception e) {
throw new DBWebException("Cannot connect to database.", e);
}
}

@NotNull
private DBAUserPasswordManager resolveUserPasswordManager(
@NotNull DBPDataSourceContainer container
) throws DBWebException {
DBAUserPasswordManager manager = null;
DBPDataSource dataSource = container.getDataSource();
if (dataSource instanceof IAdaptable adaptable) {
manager = adaptable.getAdapter(DBAUserPasswordManager.class);
}
if (manager != null) {
return manager;
}
throw new DBWebException("This driver does not support password change from CloudBeaver.");
}

@NotNull
private String resolveUserName(
@NotNull DBPDataSourceContainer container
) throws DBWebException {
String userName = container.getActualConnectionConfiguration().getUserName();
if (CommonUtils.isEmpty(userName)) {
userName = container.getConnectionConfiguration().getUserName();
}
if (!CommonUtils.isEmpty(userName)) {
return userName;
}
throw new DBWebException("Connection has no user name configured.");
}

private void applyPasswordChange(
@NotNull WebSession webSession,
@Nullable String projectId,
@NotNull String connectionId,
@NotNull DBAUserPasswordManager manager,
@NotNull String userName,
@NotNull String oldPassword,
@NotNull String newPassword
) throws DBWebException {
log.info("changeConnectionUserPassword: attempting " + formatPasswordChangeLogContext(webSession, projectId, connectionId));
try {
manager.changeUserPassword(webSession.getProgressMonitor(), userName, newPassword, oldPassword);
} catch (DBException e) {
log.info("changeConnectionUserPassword: handler error " + formatPasswordChangeLogContext(webSession, projectId, connectionId) + " error=" + e.getClass().getName());

Check warning on line 755 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Line is longer than 140 characters (found 176). Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java:755:0: warning: Line is longer than 140 characters (found 176). (com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck)
throw new DBWebException("Password change failed", e);
}
}

private void persistNewPassword(
@NotNull WebSession webSession,
@NotNull DBPDataSourceContainer container,
@Nullable String projectId,
@NotNull String connectionId,
@NotNull String newPassword
) throws DBWebException {
String failureClass = null;
boolean persisted;
try {
container.getConnectionConfiguration().setUserPassword(newPassword);
container.getActualConnectionConfiguration().setUserPassword(newPassword);
var project = container.getProject();
if (project.isUseSecretStorage()) {
container.persistSecrets(DBSSecretController.getProjectSecretController(project));
}
persisted = container.isTemporary() || container.persistConfiguration();
Comment thread
TobyTheHutt marked this conversation as resolved.
} catch (Exception e) {
failureClass = e.getClass().getName();
persisted = false;
}
if (persisted) {
return;
}
log.info("changeConnectionUserPassword: persist failed " + formatPasswordChangeLogContext(webSession, projectId, connectionId) + " error=" + failureClass);

Check warning on line 784 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Line is longer than 140 characters (found 163). Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java:784:0: warning: Line is longer than 140 characters (found 163). (com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck)
throw new DBWebException(
"Database password was changed but CloudBeaver failed to persist the new credential. "
+ "The connection will require re-entry of the new password.");
}

private static String formatPasswordChangeLogContext(

Check warning on line 790 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'String' is missing a nullability annotation. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java:790:20: warning: Reference type 'String' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)
@NotNull WebSession webSession,
@Nullable String projectId,
@NotNull String connectionId
) {
return String.format("sessionId=%s userId=%s projectId=%s connectionId=%s",
webSession.getSessionId(), webSession.getUserId(), projectId, connectionId);
}

@Override
public WebNetworkEndpointInfo testNetworkHandler(

Check warning on line 800 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'WebNetworkEndpointInfo' is missing a nullability annotation. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java:800:12: warning: Reference type 'WebNetworkEndpointInfo' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)
@NotNull WebSession webSession,
@Nullable String projectId,
@Nullable String connectionId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ input ServerConfigInput {
publicCredentialsSaveEnabled: Boolean
"Whether saving credentials is allowed"
adminCredentialsSaveEnabled: Boolean
"Enables database user password changes."
dbUserPasswordChangeEnabled: Boolean @since(version: "26.2.2")
"Whether the resource manager is enabled"
resourceManagerEnabled: Boolean
"Whether the secret manager is enabled"
Expand Down
4 changes: 4 additions & 0 deletions webapp/packages/core-root/src/ServerConfigResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ export class ServerConfigResource extends CachedDataResource<ServerConfig | null
return this.data?.publicCredentialsSaveEnabled ?? false;
}

get dbUserPasswordChangeEnabled(): boolean {
return this.data?.dbUserPasswordChangeEnabled ?? false;
}

get anonymousAccessEnabled(): boolean {
return this.data?.anonymousAccessEnabled ?? false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
mutation changeConnectionUserPassword(
$projectId: ID!,
$connectionId: ID!,
$oldPassword: String!,
$newPassword: String!
) {
result: changeConnectionUserPassword(
projectId: $projectId,
connectionId: $connectionId,
oldPassword: $oldPassword,
newPassword: $newPassword
)
}
Loading
Loading