This is an automated email from the ASF dual-hosted git repository.
arnold pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new 8418a23e4 FINERACT-1724 Read only tenant password encryption migration
- [x] ReadOnly tenant password encryption migration script - [x] Documentation
- [x] Encryption password generator
8418a23e4 is described below
commit 8418a23e4b246c276ccadafa3196761234454a3e
Author: Janos Haber <[email protected]>
AuthorDate: Thu Mar 16 16:25:03 2023 +0100
FINERACT-1724 Read only tenant password encryption migration
- [x] ReadOnly tenant password encryption migration script
- [x] Documentation
- [x] Encryption password generator
---
.../en/chapters/appendix/properties-database.adoc | 10 +++
.../docs/en/chapters/architecture/persistence.adoc | 19 +++++
.../database/DatabasePasswordEncryptor.java | 14 ++++
.../TenantReadOnlyPasswordEncryptionTask.java | 96 ++++++++++++++++++++++
.../tenant-store/changelog-tenant-store.xml | 1 +
.../0008_encrypt_existing_ro_tenant_passwords.xml} | 8 +-
6 files changed, 143 insertions(+), 5 deletions(-)
diff --git
a/fineract-doc/src/docs/en/chapters/appendix/properties-database.adoc
b/fineract-doc/src/docs/en/chapters/appendix/properties-database.adoc
index 834bec705..988a9c38f 100644
--- a/fineract-doc/src/docs/en/chapters/appendix/properties-database.adoc
+++ b/fineract-doc/src/docs/en/chapters/appendix/properties-database.adoc
@@ -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
diff --git a/fineract-doc/src/docs/en/chapters/architecture/persistence.adoc
b/fineract-doc/src/docs/en/chapters/architecture/persistence.adoc
index df5f4558c..94ef531a3 100644
--- a/fineract-doc/src/docs/en/chapters/architecture/persistence.adoc
+++ b/fineract-doc/src/docs/en/chapters/architecture/persistence.adoc
@@ -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.
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabasePasswordEncryptor.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabasePasswordEncryptor.java
index 033a13669..7dec6c30b 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabasePasswordEncryptor.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabasePasswordEncryptor.java
@@ -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())
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/TenantReadOnlyPasswordEncryptionTask.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/TenantReadOnlyPasswordEncryptionTask.java
new file mode 100644
index 000000000..e019ae163
--- /dev/null
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/TenantReadOnlyPasswordEncryptionTask.java
@@ -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);
+ }
+}
diff --git
a/fineract-provider/src/main/resources/db/changelog/tenant-store/changelog-tenant-store.xml
b/fineract-provider/src/main/resources/db/changelog/tenant-store/changelog-tenant-store.xml
index 3cc73b305..42c4aba73 100644
---
a/fineract-provider/src/main/resources/db/changelog/tenant-store/changelog-tenant-store.xml
+++
b/fineract-provider/src/main/resources/db/changelog/tenant-store/changelog-tenant-store.xml
@@ -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>
diff --git
a/fineract-provider/src/main/resources/db/changelog/tenant-store/changelog-tenant-store.xml
b/fineract-provider/src/main/resources/db/changelog/tenant-store/parts/0008_encrypt_existing_ro_tenant_passwords.xml
similarity index 70%
copy from
fineract-provider/src/main/resources/db/changelog/tenant-store/changelog-tenant-store.xml
copy to
fineract-provider/src/main/resources/db/changelog/tenant-store/parts/0008_encrypt_existing_ro_tenant_passwords.xml
index 3cc73b305..6d138f104 100644
---
a/fineract-provider/src/main/resources/db/changelog/tenant-store/changelog-tenant-store.xml
+++
b/fineract-provider/src/main/resources/db/changelog/tenant-store/parts/0008_encrypt_existing_ro_tenant_passwords.xml
@@ -22,9 +22,7 @@
<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">
- <include file="parts/0003_reset_postgresql_sequences.xml"
relativeToChangelogFile="true"/>
- <include file="parts/0004_readonly_database_connection.xml"
relativeToChangelogFile="true"/>
- <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"/>
+ <changeSet author="fineract" id="1" context="tenant_store_db">
+ <customChange
class="org.apache.fineract.infrastructure.core.service.migration.TenantReadOnlyPasswordEncryptionTask"/>
+ </changeSet>
</databaseChangeLog>