FINERACT-1831 - Encrypted tenant passwords
- [x] Encrypt tenant passwords - [x] Update integration tests - [x] Check application database use encrypted passwords - [x] One time migration for the existing password
This commit is contained in:
committed by
Arnold Gálovics
parent
f1c89ac914
commit
3297707817
@@ -22,10 +22,13 @@ import static org.mockito.Mockito.RETURNS_MOCKS;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import java.util.List;
|
||||
import javax.sql.DataSource;
|
||||
import liquibase.change.custom.CustomTaskChange;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseIndependentQueryService;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabasePasswordEncryptor;
|
||||
import org.apache.fineract.infrastructure.core.service.migration.ExtendedSpringLiquibaseFactory;
|
||||
import org.apache.fineract.infrastructure.core.service.migration.TenantDataSourceFactory;
|
||||
import org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseStateVerifier;
|
||||
@@ -83,8 +86,8 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
public class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public TenantDataSourceFactory tenantDataSourceFactory() {
|
||||
return new TenantDataSourceFactory(null) {
|
||||
public TenantDataSourceFactory tenantDataSourceFactory(DatabasePasswordEncryptor databasePasswordEncryptor) {
|
||||
return new TenantDataSourceFactory(null, databasePasswordEncryptor) {
|
||||
|
||||
@Override
|
||||
public DataSource create(FineractPlatformTenant tenant) {
|
||||
@@ -151,9 +154,10 @@ public class TestConfiguration {
|
||||
public TenantDatabaseUpgradeService tenantDatabaseUpgradeService(TenantDetailsService tenantDetailsService,
|
||||
HikariDataSource tenantDataSource, TenantDatabaseStateVerifier tenantDatabaseStateVerifier,
|
||||
ExtendedSpringLiquibaseFactory liquibaseFactory, TenantDataSourceFactory tenantDataSourceFactory,
|
||||
FineractProperties fineractProperties, Environment environment) {
|
||||
FineractProperties fineractProperties, Environment environment,
|
||||
List<CustomTaskChange> customTaskChangesForDependencyInjection) {
|
||||
return new TenantDatabaseUpgradeService(tenantDetailsService, tenantDataSource, fineractProperties, tenantDatabaseStateVerifier,
|
||||
liquibaseFactory, tenantDataSourceFactory, environment);
|
||||
liquibaseFactory, tenantDataSourceFactory, environment, customTaskChangesForDependencyInjection);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+27
-2
@@ -38,7 +38,9 @@ import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenantConnection;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DataSourcePerTenantServiceFactory;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabasePasswordEncryptor;
|
||||
import org.apache.fineract.infrastructure.core.service.database.HikariDataSourceFactory;
|
||||
import org.apache.fineract.infrastructure.security.utils.EncryptionUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -49,6 +51,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.springframework.security.crypto.bcrypt.BCrypt;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@@ -81,6 +84,10 @@ public class DataSourcePerTenantServiceFactoryTest {
|
||||
public static final String MASTER_DB_CONN_TEST_QUERY = "SELECT 1";
|
||||
public static final boolean MASTER_DB_AUTO_COMMIT_ENABLED = true;
|
||||
|
||||
public static final String MASTER_MASTER_PASSWORD = "fineract";
|
||||
|
||||
public static final String MASTER_ENCRYPTION = "AES/CBC/PKCS5Padding";
|
||||
|
||||
@Mock
|
||||
private FineractProperties fineractProperties;
|
||||
|
||||
@@ -102,6 +109,9 @@ public class DataSourcePerTenantServiceFactoryTest {
|
||||
@Captor
|
||||
private ArgumentCaptor<HikariConfig> hikariConfigCaptor;
|
||||
|
||||
@Mock
|
||||
private DatabasePasswordEncryptor databasePasswordEncryptor;
|
||||
|
||||
@InjectMocks
|
||||
private DataSourcePerTenantServiceFactory underTest;
|
||||
|
||||
@@ -119,15 +129,19 @@ public class DataSourcePerTenantServiceFactoryTest {
|
||||
given(tenantConnection.getSchemaServerPort()).willReturn(MASTER_DB_SERVER_PORT);
|
||||
given(tenantConnection.getSchemaName()).willReturn(MASTER_DB_SCHEMA_NAME);
|
||||
given(tenantConnection.getSchemaUsername()).willReturn(MASTER_DB_USERNAME);
|
||||
given(tenantConnection.getSchemaPassword()).willReturn(MASTER_DB_PASSWORD);
|
||||
given(tenantConnection.getSchemaPassword())
|
||||
.willReturn(EncryptionUtil.encryptToBase64(MASTER_ENCRYPTION, MASTER_MASTER_PASSWORD, MASTER_DB_PASSWORD));
|
||||
given(tenantConnection.getSchemaConnectionParameters()).willReturn(MASTER_DB_CONN_PARAMS);
|
||||
|
||||
given(tenantConnection.getReadOnlySchemaServer()).willReturn(READONLY_DB_SERVER);
|
||||
given(tenantConnection.getReadOnlySchemaServerPort()).willReturn(READONLY_DB_SERVER_PORT);
|
||||
given(tenantConnection.getReadOnlySchemaName()).willReturn(READONLY_DB_SCHEMA_NAME);
|
||||
given(tenantConnection.getReadOnlySchemaUsername()).willReturn(READONLY_DB_USERNAME);
|
||||
given(tenantConnection.getReadOnlySchemaPassword()).willReturn(READONLY_DB_PASSWORD);
|
||||
given(tenantConnection.getReadOnlySchemaPassword())
|
||||
.willReturn(EncryptionUtil.encryptToBase64(MASTER_ENCRYPTION, MASTER_MASTER_PASSWORD, READONLY_DB_PASSWORD));
|
||||
given(tenantConnection.getReadOnlySchemaConnectionParameters()).willReturn(READONLY_DB_CONN_PARAMS);
|
||||
String hashedMasterPassword = BCrypt.hashpw("master-password", BCrypt.gensalt());
|
||||
given(tenantConnection.getMasterPasswordHash()).willReturn(hashedMasterPassword);
|
||||
|
||||
given(defaultTenant.getConnection()).willReturn(tenantConnection);
|
||||
given(tenantConnection.getInitialSize()).willReturn(MASTER_DB_INITIAL_SIZE);
|
||||
@@ -141,6 +155,17 @@ public class DataSourcePerTenantServiceFactoryTest {
|
||||
given(tenantHikariConfig.isAutoCommit()).willReturn(MASTER_DB_AUTO_COMMIT_ENABLED);
|
||||
|
||||
given(hikariDataSourceFactory.create(any())).willReturn(mock(HikariDataSource.class));
|
||||
FineractProperties.FineractTenantProperties tenantPropertiesMock = mock(FineractProperties.FineractTenantProperties.class);
|
||||
given(tenantPropertiesMock.getEncryption()).willReturn(MASTER_ENCRYPTION);
|
||||
given(tenantPropertiesMock.getMasterPassword()).willReturn(MASTER_MASTER_PASSWORD);
|
||||
given(fineractProperties.getTenant()).willReturn(tenantPropertiesMock);
|
||||
|
||||
given(databasePasswordEncryptor.isMasterPasswordHashValid(any())).willReturn(true);
|
||||
given(databasePasswordEncryptor.getMasterPasswordHash()).willReturn(hashedMasterPassword);
|
||||
given(databasePasswordEncryptor.decrypt(any())).will(
|
||||
answer -> EncryptionUtil.decryptFromBase64(MASTER_ENCRYPTION, MASTER_MASTER_PASSWORD, answer.getArgument(0, String.class)));
|
||||
given(databasePasswordEncryptor.encrypt(any())).will(
|
||||
answer -> EncryptionUtil.encryptToBase64(MASTER_ENCRYPTION, MASTER_MASTER_PASSWORD, answer.getArgument(0, String.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+8
-1
@@ -29,16 +29,19 @@ import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import io.cucumber.java8.En;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.sql.DataSource;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabasePasswordEncryptor;
|
||||
import org.apache.fineract.infrastructure.core.service.migration.ExtendedSpringLiquibase;
|
||||
import org.apache.fineract.infrastructure.core.service.migration.ExtendedSpringLiquibaseFactory;
|
||||
import org.apache.fineract.infrastructure.core.service.migration.SchemaUpgradeNeededException;
|
||||
import org.apache.fineract.infrastructure.core.service.migration.TenantDataSourceFactory;
|
||||
import org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseStateVerifier;
|
||||
import org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService;
|
||||
import org.apache.fineract.infrastructure.core.service.migration.TenantPasswordEncryptionTask;
|
||||
import org.apache.fineract.infrastructure.core.service.tenant.TenantDetailsService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
@@ -65,6 +68,8 @@ public class LiquibaseStepDefinitions implements En {
|
||||
private DataSource defaultTenantDataSource;
|
||||
private Environment environment;
|
||||
|
||||
private DatabasePasswordEncryptor databasePasswordEncryptor;
|
||||
|
||||
public LiquibaseStepDefinitions() {
|
||||
Given("Liquibase is disabled with a default tenant", () -> {
|
||||
initializeLiquibase(false);
|
||||
@@ -169,6 +174,8 @@ public class LiquibaseStepDefinitions implements En {
|
||||
|
||||
defaultTenantDataSource = mock(DataSource.class);
|
||||
|
||||
TenantPasswordEncryptionTask tenantPasswordEncryptor = mock(TenantPasswordEncryptionTask.class);
|
||||
|
||||
given(databaseStateVerifier.isLiquibaseDisabled()).willReturn(!liquibaseEnabled);
|
||||
given(liquibaseFactory.create(tenantStoreDataSource, "tenant_store_db", "initial_switch")).willReturn(initialTenantStoreLiquibase);
|
||||
given(liquibaseFactory.create(tenantStoreDataSource, "tenant_store_db")).willReturn(tenantStoreLiquibase);
|
||||
@@ -180,6 +187,6 @@ public class LiquibaseStepDefinitions implements En {
|
||||
given(liquibaseFactory.create(defaultTenantDataSource, "tenant_db", "custom_changelog")).willReturn(customChangeLogLiquibase);
|
||||
|
||||
tenantDatabaseUpgradeService = new TenantDatabaseUpgradeService(tenantDetailsService, tenantStoreDataSource, fineractProperties,
|
||||
databaseStateVerifier, liquibaseFactory, tenantDataSourceFactory, environment);
|
||||
databaseStateVerifier, liquibaseFactory, tenantDataSourceFactory, environment, Arrays.asList(tenantPasswordEncryptor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ fineract.tenant.timezone=Asia/Kolkata
|
||||
fineract.tenant.identifier=default
|
||||
fineract.tenant.name=fineract_default
|
||||
fineract.tenant.description=Default Demo Tenant
|
||||
fineract.tenant.master-password=fineract
|
||||
fineract.tenant.encrytion="AES/CBC/PKCS5Padding"
|
||||
|
||||
fineract.mode.read-enabled=true
|
||||
fineract.mode.write-enabled=true
|
||||
|
||||
Reference in New Issue
Block a user