CB-262 Server configuration mode implementation

This commit is contained in:
Serge Rider
2020-08-28 19:37:32 +03:00
parent cda49295ac
commit d118ec47a8
4 changed files with 132 additions and 40 deletions
@@ -42,10 +42,18 @@ public class CBAppConfig {
return authenticationEnabled;
}
public void setAuthenticationEnabled(boolean authenticationEnabled) {
this.authenticationEnabled = authenticationEnabled;
}
public boolean isAnonymousAccessEnabled() {
return anonymousAccessEnabled;
}
public void setAnonymousAccessEnabled(boolean anonymousAccessEnabled) {
this.anonymousAccessEnabled = anonymousAccessEnabled;
}
public String getAnonymousUserRole() {
return anonymousUserRole;
}
@@ -58,6 +66,10 @@ public class CBAppConfig {
return supportsCustomConnections;
}
public void setSupportsCustomConnections(boolean supportsCustomConnections) {
this.supportsCustomConnections = supportsCustomConnections;
}
public boolean isSupportsConnectionBrowser() {
return supportsConnectionBrowser;
}
@@ -19,11 +19,13 @@ package io.cloudbeaver.server;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.google.gson.stream.JsonWriter;
import io.cloudbeaver.DBWSecurityController;
import io.cloudbeaver.server.jetty.CBJettyServer;
import org.eclipse.core.runtime.Platform;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.osgi.service.datalocation.Location;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.ModelPreferences;
@@ -36,6 +38,7 @@ import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.dbeaver.utils.PrefUtils;
import org.jkiss.dbeaver.utils.SystemVariablesResolver;
import org.jkiss.utils.Base64;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.StandardConstants;
@@ -160,7 +163,7 @@ public class CBApplication extends BaseApplicationImpl {
try {
loadConfiguration(configPath);
File runtimeConfigFile = new File(new File(workspaceLocation, CBConstants.RUNTIME_DATA_DIR_NAME), CBConstants.RUNTIME_CONFIG_FILE_NAME);
File runtimeConfigFile = getRuntimeConfigFile();
if (runtimeConfigFile.exists()) {
log.debug("Runtime configuration [" + runtimeConfigFile.getAbsolutePath() + "]");
parseConfiguration(runtimeConfigFile);
@@ -240,6 +243,11 @@ public class CBApplication extends BaseApplicationImpl {
return null;
}
@NotNull
private File getRuntimeConfigFile() {
return new File(new File(workspaceLocation, CBConstants.RUNTIME_DATA_DIR_NAME), CBConstants.RUNTIME_CONFIG_FILE_NAME);
}
private void initializeDatabase() throws DBException {
if (databaseConfiguration == null) {
throw new DBException("Database configuration missing");
@@ -379,18 +387,24 @@ public class CBApplication extends BaseApplicationImpl {
return configurationMode;
}
public synchronized void finishConfiguration() throws DBException {
public synchronized void finishConfiguration(
String newServerName,
String adminName,
String adminPassword,
CBAppConfig appConfig) throws DBException
{
if (!isConfigurationMode()) {
throw new DBException("Application must be in configuration mode");
}
// Save runtime configuration
log.debug("Saving runtime configuration");
saveRuntimeConfig(newServerName, adminPassword, appConfig);
// Re-load runtime configuration
try {
log.debug("Reloading application configuration");
File runtimeConfigFile = new File(new File(workspaceLocation, CBConstants.RUNTIME_DATA_DIR_NAME), CBConstants.RUNTIME_CONFIG_FILE_NAME);
File runtimeConfigFile = getRuntimeConfigFile();
if (runtimeConfigFile.exists()) {
log.debug("Runtime configuration [" + runtimeConfigFile.getAbsolutePath() + "]");
parseConfiguration(runtimeConfigFile);
@@ -399,11 +413,50 @@ public class CBApplication extends BaseApplicationImpl {
throw new DBException("Error parsing configuration", e);
}
// Re-create database
database.finishConfiguration();
database.finishConfiguration(adminName, adminPassword);
configurationMode = CommonUtils.isEmpty(serverName);
}
private void saveRuntimeConfig(String newServerName, String dbPassword, CBAppConfig appConfig) throws DBException {
File runtimeConfigFile = getRuntimeConfigFile();
try (Writer out = new OutputStreamWriter(new FileOutputStream(runtimeConfigFile), StandardCharsets.UTF_8)) {
Gson gson = new GsonBuilder()
.setLenient()
.create();
try (JsonWriter json = gson.newJsonWriter(out)) {
json.beginObject();
{
json.name("server");
json.beginObject();
if (!CommonUtils.isEmpty(newServerName)) {
JSONUtils.field(json, "serverName", newServerName);
}
if (!CommonUtils.isEmpty(dbPassword)) {
json.name("database");
json.beginObject();
JSONUtils.field(json, "password", Base64.encode(dbPassword.getBytes()));
json.endObject();
}
json.endObject();
}
{
json.name("app");
json.beginObject();
if (appConfig.isAnonymousAccessEnabled()) JSONUtils.field(json, "anonymousAccessEnabled", true);
if (appConfig.isAuthenticationEnabled()) JSONUtils.field(json, "authenticationEnabled", true);
if (appConfig.isSupportsCustomConnections()) JSONUtils.field(json, "supportsCustomConnections", true);
json.endObject();
}
json.endObject();
}
} catch (IOException e) {
throw new DBException("Error writing runtime configuration", e);
}
}
public DBNBrowseSettings getDefaultNavigatorSettings() {
return defaultNavigatorSettings;
}
@@ -30,6 +30,7 @@ import org.apache.commons.dbcp2.PoolableConnectionFactory;
import org.apache.commons.dbcp2.PoolingDataSource;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBConstants;
@@ -129,17 +130,21 @@ public class CBDatabase {
checkDatabaseStructure();
}
void finishConfiguration() throws DBException {
void finishConfiguration(String adminName, String adminPassword) throws DBException {
if (!application.isConfigurationMode()) {
throw new DBException("Database is already configured");
}
if (cbDataSource != null) {
log.debug("Drop embedded datasource");
log.debug("Alter admin settings embedded datasource");
try {
// Drop database
// Alter admin password
try (Connection connection = cbDataSource.getConnection()) {
try (Statement dbStat = connection.createStatement()) {
dbStat.execute("DROP ALL OBJECTS DELETE FILES");
dbStat.execute("ALTER USER " + databaseConfiguration.getUser() +
" SET PASSWORD '" + databaseConfiguration.getPassword() + "'");
}
if (!CommonUtils.isEmpty(adminName) && !CommonUtils.isEmpty(adminPassword)) {
createAdminUser(adminName, adminPassword);
}
}
cbDataSource.close();
@@ -272,50 +277,58 @@ public class CBDatabase {
Gson gson = new GsonBuilder().setLenient().create();
CBDatabaseInitialData initialData = gson.fromJson(reader, CBDatabaseInitialData.class);
WebUser adminUser = null;
if (!CommonUtils.isEmpty(initialData.getAdminName())) {
// Create admin user
adminUser = new WebUser(initialData.getAdminName());
serverController.createUser(adminUser);
String userPassword = initialData.getAdminPassword();
// This is how client password will be transmitted from client
String clientPassword = LocalAuthProvider.makeClientPasswordHash(adminUser.getUserId(), userPassword);
Map<String, Object> credentials = new LinkedHashMap<>();
credentials.put(LocalAuthProvider.CRED_USER, adminUser.getUserId());
credentials.put(LocalAuthProvider.CRED_PASSWORD, clientPassword);
WebAuthProviderDescriptor authProvider = WebServiceRegistry.getInstance().getAuthProvider(LocalAuthProvider.PROVIDER_ID);
if (authProvider != null) {
serverController.setUserCredentials(adminUser.getUserId(), authProvider, credentials);
}
}
if (!CommonUtils.isEmpty(initialData.getRoles())) {
// Create roles
for (WebRole role : initialData.getRoles()) {
serverController.createRole(role);
if (adminUser != null) {
serverController.setSubjectPermissions(role.getRoleId(), role.getPermissions().toArray(new String[0]), adminUser.getUserId());
}
// if (adminUser != null) {
// serverController.setSubjectPermissions(role.getRoleId(), role.getPermissions().toArray(new String[0]), adminUser.getUserId());
// }
}
}
if (adminUser != null) {
// Grant all roles
WebRole[] allRoles = serverController.readAllRoles();
serverController.setUserRoles(
adminUser.getUserId(),
Arrays.stream(allRoles).map(WebRole::getRoleId).toArray(String[]::new),
adminUser.getUserId());
String adminName = initialData.getAdminName();
String adminPassword = initialData.getAdminPassword();
if (!CommonUtils.isEmpty(adminName)) {
// Create admin user
createAdminUser(adminName, adminPassword);
}
} catch (Exception e) {
log.error("Error loading initial data configuration", e);
}
}
@NotNull
private WebUser createAdminUser(String adminName, String adminPassword) throws DBCException {
DBWSecurityController serverController = application.getSecurityController();
WebUser adminUser;
adminUser = new WebUser(adminName);
serverController.createUser(adminUser);
// This is how client password will be transmitted from client
String clientPassword = LocalAuthProvider.makeClientPasswordHash(adminUser.getUserId(), adminPassword);
Map<String, Object> credentials = new LinkedHashMap<>();
credentials.put(LocalAuthProvider.CRED_USER, adminUser.getUserId());
credentials.put(LocalAuthProvider.CRED_PASSWORD, clientPassword);
WebAuthProviderDescriptor authProvider = WebServiceRegistry.getInstance().getAuthProvider(LocalAuthProvider.PROVIDER_ID);
if (authProvider != null) {
serverController.setUserCredentials(adminUser.getUserId(), authProvider, credentials);
}
// Grant all roles
WebRole[] allRoles = serverController.readAllRoles();
serverController.setUserRoles(
adminUser.getUserId(),
Arrays.stream(allRoles).map(WebRole::getRoleId).toArray(String[]::new),
adminUser.getUserId());
return adminUser;
}
void shutdown() {
log.debug("Shutdown database");
if (cbDataSource != null) {
@@ -29,6 +29,7 @@ import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebPermissionDescriptor;
import io.cloudbeaver.registry.WebServiceDescriptor;
import io.cloudbeaver.registry.WebServiceRegistry;
import io.cloudbeaver.server.CBAppConfig;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBPlatform;
import io.cloudbeaver.service.admin.*;
@@ -297,7 +298,20 @@ public class WebServiceAdmin implements DBWServiceAdmin {
@Override
public boolean configureServer(WebSession webSession, AdminServerConfig config) throws DBWebException {
throw new DBWebException("Not implemented");
try {
CBAppConfig appConfig = new CBAppConfig();
appConfig.setAnonymousAccessEnabled(config.isAnonymousAccessEnabled());
appConfig.setAuthenticationEnabled(config.isAuthenticationEnabled());
appConfig.setSupportsCustomConnections(config.isCustomConnectionsEnabled());
CBApplication.getInstance().finishConfiguration(
config.getServerName(),
config.getAdminName(),
config.getAdminPassword(),
appConfig);
} catch (Throwable e) {
throw new DBWebException("Error configuring server", e);
}
return true;
}
@Override