
io.mosip.registration.config.DaoConfig Maven / Gradle / Ivy
package io.mosip.registration.config;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.*;
import javax.sql.DataSource;
import io.mosip.registration.context.ApplicationContext;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import com.google.common.annotations.VisibleForTesting;
import io.mosip.kernel.clientcrypto.constant.ClientCryptoManagerConstant;
import io.mosip.kernel.clientcrypto.service.impl.ClientCryptoFacade;
import io.mosip.kernel.core.exception.ExceptionUtils;
import io.mosip.kernel.core.logger.spi.Logger;
import io.mosip.kernel.dataaccess.hibernate.config.HibernateDaoConfig;
import io.mosip.kernel.dataaccess.hibernate.constant.HibernatePersistenceConstant;
import io.mosip.registration.exception.RegBaseCheckedException;
import io.mosip.registration.exception.RegistrationExceptionConstants;
import lombok.SneakyThrows;
import static io.mosip.registration.constants.RegistrationConstants.*;
/**
*
* Data source and properties loading from the Database.
*
* @author Omsai Eswar M
*
*/
@ComponentScan(basePackages = { "io.mosip.kernel.core", "io.mosip.kernel.clientcrypto.service.impl",
"io.mosip.kernel.partnercertservice.service", "io.mosip.kernel.partnercertservice.helper" })
public class DaoConfig extends HibernateDaoConfig {
private static final Logger LOGGER = AppConfig.getLogger(DaoConfig.class);
private static final String LOGGER_CLASS_NAME = "REGISTRATION - DAO Config - DB";
private static final String dbPath = "db/reg";
private static final String DRIVER_CLASS_NAME = "org.apache.derby.jdbc.EmbeddedDriver";
private static final String URL = "jdbc:derby:%s;bootPassword=%s";
private static final String SHUTDOWN_URL = "jdbc:derby:;shutdown=true;deregister=false;";
private static final String ENCRYPTION_URL_ATTRIBUTES = "dataEncryption=true;encryptionKeyLength=256;encryptionAlgorithm=AES/CFB/NoPadding;";
private static final String SCHEMA_NAME = "REG";
private static final String SEPARATOR = "-BREAK-";
private static final String BOOTPWD_KEY = "bootPassword";
private static final String USERNAME_KEY = "username";
private static final String PWD_KEY = "password";
private static final String STATE_KEY = "state";
private static final String ERROR_STATE = "0";
private static final String SAFE_STATE = "1";
private static Properties keys;
private static JdbcTemplate jdbcTemplate;
private static final String GLOBAL_PARAM_PROPERTIES = "SELECT CODE, VAL FROM REG.GLOBAL_PARAM WHERE IS_ACTIVE=TRUE AND VAL IS NOT NULL";
private static final String KEY = "CODE";
private static final String VALUE = "VAL";
private static final String LOCAL_PREFERENCES = "SELECT NAME, VAL FROM REG.LOCAL_PREFERENCES WHERE IS_DELETED=FALSE AND CONFIG_TYPE='CONFIGURATION' AND VAL IS NOT NULL";
private static final String NAME = "NAME";
@Autowired
private ClientCryptoFacade clientCryptoFacade;
@Autowired
private ConfigurableEnvironment environment;
private static boolean isPPCUpdated = false;
private DriverManagerDataSource driverManagerDataSource = null;
private static ApplicationContext applicationContext;
private static final SecureRandom secureRandom = new SecureRandom();
static {
try (InputStream configKeys = DaoConfig.class.getClassLoader().getResourceAsStream("spring.properties");
InputStream buildKeys = DaoConfig.class.getClassLoader().getResourceAsStream("props/mosip-application.properties")) {
applicationContext = io.mosip.registration.context.ApplicationContext.getInstance();
keys = new Properties();
keys.load(configKeys);
keys.load(buildKeys);
keys.keySet().forEach( k -> {
applicationContext.getApplicationMap().put((String) k, keys.get(k));
});
} catch (Exception e) {
LOGGER.error(LOGGER_CLASS_NAME, APPLICATION_NAME, APPLICATION_ID,
"Exception encountered during context initialization - DaoConfig "
+ ExceptionUtils.getStackTrace(e));
System.exit(0);
}
}
/*
* (non-Javadoc)
*
* @see
* io.mosip.kernel.dataaccess.hibernate.config.HibernateDaoConfig#dataSource()
*/
@SneakyThrows
@Override
@Bean(name = "dataSource")
public DataSource dataSource() {
if (this.driverManagerDataSource == null) {
setupDataSource();
}
jdbcTemplate();
return this.driverManagerDataSource;
}
/**
* setting datasource to jdbcTemplate
*
* @return JdbcTemplate
*/
@Bean
@DependsOn("dataSource")
public JdbcTemplate jdbcTemplate() {
if (jdbcTemplate == null)
jdbcTemplate = new JdbcTemplate(this.driverManagerDataSource);
updateGlobalParamsInProperties(jdbcTemplate);
return jdbcTemplate;
}
@Override
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSource());
entityManagerFactory.setPackagesToScan(HibernatePersistenceConstant.MOSIP_PACKAGE);
entityManagerFactory.setPersistenceUnitName(HibernatePersistenceConstant.HIBERNATE);
entityManagerFactory.setJpaPropertyMap(jpaProperties());
entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter());
entityManagerFactory.setJpaDialect(jpaDialect());
return entityManagerFactory;
}
@Override
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(
Boolean.parseBoolean(keys.getProperty("hibernate.generate_ddl", HibernatePersistenceConstant.FALSE)));
vendorAdapter.setShowSql(Boolean.parseBoolean(
keys.getProperty(HibernatePersistenceConstant.HIBERNATE_SHOW_SQL, HibernatePersistenceConstant.FALSE)));
return vendorAdapter;
}
@Override
public Map jpaProperties() {
HashMap jpaProperties = new HashMap<>();
jpaProperties.put(HibernatePersistenceConstant.HIBERNATE_HBM2DDL_AUTO, keys
.getProperty(HibernatePersistenceConstant.HIBERNATE_HBM2DDL_AUTO, HibernatePersistenceConstant.UPDATE));
jpaProperties.put(HibernatePersistenceConstant.HIBERNATE_DIALECT, keys.getProperty(
HibernatePersistenceConstant.HIBERNATE_DIALECT, HibernatePersistenceConstant.MY_SQL5_DIALECT));
jpaProperties.put(HibernatePersistenceConstant.HIBERNATE_SHOW_SQL,
keys.getProperty(HibernatePersistenceConstant.HIBERNATE_SHOW_SQL, HibernatePersistenceConstant.TRUE));
jpaProperties.put(HibernatePersistenceConstant.HIBERNATE_FORMAT_SQL,
keys.getProperty(HibernatePersistenceConstant.HIBERNATE_FORMAT_SQL, HibernatePersistenceConstant.TRUE));
jpaProperties.put(HibernatePersistenceConstant.HIBERNATE_CONNECTION_CHAR_SET, keys.getProperty(
HibernatePersistenceConstant.HIBERNATE_CONNECTION_CHAR_SET, HibernatePersistenceConstant.UTF8));
jpaProperties.put(HibernatePersistenceConstant.HIBERNATE_CACHE_USE_SECOND_LEVEL_CACHE,
keys.getProperty(HibernatePersistenceConstant.HIBERNATE_CACHE_USE_SECOND_LEVEL_CACHE,
HibernatePersistenceConstant.FALSE));
jpaProperties.put(HibernatePersistenceConstant.HIBERNATE_CACHE_USE_QUERY_CACHE, keys.getProperty(
HibernatePersistenceConstant.HIBERNATE_CACHE_USE_QUERY_CACHE, HibernatePersistenceConstant.FALSE));
jpaProperties.put(HibernatePersistenceConstant.HIBERNATE_CACHE_USE_STRUCTURED_ENTRIES,
keys.getProperty(HibernatePersistenceConstant.HIBERNATE_CACHE_USE_STRUCTURED_ENTRIES,
HibernatePersistenceConstant.FALSE));
jpaProperties.put(HibernatePersistenceConstant.HIBERNATE_GENERATE_STATISTICS, keys.getProperty(
HibernatePersistenceConstant.HIBERNATE_GENERATE_STATISTICS, HibernatePersistenceConstant.FALSE));
jpaProperties.put(HibernatePersistenceConstant.HIBERNATE_NON_CONTEXTUAL_CREATION, keys.getProperty(
HibernatePersistenceConstant.HIBERNATE_NON_CONTEXTUAL_CREATION, HibernatePersistenceConstant.FALSE));
jpaProperties.put(HibernatePersistenceConstant.HIBERNATE_CURRENT_SESSION_CONTEXT, keys.getProperty(
HibernatePersistenceConstant.HIBERNATE_CURRENT_SESSION_CONTEXT, HibernatePersistenceConstant.JTA));
return jpaProperties;
}
@VisibleForTesting
private void setupDataSource() throws Exception {
LOGGER.info(LOGGER_CLASS_NAME, APPLICATION_NAME, APPLICATION_ID, "****** SETTING UP DATASOURCE *******");
System.setProperty("derby.stream.error.method", "io.mosip.registration.config.DerbySlf4jBridge.bridge");
backwardCompatibleFix();
createDatabase();
reEncryptExistingDB();
setupUserAndPermits();
Map dbConf = getDBConf();
this.driverManagerDataSource = new DriverManagerDataSource();
this.driverManagerDataSource.setDriverClassName(DRIVER_CLASS_NAME);
this.driverManagerDataSource.setSchema(SCHEMA_NAME);
this.driverManagerDataSource.setUrl(String.format(URL, dbPath, dbConf.get(BOOTPWD_KEY)));
this.driverManagerDataSource.setUsername(dbConf.get(USERNAME_KEY));
this.driverManagerDataSource.setPassword(dbConf.get(PWD_KEY));
}
private static void shutdownDatabase() {
try {
DriverManager.getConnection(SHUTDOWN_URL);
} catch (SQLException ex) {
if (((ex.getErrorCode() == 50000) && ("XJ015".equals(ex.getSQLState())))) {
LOGGER.info(LOGGER_CLASS_NAME, APPLICATION_NAME, APPLICATION_ID, "Derby DB shutdown successful.");
} else
LOGGER.error(LOGGER_CLASS_NAME, APPLICATION_NAME, APPLICATION_ID, ExceptionUtils.getStackTrace(ex));
}
}
/**
* check if db/reg doesnot exists && db.conf doesnot exists if true -> creates
* db -> create DB secret -> runs initial DB script -> shutdown database
*/
private void createDatabase() throws Exception {
LOGGER.debug(LOGGER_CLASS_NAME, APPLICATION_NAME, APPLICATION_ID, "****** DATASOURCE dbPath : " + dbPath);
Connection connection = null;
try {
if (createDb(dbPath)) {
Map dbConf = getDBConf();
connection = DriverManager.getConnection(String
.format(URL + ";create=true;" + ENCRYPTION_URL_ATTRIBUTES, dbPath, dbConf.get(BOOTPWD_KEY)),
dbConf.get(USERNAME_KEY), dbConf.get(PWD_KEY));
SQLWarning sqlWarning = connection.getWarnings();
if (sqlWarning != null) {
LOGGER.error(LOGGER_CLASS_NAME, APPLICATION_NAME, APPLICATION_ID,
ExceptionUtils.getStackTrace(sqlWarning.getCause()));
throw new Exception(sqlWarning.getCause());// SQLWarning will not be available once connection is
// closed.
}
org.apache.derby.tools.ij.runScript(connection,
DaoConfig.class.getClassLoader().getResourceAsStream("initial.sql"), "UTF-8", System.out,
"UTF-8");
shutdownDatabase();
dbConf.put(STATE_KEY, SAFE_STATE);
saveDbConf(dbConf);
}
} finally {
if (connection != null)
connection.close();
}
}
private void reEncryptExistingDB() throws Exception {
Connection connection = null;
try {
Map dbConf = getDBConf();
if (dbConf.get(STATE_KEY).equals(ERROR_STATE)) {
shutdownDatabase(); // We need to shutdown DB before encrypting
LOGGER.info(LOGGER_CLASS_NAME, APPLICATION_NAME, APPLICATION_ID,
"IMP : (Re)Encrypting DB started ......");
connection = DriverManager.getConnection("jdbc:derby:" + dbPath + ";" + ENCRYPTION_URL_ATTRIBUTES
+ ";bootPassword=" + dbConf.get(BOOTPWD_KEY), dbConf.get(USERNAME_KEY), dbConf.get(PWD_KEY));
SQLWarning sqlWarning = connection.getWarnings();
if (sqlWarning != null) {
LOGGER.error(LOGGER_CLASS_NAME, APPLICATION_NAME, APPLICATION_ID,
ExceptionUtils.getStackTrace(sqlWarning.getCause()));
throw new Exception(sqlWarning.getCause()); // SQLWarning will not be available once connection is
// closed.
}
LOGGER.info(LOGGER_CLASS_NAME, APPLICATION_NAME, APPLICATION_ID, "IMP : (Re)Encrypting DB Done ......");
dbConf.put(STATE_KEY, SAFE_STATE);
saveDbConf(dbConf);
}
} finally {
if (connection != null)
connection.close();
}
}
private void setupUserAndPermits() throws Exception {
LOGGER.info(LOGGER_CLASS_NAME, APPLICATION_NAME, "Checking Derby Security properties", "Started ... ");
Connection connection = null;
try {
Map dbConf = getDBConf();
connection = DriverManager.getConnection(String.format(URL, dbPath, getDBConf().get(BOOTPWD_KEY)),
dbConf.get(USERNAME_KEY), dbConf.get(PWD_KEY));
if (!isUserSetupComplete(connection, dbConf)) {
try (Statement statement = connection.createStatement()) {
LOGGER.info(LOGGER_CLASS_NAME, APPLICATION_NAME, APPLICATION_ID,
"Started setting up DB user and access permits...");
// setting requireAuthentication
statement.executeUpdate(
"CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.connection.requireAuthentication', 'true')");
// Setting authentication scheme to derby
statement.executeUpdate(
"CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.authentication.provider', 'BUILTIN')");
// creating user
statement.executeUpdate(String.format("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.user.%s', '%s')",
dbConf.get(USERNAME_KEY), dbConf.get(PWD_KEY)));
// setting default connection mode to noaccess
statement.executeUpdate(
"CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.defaultConnectionMode', 'noAccess')");
// setting read-write access to only one user
statement.executeUpdate(String.format("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.fullAccessUsers', '%s')",
dbConf.get(USERNAME_KEY)));
// property ensures that database-wide properties cannot be overridden by
// system-wide properties
statement.executeUpdate(
"CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.propertiesOnly', 'true')");
// shutdown derby db, for the changes to be applied
shutdownDatabase();
} catch (Throwable t) {
LOGGER.error(LOGGER_CLASS_NAME, APPLICATION_NAME, APPLICATION_ID, ExceptionUtils.getStackTrace(t));
cleanupUserAuthAndPermits(connection, dbConf);
}
LOGGER.info(LOGGER_CLASS_NAME, APPLICATION_NAME, APPLICATION_ID, "Security setup check completed.");
}
} finally {
if (connection != null)
connection.close();
}
}
private boolean isUserSetupComplete(Connection connection, Map dbConf) {
boolean completed = false;
try (Statement statement = connection.createStatement()) {
isKeySet(statement, "derby.connection.requireAuthentication", "true");
isKeySet(statement, "derby.authentication.provider", "BUILTIN");
isKeySet(statement, "derby.user." + dbConf.get(USERNAME_KEY), dbConf.get(PWD_KEY));
isKeySet(statement, "derby.database.defaultConnectionMode", "noAccess");
isKeySet(statement, "derby.database.fullAccessUsers", dbConf.get(USERNAME_KEY));
isKeySet(statement, "derby.database.propertiesOnly", "true");
completed = true;
LOGGER.info(LOGGER_CLASS_NAME, APPLICATION_NAME, APPLICATION_ID,
"Security setup check is complete & success.");
} catch (RegBaseCheckedException | SQLException regBaseCheckedException) {
LOGGER.error(LOGGER_CLASS_NAME, APPLICATION_NAME, APPLICATION_ID,
ExceptionUtils.getStackTrace(regBaseCheckedException));
}
return completed;
}
private void cleanupUserAuthAndPermits(Connection connection, Map dbConf) {
try (Statement statement = connection.createStatement()) {
// setting requireAuthentication
statement.executeUpdate(
"CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.connection.requireAuthentication', 'false')");
// Setting authentication scheme to derby
statement.executeUpdate(
"CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.authentication.provider', null)");
// creating user
statement.executeUpdate(String.format("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.user.%s', null)",
dbConf.get(USERNAME_KEY)));
// setting default connection mode to noaccess
statement.executeUpdate(
"CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.defaultConnectionMode', 'fullAccess')");
// setting read-write access to only one user
statement.executeUpdate(
"CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.fullAccessUsers', null)");
// property ensures that database-wide properties cannot be overridden by
// system-wide properties
statement.executeUpdate(
"CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.propertiesOnly', 'false')");
} catch (SQLException sqlException) {
LOGGER.error(LOGGER_CLASS_NAME, APPLICATION_NAME, "Failed to cleanup security properties",
ExceptionUtils.getStackTrace(sqlException));
} finally {
shutdownDatabase();// shutdown derby db, for the changes to be applied
}
}
private void isKeySet(Statement statement, String key, String value) throws SQLException, RegBaseCheckedException {
ResultSet rs = statement.executeQuery(String.format("VALUES SYSCS_UTIL.SYSCS_GET_DATABASE_PROPERTY('%s')", key));
if (rs.next() && value.equalsIgnoreCase(rs.getString(1)))
return;
if(key.startsWith("derby.user") && rs.getString(1) != null)
return;
throw new RegBaseCheckedException("", (key.startsWith("derby.user") ? "derby.user.*" : key) +
" : is not set to preferred value!");
}
private boolean createDb(String dbPath) throws RegBaseCheckedException {
if (!Files.isDirectory(Paths.get(dbPath)) && isDBInitializeRequired())
return true;
if (Files.isDirectory(Paths.get(dbPath)) && !isDBInitializeRequired())
return false;
throw new RegBaseCheckedException(RegistrationExceptionConstants.APP_INVALID_STATE.getErrorCode(),
RegistrationExceptionConstants.APP_INVALID_STATE.getErrorMessage());
}
public void updateGlobalParamsInProperties(JdbcTemplate jdbcTemplate) {
if (!isPPCUpdated) {
Properties properties = new Properties();
Map globalProps = getDBProps(jdbcTemplate);
properties.putAll(keys);
properties.putAll(getLocalProps(jdbcTemplate, globalProps));
PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource("globalparams", properties);
environment.getPropertySources().addFirst(propertiesPropertySource);
applicationContext.getApplicationMap().putAll(globalProps);
isPPCUpdated = true;
}
}
/**
* Fetch all the active global param values from the DB and set it in a map
*
* @return Collection of Global param values
*/
private static Map getDBProps(JdbcTemplate jdbcTemplate) {
return jdbcTemplate.query(GLOBAL_PARAM_PROPERTIES, new ResultSetExtractor
© 2015 - 2025 Weber Informatics LLC | Privacy Policy