You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by vo...@apache.org on 2021/12/12 08:37:49 UTC
[fineract] branch develop updated: Integration tests for twofactor
This is an automated email from the ASF dual-hosted git repository.
vorburger 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 5dbde0f Integration tests for twofactor
5dbde0f is described below
commit 5dbde0f2ede60f45a39d5321b2faf541bf75aacc
Author: Petri Tuomola <pe...@tuomola.org>
AuthorDate: Sat Dec 4 18:05:05 2021 +0800
Integration tests for twofactor
---
.../workflows/{build.yml => build-twofactor.yml} | 4 +-
.github/workflows/build.yml | 4 +-
build.gradle | 2 +
...lServicesPropertiesReadPlatformServiceImpl.java | 2 +-
.../infrastructure/core/service/DateUtils.java | 3 +-
.../security/domain/TFAccessToken.java | 2 +-
.../V265__modify_external_service_schema.sql | 4 +-
settings.gradle | 1 +
twofactor-tests/build.gradle | 67 +++++
.../dependencies.gradle | 24 +-
.../TwoFactorAuthenticationTest.java | 304 +++++++++++++++++++++
11 files changed, 403 insertions(+), 14 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build-twofactor.yml
similarity index 91%
copy from .github/workflows/build.yml
copy to .github/workflows/build-twofactor.yml
index cfcc71c..c0e054d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build-twofactor.yml
@@ -1,4 +1,4 @@
-name: Fineract Gradle build
+name: Fineract Gradle build - twofactor
on: [push, pull_request]
jobs:
@@ -39,4 +39,4 @@ jobs:
sudo apt-get update
sudo apt-get install ghostscript -y
- name: Build & Test
- run: ./gradlew --no-daemon -q --console=plain licenseMain licenseTest check build test --fail-fast doc
+ run: ./gradlew --no-daemon -q --console=plain licenseMain licenseTest check build test --fail-fast doc -x :integration-tests:test -Ptwofactor=enabled
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index cfcc71c..fe53337 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,4 +1,4 @@
-name: Fineract Gradle build
+name: Fineract Gradle build - basicauth
on: [push, pull_request]
jobs:
@@ -39,4 +39,4 @@ jobs:
sudo apt-get update
sudo apt-get install ghostscript -y
- name: Build & Test
- run: ./gradlew --no-daemon -q --console=plain licenseMain licenseTest check build test --fail-fast doc
+ run: ./gradlew --no-daemon -q --console=plain licenseMain licenseTest check build test --fail-fast doc -x :twofactor-tests:test
diff --git a/build.gradle b/build.gradle
index 37013f1..0de0c8b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -28,6 +28,7 @@ buildscript {
'fineract-api',
'fineract-provider',
'integration-tests',
+ 'twofactor-tests',
'fineract-client'
].contains(it.name)
}
@@ -144,6 +145,7 @@ allprojects {
dependency 'org.webjars.npm:swagger-ui-dist:4.0.1'
dependency 'org.webjars:webjars-locator-core:0.48'
dependency 'org.springframework.boot:spring-boot-starter-mail:2.6.0'
+ dependency 'com.icegreen:greenmail-junit5:1.6.5'
// fineract client dependencies
dependency "com.squareup.retrofit2:retrofit:$retrofitVersion"
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformServiceImpl.java
index 8bde327..c66cda9 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformServiceImpl.java
@@ -83,7 +83,7 @@ public class ExternalServicesPropertiesReadPlatformServiceImpl implements Extern
if (ExternalServicesConstants.SMTP_USERNAME.equalsIgnoreCase(name)) {
username = value;
- } else if (ExternalServicesConstants.SMTP_PORT.equalsIgnoreCase(port)) {
+ } else if (ExternalServicesConstants.SMTP_PORT.equalsIgnoreCase(name)) {
port = value;
} else if (ExternalServicesConstants.SMTP_PASSWORD.equalsIgnoreCase(name)) {
password = value;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
index 53c6fad..bd08a6c 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
@@ -25,6 +25,7 @@ import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -69,7 +70,7 @@ public final class DateUtils {
public static LocalDateTime getLocalDateTimeOfTenant() {
final ZoneId zone = getDateTimeZoneOfTenant();
- LocalDateTime today = LocalDateTime.now(zone);
+ LocalDateTime today = LocalDateTime.now(zone).truncatedTo(ChronoUnit.SECONDS);
return today;
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/domain/TFAccessToken.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/domain/TFAccessToken.java
index c9165e7..5593475 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/domain/TFAccessToken.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/domain/TFAccessToken.java
@@ -128,6 +128,6 @@ public class TFAccessToken extends AbstractPersistableCustom {
}
private boolean isDateInThePast(LocalDateTime dateTime) {
- return dateTime.isBefore(DateUtils.getLocalDateTimeOfTenant());
+ return (dateTime.isBefore(DateUtils.getLocalDateTimeOfTenant()) || dateTime.isEqual(DateUtils.getLocalDateTimeOfTenant()));
}
}
diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V265__modify_external_service_schema.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V265__modify_external_service_schema.sql
index b4e8d2c..b83799c 100644
--- a/fineract-provider/src/main/resources/sql/migrations/core_db/V265__modify_external_service_schema.sql
+++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V265__modify_external_service_schema.sql
@@ -53,9 +53,9 @@ insert into c_external_service_properties (`name`, `value`, `external_service_id
insert into c_external_service_properties (`name`, `value`, `external_service_id`) values('password', 'support80', (select id from c_external_service where name = 'SMTP_Email_Account'));
-insert into c_external_service_properties (`name`, `value`, `external_service_id`) values('host', 'smtp.gmail.com', (select id from c_external_service where name = 'SMTP_Email_Account'));
+insert into c_external_service_properties (`name`, `value`, `external_service_id`) values('host', 'localhost', (select id from c_external_service where name = 'SMTP_Email_Account'));
-insert into c_external_service_properties (`name`, `value`, `external_service_id`) values('port', '587', (select id from c_external_service where name = 'SMTP_Email_Account'));
+insert into c_external_service_properties (`name`, `value`, `external_service_id`) values('port', '3025', (select id from c_external_service where name = 'SMTP_Email_Account'));
insert into c_external_service_properties (`name`, `value`, `external_service_id`) values('useTLS', 'true', (select id from c_external_service where name = 'SMTP_Email_Account'));
diff --git a/settings.gradle b/settings.gradle
index fcb6dc0..596dc4e 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -19,5 +19,6 @@
rootProject.name='fineract'
include ':fineract-provider'
include ':integration-tests'
+include ':twofactor-tests'
include ':fineract-client'
include ':fineract-doc'
diff --git a/twofactor-tests/build.gradle b/twofactor-tests/build.gradle
new file mode 100644
index 0000000..bddfd3d
--- /dev/null
+++ b/twofactor-tests/build.gradle
@@ -0,0 +1,67 @@
+/**
+ * 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.
+ */
+description = 'Fineract Integration Tests'
+
+apply plugin: 'com.bmuschko.cargo'
+
+// Configuration for the Gradle Cargo plugin
+// https://github.com/bmuschko/gradle-cargo-plugin
+configurations {
+ tomcat
+}
+
+apply from: 'dependencies.gradle'
+
+cargo {
+ containerId "tomcat9x"
+
+ // looks like Cargo doesn't detect the WAR file automatically in the multi-module setup
+ deployable {
+ file = file("$rootDir/fineract-provider/build/libs/fineract-provider.war")
+ context = 'fineract-provider'
+ }
+
+ local {
+ installer {
+ installConfiguration = configurations.tomcat
+ downloadDir = file("$buildDir/download")
+ extractDir = file("$buildDir/tomcat")
+ }
+ startStopTimeout = 240000
+ containerProperties {
+ property 'cargo.start.jvmargs', '-Dspring.profiles.active=twofactor,basicauth'
+ property 'cargo.tomcat.connector.keystoreFile', file("$rootDir/fineract-provider/src/main/resources/keystore.jks")
+ property 'cargo.tomcat.connector.keystorePass', 'openmf'
+ property 'cargo.tomcat.httpSecure', true
+ property 'cargo.tomcat.connector.sslProtocol', 'TLS'
+ property 'cargo.tomcat.connector.clientAuth', false
+ property 'cargo.protocol', 'https'
+ property 'cargo.servlet.port', 8443
+ }
+ }
+}
+
+cargoRunLocal.dependsOn ':fineract-provider:war'
+cargoStartLocal.dependsOn ':fineract-provider:war'
+cargoStartLocal.mustRunAfter 'testClasses'
+
+test {
+ dependsOn cargoStartLocal
+ finalizedBy cargoStopLocal
+}
diff --git a/settings.gradle b/twofactor-tests/dependencies.gradle
similarity index 50%
copy from settings.gradle
copy to twofactor-tests/dependencies.gradle
index fcb6dc0..d99bb86 100644
--- a/settings.gradle
+++ b/twofactor-tests/dependencies.gradle
@@ -16,8 +16,22 @@
* specific language governing permissions and limitations
* under the License.
*/
-rootProject.name='fineract'
-include ':fineract-provider'
-include ':integration-tests'
-include ':fineract-client'
-include ':fineract-doc'
+dependencies {
+ // testCompile dependencies are ONLY used in src/test, not src/main.
+ // Do NOT repeat dependencies which are ALREADY in implementation or runtimeOnly!
+ //
+ tomcat 'org.apache.tomcat:tomcat:9.0.54@zip'
+ testImplementation( files("$rootDir/fineract-provider/build/classes/java/main/"),
+ project(path: ':fineract-provider', configuration: 'runtimeElements'),
+ 'org.junit.jupiter:junit-jupiter-api',
+ 'com.icegreen:greenmail-junit5'
+ )
+ testImplementation ('io.rest-assured:rest-assured') {
+ exclude group: 'commons-logging'
+ exclude group: 'org.apache.sling'
+ exclude group: 'com.sun.xml.bind'
+ }
+ testRuntimeOnly(
+ 'org.junit.jupiter:junit-jupiter-engine'
+ )
+}
diff --git a/twofactor-tests/src/test/java/org/apache/fineract/twofactortests/TwoFactorAuthenticationTest.java b/twofactor-tests/src/test/java/org/apache/fineract/twofactortests/TwoFactorAuthenticationTest.java
new file mode 100644
index 0000000..e3703b4
--- /dev/null
+++ b/twofactor-tests/src/test/java/org/apache/fineract/twofactortests/TwoFactorAuthenticationTest.java
@@ -0,0 +1,304 @@
+/**
+ * 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.twofactortests;
+
+import static io.restassured.RestAssured.given;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import com.icegreen.greenmail.configuration.GreenMailConfiguration;
+import com.icegreen.greenmail.junit5.GreenMailExtension;
+import com.icegreen.greenmail.util.ServerSetupTest;
+import io.restassured.RestAssured;
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.path.json.JsonPath;
+import io.restassured.response.Response;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.mail.MessagingException;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+public class TwoFactorAuthenticationTest {
+
+ private ResponseSpecification responseSpec;
+ private ResponseSpecification responseSpec403;
+ private ResponseSpecification responseSpec401;
+ private RequestSpecification requestSpecWithoutBasic;
+ private RequestSpecification requestSpec;
+ private String basicAuthenticationKey;
+
+ public static final String TENANT_PARAM_NAME = "tenantIdentifier";
+ public static final String DEFAULT_TENANT = "default";
+ public static final String TENANT_IDENTIFIER = TENANT_PARAM_NAME + '=' + DEFAULT_TENANT;
+ private static final String LOGIN_URL = "/fineract-provider/api/v1/authentication?" + TENANT_IDENTIFIER;
+ private static final String HEALTH_URL = "/fineract-provider/actuator/health";
+
+ @RegisterExtension
+ static GreenMailExtension greenMail = new GreenMailExtension(ServerSetupTest.SMTP)
+ .withConfiguration(GreenMailConfiguration.aConfig().withUser("support@cloudmicrofinance.com", "support81"))
+ .withPerMethodLifecycle(true);
+
+ @BeforeEach
+ public void setup() throws InterruptedException {
+ initializeRestAssured();
+
+ this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+
+ // Login with basic authentication
+ awaitSpringBootActuatorHealthyUp();
+ String json = RestAssured.given().contentType(ContentType.JSON).body("{\"username\":\"mifos\", \"password\":\"password\"}").expect()
+ .log().ifError().when().post(LOGIN_URL).asString();
+ assertFalse(StringUtils.isBlank(json));
+
+ this.basicAuthenticationKey = JsonPath.with(json).get("base64EncodedAuthenticationKey");
+ assertFalse(StringUtils.isBlank(this.basicAuthenticationKey));
+
+ this.requestSpec.header("Authorization", "Basic " + basicAuthenticationKey);
+
+ this.requestSpecWithoutBasic = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ this.responseSpec403 = new ResponseSpecBuilder().expectStatusCode(403).build();
+ this.responseSpec401 = new ResponseSpecBuilder().expectStatusCode(401).build();
+ }
+
+ @Test
+ public void testActuatorAccess() {
+ performServerGet(requestSpecWithoutBasic, responseSpec, "/fineract-provider/actuator/info", null);
+ }
+
+ @Test
+ public void testApiDocsAccess() {
+ performServerGet(requestSpecWithoutBasic, responseSpec, "/fineract-provider/api-docs/apiLive.htm", null);
+ }
+
+ @Test
+ public void testAccessWithoutTwofactor() {
+ performServerGet(requestSpec, responseSpec403, "/fineract-provider/api/v1/offices/1?" + TENANT_IDENTIFIER, "");
+ }
+
+ @Test
+ public void testCheckTwofactorEnabled() {
+ String json = RestAssured.given().contentType(ContentType.JSON).body("{\"username\":\"mifos\", \"password\":\"password\"}").expect()
+ .log().ifError().when().post("/fineract-provider/api/v1/authentication?" + TENANT_IDENTIFIER).asString();
+ assertFalse(StringUtils.isBlank(json));
+ Boolean key = JsonPath.with(json).get("isTwoFactorAuthenticationRequired");
+ assertEquals(key, true);
+ }
+
+ @Test
+ public void testGetTwofactorMethods() {
+ String json = RestAssured.given().spec(requestSpec).expect().log().ifError().when()
+ .get("/fineract-provider/api/v1/twofactor?" + TENANT_IDENTIFIER).asString();
+ assertFalse(StringUtils.isBlank(json));
+ List<HashMap<String, Object>> twoFactorMethods = JsonPath.with(json).getList("$");
+ assertEquals("email", twoFactorMethods.get(0).get("name"));
+ assertEquals("demomfi@mifos.org", twoFactorMethods.get(0).get("target"));
+ }
+
+ @Test
+ public void testTwofactorLogin() throws IOException, MessagingException {
+ assertEquals(greenMail.getReceivedMessages().length, 0);
+ performServerPost(requestSpec, responseSpec,
+ "/fineract-provider/api/v1/twofactor?deliveryMethod=email&extendedToken=false&" + TENANT_IDENTIFIER, "", "");
+ assertEquals(greenMail.getReceivedMessages().length, 1);
+
+ Pattern p = Pattern.compile("token is (.+).");
+ Matcher m = p.matcher((CharSequence) greenMail.getReceivedMessages()[0].getContent());
+
+ String token = null;
+
+ while (m.find()) {
+ token = m.group(1);
+ }
+
+ assertNotNull(token);
+ String tfaToken = performServerPost(requestSpec, responseSpec,
+ "/fineract-provider/api/v1/twofactor/validate?token=" + token + "&" + TENANT_IDENTIFIER, "", "token");
+ assertNotNull(tfaToken);
+
+ RequestSpecification requestSpecWithTFA = new RequestSpecBuilder() //
+ .setContentType(ContentType.JSON) //
+ .addHeader("Fineract-Platform-TFA-Token", tfaToken) //
+ .addHeader("Authorization", "Basic " + basicAuthenticationKey) //
+ .build();
+
+ performServerGet(requestSpecWithTFA, responseSpec, "/fineract-provider/api/v1/offices/1?" + TENANT_IDENTIFIER, "");
+
+ performServerPost(requestSpecWithTFA, responseSpec, "/fineract-provider/api/v1/twofactor/invalidate?" + TENANT_IDENTIFIER,
+ "{ \"token\": \"" + tfaToken + "\" }", "");
+
+ performServerGet(requestSpecWithTFA, responseSpec401, "/fineract-provider/api/v1/offices/1?" + TENANT_IDENTIFIER, "");
+ }
+
+ @Test
+ public void testTfaConfigSettings() throws IOException, MessagingException {
+ assertEquals(greenMail.getReceivedMessages().length, 0);
+ performServerPost(requestSpec, responseSpec,
+ "/fineract-provider/api/v1/twofactor?deliveryMethod=email&extendedToken=false&" + TENANT_IDENTIFIER, "", "");
+ assertEquals(greenMail.getReceivedMessages().length, 1);
+
+ Pattern p = Pattern.compile("token is (.+).");
+ Matcher m = p.matcher((CharSequence) greenMail.getReceivedMessages()[0].getContent());
+
+ String token = null;
+
+ while (m.find()) {
+ token = m.group(1);
+ }
+
+ assertNotNull(token);
+ String tfaToken = performServerPost(requestSpec, responseSpec,
+ "/fineract-provider/api/v1/twofactor/validate?token=" + token + "&" + TENANT_IDENTIFIER, "", "token");
+ assertNotNull(tfaToken);
+
+ RequestSpecification requestSpecWithTFA = new RequestSpecBuilder() //
+ .setContentType(ContentType.JSON) //
+ .addHeader("Fineract-Platform-TFA-Token", tfaToken) //
+ .addHeader("Authorization", "Basic " + basicAuthenticationKey) //
+ .build();
+
+ // Get the configuration and check one of the values (OTP token length)
+ LinkedHashMap<String, Object> json = performServerGet(requestSpecWithTFA, responseSpec,
+ "/fineract-provider/api/v1/twofactor/configure?" + TENANT_IDENTIFIER, "");
+ assertEquals(json.get("otp-token-length"), token.length());
+
+ // Update OTP token length
+ performServerPut(requestSpecWithTFA, responseSpec, "/fineract-provider/api/v1/twofactor/configure?" + TENANT_IDENTIFIER,
+ "{\"otp-token-length\": 10 }", "");
+
+ // Invalidate token for re-login
+ performServerPost(requestSpecWithTFA, responseSpec, "/fineract-provider/api/v1/twofactor/invalidate?" + TENANT_IDENTIFIER,
+ "{ \"token\": \"" + tfaToken + "\" }", "");
+
+ // Login again
+ performServerPost(requestSpec, responseSpec,
+ "/fineract-provider/api/v1/twofactor?deliveryMethod=email&extendedToken=false&" + TENANT_IDENTIFIER, "", "");
+ assertEquals(greenMail.getReceivedMessages().length, 2);
+
+ Matcher m2 = p.matcher((CharSequence) greenMail.getReceivedMessages()[1].getContent());
+
+ String token2 = null;
+
+ while (m2.find()) {
+ token2 = m2.group(1);
+ }
+
+ assertNotNull(token2);
+
+ // Check that the configuration has worked and length is now 10
+ assertEquals(10, token2.length());
+
+ tfaToken = performServerPost(requestSpec, responseSpec,
+ "/fineract-provider/api/v1/twofactor/validate?token=" + token2 + "&" + TENANT_IDENTIFIER, "", "token");
+ assertNotNull(tfaToken);
+
+ requestSpecWithTFA = new RequestSpecBuilder() //
+ .setContentType(ContentType.JSON) //
+ .addHeader("Fineract-Platform-TFA-Token", tfaToken) //
+ .addHeader("Authorization", "Basic " + basicAuthenticationKey) //
+ .build();
+
+ // Get the configuration and check one of the values (OTP token length)
+ json = performServerGet(requestSpecWithTFA, responseSpec, "/fineract-provider/api/v1/twofactor/configure?" + TENANT_IDENTIFIER, "");
+ assertEquals(json.get("otp-token-length"), token2.length());
+
+ // Update OTP token length back to original value
+ performServerPut(requestSpecWithTFA, responseSpec, "/fineract-provider/api/v1/twofactor/configure?" + TENANT_IDENTIFIER,
+ "{\"otp-token-length\": " + token.length() + "}", "");
+
+ // Check that configuration has been reset
+ json = performServerGet(requestSpecWithTFA, responseSpec, "/fineract-provider/api/v1/twofactor/configure?" + TENANT_IDENTIFIER, "");
+ assertEquals(json.get("otp-token-length"), token.length());
+
+ // Invalidate token for re-login
+ performServerPost(requestSpecWithTFA, responseSpec, "/fineract-provider/api/v1/twofactor/invalidate?" + TENANT_IDENTIFIER,
+ "{ \"token\": \"" + tfaToken + "\" }", "");
+ }
+
+ private static void initializeRestAssured() {
+ RestAssured.baseURI = "https://localhost";
+ RestAssured.port = 8443;
+ RestAssured.keyStore("src/main/resources/keystore.jks", "openmf");
+ RestAssured.useRelaxedHTTPSValidation();
+ }
+
+ private static void awaitSpringBootActuatorHealthyUp() throws InterruptedException {
+ int attempt = 0;
+ final int max_attempts = 10;
+ Response response = null;
+
+ do {
+ try {
+ response = RestAssured.get(HEALTH_URL);
+
+ if (response.statusCode() == 200) {
+ return;
+ }
+
+ Thread.sleep(3000);
+ } catch (Exception e) {
+ Thread.sleep(3000);
+ }
+ } while (attempt < max_attempts);
+
+ fail(HEALTH_URL + " returned " + response.prettyPrint());
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> T performServerGet(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
+ final String getURL, final String jsonAttributeToGetBack) {
+ final String json = given().spec(requestSpec).expect().spec(responseSpec).log().ifError().when().get(getURL).andReturn().asString();
+ if (jsonAttributeToGetBack == null) {
+ return (T) json;
+ }
+ return (T) JsonPath.from(json).get(jsonAttributeToGetBack);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> T performServerPost(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
+ final String postURL, final String jsonBodyToSend, final String jsonAttributeToGetBack) {
+ final String json = given().spec(requestSpec).body(jsonBodyToSend).expect().spec(responseSpec).log().ifError().when().post(postURL)
+ .andReturn().asString();
+ if (jsonAttributeToGetBack == null) {
+ return (T) json;
+ }
+ return (T) JsonPath.from(json).get(jsonAttributeToGetBack);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> T performServerPut(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
+ final String putURL, final String jsonBodyToSend, final String jsonAttributeToGetBack) {
+ final String json = given().spec(requestSpec).body(jsonBodyToSend).expect().spec(responseSpec).log().ifError().when().put(putURL)
+ .andReturn().asString();
+ return (T) JsonPath.from(json).get(jsonAttributeToGetBack);
+ }
+}