Cb 5875 creating schemas only for pro (#3324)

This commit is contained in:
Ruslan Musaev
2025-04-10 09:06:09 +00:00
committed by GitHub
parent 42da37830f
commit 02a8226196
8 changed files with 227 additions and 175 deletions
@@ -1,7 +1,8 @@
CREATE TABLE {table_prefix}CB_SCHEMA_INFO
(
VERSION INTEGER NOT NULL,
UPDATE_TIME TIMESTAMP NOT NULL
MODULE_ID VARCHAR(10) NOT NULL,
VERSION INTEGER NOT NULL,
UPDATE_TIME TIMESTAMP NOT NULL
);
CREATE TABLE {table_prefix}CB_INSTANCE
@@ -236,65 +237,3 @@ CREATE TABLE {table_prefix}CB_AUTH_ATTEMPT_INFO
PRIMARY KEY (AUTH_ID, AUTH_PROVIDER_ID),
FOREIGN KEY (AUTH_ID) REFERENCES {table_prefix}CB_AUTH_ATTEMPT (AUTH_ID) ON DELETE CASCADE
);
-- Secrets
CREATE TABLE {table_prefix}CB_USER_SECRETS
(
USER_ID VARCHAR(128) NOT NULL,
SECRET_ID VARCHAR(512) NOT NULL,
SECRET_VALUE TEXT NOT NULL,
SECRET_LABEL VARCHAR(128),
SECRET_DESCRIPTION VARCHAR(1024),
ENCODING_TYPE VARCHAR(32) DEFAULT 'PLAINTEXT' NOT NULL,
UPDATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (USER_ID, SECRET_ID),
FOREIGN KEY (USER_ID) REFERENCES {table_prefix}CB_USER (USER_ID) ON DELETE CASCADE
);
CREATE TABLE {table_prefix}CB_SUBJECT_SECRETS
(
SUBJECT_ID VARCHAR(128) NOT NULL,
SECRET_ID VARCHAR(255) NOT NULL,
PROJECT_ID VARCHAR(128),
OBJECT_TYPE VARCHAR(32),
OBJECT_ID VARCHAR(128),
SECRET_VALUE TEXT NOT NULL,
ENCODING_TYPE VARCHAR(32) DEFAULT 'PLAINTEXT' NOT NULL,
CREATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
UPDATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (SUBJECT_ID, SECRET_ID),
FOREIGN KEY (SUBJECT_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE CASCADE
);
CREATE INDEX IDX_SUBJECT_SECRETS_PROJECT ON {table_prefix}CB_SUBJECT_SECRETS (PROJECT_ID,SUBJECT_ID);
CREATE INDEX IDX_SUBJECT_SECRETS_OBJECT ON {table_prefix}CB_SUBJECT_SECRETS (PROJECT_ID,OBJECT_TYPE,OBJECT_ID);
-- Tasks
CREATE TABLE {table_prefix}CB_TASKS
(
TASK_ID VARCHAR(128) NOT NULL,
CREATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
TIMEOUT INTEGER NOT NULL,
PRIMARY KEY (TASK_ID)
);
CREATE TABLE {table_prefix}CB_ACCESS_TOKEN
(
TOKEN_ID VARCHAR(128) NOT NULL,
USER_ID VARCHAR(128) NOT NULL,
TOKEN_NAME VARCHAR(128) NOT NULL,
CREATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
EXPIRATION_TIME TIMESTAMP NULL,
PRIMARY KEY (USER_ID, TOKEN_ID),
UNIQUE (USER_ID, TOKEN_NAME),
FOREIGN KEY (USER_ID) REFERENCES {table_prefix}CB_USER(USER_ID) ON DELETE CASCADE
);
@@ -14,4 +14,3 @@ CREATE TABLE {table_prefix}CB_USER_SECRETS
);
CREATE INDEX CB_USER_SECRETS_ID ON {table_prefix}CB_USER_SECRETS (USER_ID,SECRET_ID);
@@ -9,4 +9,4 @@ CREATE TABLE {table_prefix}CB_ACCESS_TOKEN
PRIMARY KEY (USER_ID, TOKEN_ID),
UNIQUE (USER_ID, TOKEN_NAME),
FOREIGN KEY (USER_ID) REFERENCES {table_prefix}CB_USER(USER_ID) ON DELETE CASCADE
);
);
@@ -0,0 +1,2 @@
ALTER TABLE {table_prefix}CB_SCHEMA_INFO
ADD MODULE_ID VARCHAR(10) DEFAULT 'CB_CE' NOT NULL;
@@ -10,4 +10,4 @@ CREATE TABLE {table_prefix}CB_AUTH_TOKEN
PRIMARY KEY (TOKEN_ID),
FOREIGN KEY (SESSION_ID) REFERENCES {table_prefix}CB_SESSION (SESSION_ID) ON DELETE CASCADE,
FOREIGN KEY (USER_ID) REFERENCES {table_prefix}CB_USER (USER_ID) ON DELETE CASCADE
);
);
@@ -17,6 +17,7 @@
package io.cloudbeaver.service.security;
import io.cloudbeaver.auth.NoAuthCredentialsProvider;
import io.cloudbeaver.model.app.ServletApplication;
import io.cloudbeaver.model.app.ServletAuthApplication;
import io.cloudbeaver.model.config.SMControllerConfiguration;
import io.cloudbeaver.model.config.WebDatabaseConfig;
@@ -73,7 +74,7 @@ public class EmbeddedSecurityControllerFactory<T extends ServletAuthApplication>
@NotNull WebDatabaseConfig databaseConfig,
@NotNull SMControllerConfiguration smConfig
) throws DBException {
var database = new CBDatabase(application, databaseConfig);
var database = makeDatabase(application, databaseConfig);
var securityController = createEmbeddedSecurityController(
application, database, new NoAuthCredentialsProvider(), smConfig
);
@@ -97,4 +98,8 @@ public class EmbeddedSecurityControllerFactory<T extends ServletAuthApplication>
) {
return new CBEmbeddedSecurityController<T>(application, database, credentialsProvider, smConfig);
}
protected CBDatabase makeDatabase(ServletApplication application, WebDatabaseConfig databaseConfig) {
return new CBDatabase(application, databaseConfig);
}
}
@@ -44,7 +44,6 @@ import org.jkiss.dbeaver.model.security.user.SMTeam;
import org.jkiss.dbeaver.model.security.user.SMUser;
import org.jkiss.dbeaver.model.sql.db.InternalDB;
import org.jkiss.dbeaver.model.sql.schema.SQLSchemaConfig;
import org.jkiss.dbeaver.model.sql.schema.SQLSchemaVersionManager;
import org.jkiss.dbeaver.registry.DataSourceProviderRegistry;
import org.jkiss.dbeaver.registry.storage.H2Migrator;
import org.jkiss.dbeaver.runtime.DBWorkbench;
@@ -54,7 +53,6 @@ import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.IOUtils;
import org.jkiss.utils.SecurityUtils;
import javax.sql.DataSource;
import java.io.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -65,6 +63,7 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;
import javax.sql.DataSource;
/**
* Database management
@@ -72,15 +71,18 @@ import java.util.stream.Collectors;
public class CBDatabase extends InternalDB<WebDatabaseConfig> {
private static final Log log = Log.getLog(CBDatabase.class);
private static final int LEGACY_SCHEMA_VERSION = 1;
private static final int CURRENT_SCHEMA_VERSION = 23;
private static final int CURRENT_SCHEMA_VERSION = 24;
private static final String SCHEMA_ID = "CB_CE";
private static final SQLSchemaConfig SCHEMA_CREATE_CONFIG = new SQLSchemaConfig(
"CB",
SCHEMA_ID,
"db/cb_schema_create.sql",
"db/cb_schema_update_",
CURRENT_SCHEMA_VERSION,
0
0,
new CBSchemaVersionManager(CURRENT_SCHEMA_VERSION, SCHEMA_ID),
CBDatabase.class.getClassLoader(),
null
);
private static final String DEFAULT_DB_USER_NAME = "cb-data";
@@ -97,8 +99,23 @@ public class CBDatabase extends InternalDB<WebDatabaseConfig> {
private SMAdminController adminSecurityController;
public CBDatabase(@NotNull ServletApplication application, @NotNull WebDatabaseConfig databaseConfiguration) {
super("Security Manager", databaseConfiguration, SCHEMA_CREATE_CONFIG);
this(application, databaseConfiguration, Collections.emptyList());
}
public CBDatabase(
@NotNull ServletApplication application,
@NotNull WebDatabaseConfig databaseConfiguration,
@NotNull List<SQLSchemaConfig> sqlSchemaConfigList
) {
super("Security Manager", databaseConfiguration, appendSchemaConfig(sqlSchemaConfigList));
this.application = application;
SCHEMA_CREATE_CONFIG.setInitialSchemaFiller(this::fillInitialSchemaData);
}
private static List<SQLSchemaConfig> appendSchemaConfig(List<SQLSchemaConfig> sqlSchemaConfigList) {
List<SQLSchemaConfig> sqlSchemaConfigs = new ArrayList<>(sqlSchemaConfigList);
sqlSchemaConfigs.add(0, SCHEMA_CREATE_CONFIG);
return sqlSchemaConfigs;
}
public void setAdminSecurityController(SMAdminController adminSecurityController) {
@@ -201,7 +218,7 @@ public class CBDatabase extends InternalDB<WebDatabaseConfig> {
throw new DBException("CB database connection is not defined");
}
createSchemaIfNotExists(connection);
updateSchema(monitor, connection, CBDatabase.class.getClassLoader(), new CBSchemaVersionManager());
updateSchema(monitor, connection);
validateInstancePersistentState(connection);
}
@@ -322,113 +339,63 @@ public class CBDatabase extends InternalDB<WebDatabaseConfig> {
closeConnection();
}
private class CBSchemaVersionManager implements SQLSchemaVersionManager {
@Override
public int getCurrentSchemaVersion(DBRProgressMonitor monitor, Connection connection, String schemaName)
throws DBException, SQLException {
// Check and update schema
try {
int version = CommonUtils.toInt(JDBCUtils.executeQuery(connection,
normalizeTableNames("SELECT VERSION FROM {table_prefix}CB_SCHEMA_INFO")));
return version == 0 ? 1 : version;
} catch (SQLException e) {
try {
Object legacyVersion = CommonUtils.toInt(JDBCUtils.executeQuery(connection,
normalizeTableNames("SELECT SCHEMA_VERSION FROM {table_prefix}CB_SERVER")));
// Table CB_SERVER exist - this is a legacy schema
return LEGACY_SCHEMA_VERSION;
} catch (SQLException ex) {
// Empty schema. Create it from scratch
return -1;
public void fillInitialSchemaData(DBRProgressMonitor monitor, Connection connection) throws DBException, SQLException {
// Set exclusive connection. Otherwise security controller will open a new one and won't see new schema objects.
exclusiveConnection = new DelegatingConnection<Connection>(connection) {
@Override
public void close() throws SQLException {
// do nothing
}
};
try {
// Fill initial data
if (initialData == null) {
return;
}
String adminName = initialData.getAdminName();
String adminPassword = initialData.getAdminPassword();
List<SMTeam> initialTeams = initialData.getTeams();
String defaultTeam = application.getAppConfiguration().getDefaultUserTeam();
if (CommonUtils.isNotEmpty(defaultTeam)) {
Set<String> initialTeamNames = initialTeams == null
? Set.of()
: initialTeams.stream().map(SMTeam::getTeamId).collect(Collectors.toSet());
if (!initialTeamNames.contains(defaultTeam)) {
throw new DBException("Initial teams configuration doesn't contain default team " + defaultTeam);
}
}
}
@Override
public int getLatestSchemaVersion() {
return SCHEMA_CREATE_CONFIG.schemaVersionActual();
}
@Override
public void updateCurrentSchemaVersion(
DBRProgressMonitor monitor,
@NotNull Connection connection,
@NotNull String schemaName,
int version
) throws DBException, SQLException {
var updateCount = JDBCUtils.executeUpdate(
connection,
normalizeTableNames("UPDATE {table_prefix}CB_SCHEMA_INFO SET VERSION=?,UPDATE_TIME=CURRENT_TIMESTAMP"),
version
);
if (updateCount <= 0) {
JDBCUtils.executeSQL(
connection,
normalizeTableNames(
"INSERT INTO {table_prefix}CB_SCHEMA_INFO (VERSION,UPDATE_TIME) VALUES(?,CURRENT_TIMESTAMP)"),
version
);
}
}
@Override
//TODO move out
public void fillInitialSchemaData(DBRProgressMonitor monitor, Connection connection)
throws DBException, SQLException {
// Set exclusive connection. Otherwise security controller will open a new one and won't see new schema objects.
exclusiveConnection = new DelegatingConnection<Connection>(connection) {
@Override
public void close() throws SQLException {
// do nothing
}
};
try {
// Fill initial data
if (initialData == null) {
return;
}
String adminName = initialData.getAdminName();
String adminPassword = initialData.getAdminPassword();
List<SMTeam> initialTeams = initialData.getTeams();
String defaultTeam = application.getAppConfiguration().getDefaultUserTeam();
if (CommonUtils.isNotEmpty(defaultTeam)) {
Set<String> initialTeamNames = initialTeams == null
? Set.of()
: initialTeams.stream().map(SMTeam::getTeamId).collect(Collectors.toSet());
if (!initialTeamNames.contains(defaultTeam)) {
throw new DBException("Initial teams configuration doesn't contain default team " + defaultTeam);
if (!CommonUtils.isEmpty(initialTeams)) {
// Create teams
for (SMTeam team : initialTeams) {
adminSecurityController.createTeam(
team.getTeamId(),
team.getName(),
team.getDescription(),
adminName
);
if (!application.isMultiNode()) {
adminSecurityController.setSubjectPermissions(
team.getTeamId(),
new ArrayList<>(team.getPermissions()),
"initial-data-configuration"
);
}
}
if (!CommonUtils.isEmpty(initialTeams)) {
// Create teams
for (SMTeam team : initialTeams) {
adminSecurityController.createTeam(team.getTeamId(),
team.getName(),
team.getDescription(),
adminName);
if (!application.isMultiNode()) {
adminSecurityController.setSubjectPermissions(
team.getTeamId(),
new ArrayList<>(team.getPermissions()),
"initial-data-configuration"
);
}
}
}
if (!CommonUtils.isEmpty(adminName)) {
// Create admin user
createAdminUser(adminName, adminPassword);
}
} finally {
exclusiveConnection = null;
}
if (!CommonUtils.isEmpty(adminName)) {
// Create admin user
createAdminUser(adminName, adminPassword);
}
} finally {
exclusiveConnection = null;
}
}
//////////////////////////////////////////
// Persistence
@@ -0,0 +1,140 @@
package io.cloudbeaver.service.security.db;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.sql.schema.SQLSchemaVersionManager;
import org.jkiss.utils.CommonUtils;
import java.sql.Connection;
import java.sql.SQLException;
public class CBSchemaVersionManager implements SQLSchemaVersionManager {
private static final Log log = Log.getLog(CBSchemaVersionManager.class);
private static final int LEGACY_SCHEMA_VERSION = 1;
private final int currentSchemaVersion;
private final String schemaId;
public CBSchemaVersionManager(int currentSchemaVersion, String schemaId) {
this.currentSchemaVersion = currentSchemaVersion;
this.schemaId = schemaId;
}
@Override
public int getCurrentSchemaVersion(
DBRProgressMonitor monitor,
Connection connection,
String schemaName
) throws DBException, SQLException {
Integer version = tryGetVersion(
connection,
CommonUtils.normalizeTableNames("SELECT VERSION FROM {table_prefix}CB_SCHEMA_INFO WHERE MODULE_ID = ?", schemaName),
getSchemaId()
);
if (version != null) {
return version;
}
version = tryGetVersion(
connection,
CommonUtils.normalizeTableNames("SELECT VERSION FROM {table_prefix}CB_SCHEMA_INFO", schemaName)
);
if (version != null) {
return version;
}
version = tryGetVersion(
connection,
CommonUtils.normalizeTableNames("SELECT SCHEMA_VERSION FROM {table_prefix}CB_SERVER", schemaName)
);
if (version != null) {
return LEGACY_SCHEMA_VERSION;
}
// Empty schema. Create it from scratch
return -1;
}
@Override
public int getLatestSchemaVersion() {
return currentSchemaVersion;
}
@Override
public void updateCurrentSchemaVersion(
DBRProgressMonitor monitor,
@NotNull Connection connection,
@NotNull String schemaName,
int version
) throws DBException, SQLException {
var selectCount = CommonUtils.toInt(JDBCUtils.executeQuery(
connection,
CommonUtils.normalizeTableNames(
"SELECT count(1) FROM {table_prefix}CB_SCHEMA_INFO",
schemaName
)
));
if (selectCount <= 0) {
log.debug("Didn't find any records in " +
CommonUtils.normalizeTableNames("{table_prefix}CB_SCHEMA_INFO", schemaName));
JDBCUtils.executeSQL(
connection,
CommonUtils.normalizeTableNames(
"INSERT INTO {table_prefix}CB_SCHEMA_INFO (MODULE_ID, VERSION,UPDATE_TIME) VALUES(?, ?, CURRENT_TIMESTAMP)",
schemaName
),
getSchemaId(),
version
);
return;
}
if (selectCount == 1) {
log.debug("Found only one record in " +
CommonUtils.normalizeTableNames("{table_prefix}CB_SCHEMA_INFO", schemaName));
JDBCUtils.executeUpdate(
connection,
CommonUtils.normalizeTableNames(
"UPDATE {table_prefix}CB_SCHEMA_INFO SET VERSION=?,UPDATE_TIME=CURRENT_TIMESTAMP",
schemaName
),
version
);
return;
}
JDBCUtils.executeUpdate(
connection,
CommonUtils.normalizeTableNames(
"UPDATE {table_prefix}CB_SCHEMA_INFO SET VERSION=?,UPDATE_TIME=CURRENT_TIMESTAMP WHERE MODULE_ID = ?",
schemaName
),
version,
getSchemaId()
);
}
protected Integer tryGetVersion(Connection connection, String sql, Object... params) {
return tryGetVersion(connection, sql, 1, params);
}
protected Integer tryGetVersion(Connection connection, String sql, Integer defaultVersion, Object... params) {
try {
Object result = JDBCUtils.executeQuery(connection, sql, params);
return result == null ? defaultVersion : CommonUtils.toInt(result);
} catch (Exception e) {
try {
connection.rollback();
} catch (SQLException ex) {
log.error("Can't rollback after unsuccessful try to get version of current schema", ex);
}
return null;
}
}
protected String getSchemaId() {
return schemaId;
}
}