FINERACT-1724 Read only tenant password encryption migration
- [x] ReadOnly tenant password encryption migration script - [x] Documentation - [x] Encryption password generator
This commit is contained in:
committed by
Arnold Gálovics
parent
e41726edfd
commit
8418a23e4b
@@ -49,6 +49,16 @@
|
||||
|Default Demo Tenant
|
||||
|TBD
|
||||
|
||||
|fineract.tenant.master-password
|
||||
|FINERACT_DEFAULT_TENANTDB_MASTER_PASSWORD
|
||||
|fineract
|
||||
|TBD
|
||||
|
||||
|fineract.tenant.encrytion
|
||||
|FINERACT_DEFAULT_TENANTDB_ENCRYPTION
|
||||
|AES/CBC/PKCS5Padding
|
||||
|TBD
|
||||
|
||||
|spring.liquibase.enabled
|
||||
|FINERACT_LIQUIBASE_ENABLED
|
||||
|true
|
||||
|
||||
@@ -31,6 +31,25 @@ The currently supported JDBC driver and corresponding mappings can be found belo
|
||||
|
||||
The actual code can be found in the `DatabaseTypeResolver` class.
|
||||
|
||||
== Tenant database security
|
||||
|
||||
The tenant database schema password is stored in the `tenant_server_connections` table in the tenant database.
|
||||
The password and the read only schema password are encrypted using the `fineract.tenant.master-password` property.
|
||||
By default, the database property will be encrypted in the first start from a plane text.
|
||||
|
||||
When you want to generate a new encrypted password, you can use the `org.apache.fineract.infrastructure.core.service.database.DatabasePasswordEncryptor` class.
|
||||
|
||||
=== Database password encryption usage
|
||||
```
|
||||
java -cp fineract-provider.jar -Dloader.main=org.apache.fineract.infrastructure.core.service.database.DatabasePasswordEncryptor org.springframework.boot.loader.PropertiesLauncher <masterPassword> <plainPassword>
|
||||
```
|
||||
|
||||
For example:
|
||||
```
|
||||
java -cp fineract-provider-0.0.0-48f7e315.jar -Dloader.main=org.apache.fineract.infrastructure.core.service.database.DatabasePasswordEncryptor org.springframework.boot.loader.PropertiesLauncher fineract-master-password fineract-tenant-password
|
||||
The encrypted password: VLwGl7vOP/q275ZTku+PNGWnGwW4mzzNHSNaO9Pr67WT5/NZMpBr9tGYYiYsqwL1eRew2jl7O3/N1EFbLlXhSA==
|
||||
```
|
||||
|
||||
== Data-access layer
|
||||
|
||||
The data-access layer of Fineract is implemented by using JPA (Java Persistence API) with the EclipseLink provider.
|
||||
|
||||
+14
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.infrastructure.core.service.database;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
@@ -37,6 +38,19 @@ public class DatabasePasswordEncryptor implements PasswordEncryptor {
|
||||
|
||||
private final FineractProperties fineractProperties;
|
||||
|
||||
@SuppressWarnings("checkstyle:regexpsinglelinejava")
|
||||
public static void main(String[] args) {
|
||||
if (args.length < 2) {
|
||||
System.out.println(
|
||||
"Usage: java -cp fineract-provider.jar java -Dloader.main=org.apache.fineract.infrastructure.core.service.database.DatabasePasswordEncryptor org.springframework.boot.loader.PropertiesLauncher <masterPassword> <plainPassword>");
|
||||
System.exit(1);
|
||||
}
|
||||
String masterPassword = args[0];
|
||||
String plainPassword = args[1];
|
||||
String encryptedPassword = EncryptionUtil.encryptToBase64(DEFAULT_ENCRYPTION, masterPassword, plainPassword);
|
||||
System.out.println(MessageFormat.format("The encrypted password: {0}", encryptedPassword));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encrypt(String plainPassword) {
|
||||
String masterPassword = Optional.ofNullable(fineractProperties.getTenant())
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.service.migration;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Statement;
|
||||
import liquibase.change.custom.CustomTaskChange;
|
||||
import liquibase.database.Database;
|
||||
import liquibase.database.jvm.JdbcConnection;
|
||||
import liquibase.exception.CustomChangeException;
|
||||
import liquibase.exception.SetupException;
|
||||
import liquibase.exception.ValidationErrors;
|
||||
import liquibase.resource.ResourceAccessor;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabasePasswordEncryptor;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class TenantReadOnlyPasswordEncryptionTask implements CustomTaskChange, ApplicationContextAware {
|
||||
|
||||
private static DatabasePasswordEncryptor databasePasswordEncryptor;
|
||||
|
||||
@Override
|
||||
public void execute(Database database) throws CustomChangeException {
|
||||
JdbcConnection dbConn = (JdbcConnection) database.getConnection(); // autocommit is false
|
||||
try (Statement selectStatement = dbConn.createStatement(); Statement updateStatement = dbConn.createStatement()) {
|
||||
|
||||
try (ResultSet rs = selectStatement.executeQuery(
|
||||
"SELECT id, readonly_schema_password FROM tenant_server_connections WHERE readonly_schema_password IS NOT NULL")) {
|
||||
while (rs.next()) {
|
||||
String id = rs.getString("id");
|
||||
String readOnlySchemaPassword = rs.getString("readonly_schema_password");
|
||||
String encryptedPassword = TenantReadOnlyPasswordEncryptionTask.databasePasswordEncryptor
|
||||
.encrypt(readOnlySchemaPassword);
|
||||
|
||||
String updateSql = String.format(
|
||||
"update tenant_server_connections set readonly_schema_password = '%s', master_password_hash = '%s' where id = %s",
|
||||
encryptedPassword, TenantReadOnlyPasswordEncryptionTask.databasePasswordEncryptor.getMasterPasswordHash(), id);
|
||||
updateStatement.execute(updateSql);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new CustomChangeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfirmationMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws SetupException {
|
||||
// Not required
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFileOpener(ResourceAccessor resourceAccessor) {
|
||||
// Not required
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationErrors validate(Database database) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("static-access")
|
||||
@SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
TenantReadOnlyPasswordEncryptionTask.databasePasswordEncryptor = applicationContext.getBean(DatabasePasswordEncryptor.class);
|
||||
}
|
||||
}
|
||||
+1
@@ -27,4 +27,5 @@
|
||||
<include file="parts/0005_jdbc_connection_string.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0006_drop_retry_parameter_columns.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0007_encrypt_existing_tenant_passwords.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0008_encrypt_existing_ro_tenant_passwords.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet author="fineract" id="1" context="tenant_store_db">
|
||||
<customChange class="org.apache.fineract.infrastructure.core.service.migration.TenantReadOnlyPasswordEncryptionTask"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user