From 28b321b5f94c64238afe3daf7ef7cf3b5329b3e1 Mon Sep 17 00:00:00 2001 From: Roman Strobl Date: Mon, 14 Mar 2022 17:55:38 +0100 Subject: [PATCH 1/6] First version of test server with implementation of code-based activation --- powerauth-test-server/README.md | 75 +++++++++ .../docs/sql/postgresql/create-schema.sql | 22 +++ powerauth-test-server/pom.xml | 159 ++++++++++++++++++ .../app/testserver/ServletInitializer.java | 39 +++++ .../app/testserver/TestServerApplication.java | 32 ++++ .../config/TestServerConfiguration.java | 42 +++++ .../controller/ActivationController.java | 69 ++++++++ .../database/TestConfigRepository.java | 31 ++++ .../database/TestStatusRepository.java | 31 ++++ .../database/entity/TestConfigEntity.java | 76 +++++++++ .../database/entity/TestStatusEntity.java | 91 ++++++++++ .../AppConfigNotFoundException.java | 44 +++++ .../DefaultExceptionHandler.java | 88 ++++++++++ .../GenericCryptographyException.java | 44 +++++ .../RemoteExecutionException.java | 44 +++++ .../request/CreateActivationRequest.java | 36 ++++ .../response/CreateActivationResponse.java | 33 ++++ .../testserver/service/ActivationService.java | 151 +++++++++++++++++ .../app/testserver/util/ResultStatusUtil.java | 89 ++++++++++ .../src/main/resources/application.properties | 19 +++ 20 files changed, 1215 insertions(+) create mode 100644 powerauth-test-server/README.md create mode 100644 powerauth-test-server/docs/sql/postgresql/create-schema.sql create mode 100644 powerauth-test-server/pom.xml create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/ServletInitializer.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/TestServerApplication.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/config/TestServerConfiguration.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/controller/ActivationController.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/TestConfigRepository.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/TestStatusRepository.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/entity/TestConfigEntity.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/entity/TestStatusEntity.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/AppConfigNotFoundException.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/DefaultExceptionHandler.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/GenericCryptographyException.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/RemoteExecutionException.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/model/request/CreateActivationRequest.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/model/response/CreateActivationResponse.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ActivationService.java create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/util/ResultStatusUtil.java create mode 100644 powerauth-test-server/src/main/resources/application.properties diff --git a/powerauth-test-server/README.md b/powerauth-test-server/README.md new file mode 100644 index 00000000..3f81a2e5 --- /dev/null +++ b/powerauth-test-server/README.md @@ -0,0 +1,75 @@ +# PowerAuth Test Server + +PowerAuth TestServer is deployed to simplify testing of PowerAuth backends. The REST API encapsulates PowerAuth actions which require cryptography with an embedded `powerauth-java-cmd-lib` library. + +## Test Server Configuration + +Test server runs by default with embedded H2 database. The database structure is created automatically on application startup. + +Once the database is created, you can connect to it using following URL: + +```properties +spring.datasource.url=jdbc:h2:file:~/powerauth-test;DB_CLOSE_ON_EXIT=FALSE;AUTO_SERVER=TRUE +``` + +The test server configuration is performed using following query: + +```sql +insert into PA_TEST_CONFIG (APPLICATION_ID, APPLICATION_NAME, APPLICATION_KEY, APPLICATION_SECRET, MASTER_PUBLIC_KEY) +values (1, 'test-app', '66arXznJzaEs1k4cNfyWzA==', 'CNtWEvyDyJupKL9n07y+aA==', 'BLWJ8cTWx/LxU8dTC7CiNbWKXExRSG/yMKmR3Iw5ZhlPpMQ9qTvBWhY0DnkFr++53JPEwfJaW6zEdIEdq34z59E='); +``` + +The `APPLICATION_ID` value should correspond to the PowerAuth application identifier of the application used for testing. +You can obtain all the other values from PowerAuth Admin application. + +## Create Activation + +The activation needs to be at first initialized using one of the possible ways: +- creating the activation in PowerAuth Admin in the `Activations` tab +- calling the PowerAuth server POST `/rest/v3/activation/init` endpoint +- calling the PowerAuth cloud POST `/registration` or POST `/v2/registrations` endpoint + +Once the activation is initialized, you can create the activation using following REST API call. + +```shell +curl --request POST \ +--url http://localhost:8080/powerauth-test-server/activation/create \ +--header 'Content-Type: application/json' \ +--data '{ + "requestObject": { + "applicationId": "1", + "activationName": "test-activation", + "password": "1234", + "activationCode": "3A33O-3XMFZ-ORDKE-XJOYQ" + } +}' +``` + +The following request parameters are used: + +| Parameter | Note | +|---|---| +| `applicationId` | PowerAuth application identifier | +| `activationName` | PowerAuth application name | +| `password` | PIN code for future signature verifications (knowledge factor) | +| `activationCode` | Activation code, created using the previous initialization request | + +The response contains the `activationId` parameter which is the activation identifier: + +```json +{ + "status": "OK", + "responseObject": { + "activationId": "5df48d17-e477-467b-8b93-2d6a0185b642" + } +} +``` + +In order for the activation to become `ACTIVE`, the activation needs to be committed, unless auto-commit mode is enabled using one of the possible ways: +- committing the activation in PowerAuth Admin in the `Activations` tab +- calling the PowerAuth server POST `/rest/v3/activation/commit` endpoint +- calling the PowerAuth cloud POST `/registration` or POST `/v2/registrations` endpoint + +# License + +PowerAuth Test Server is licensed using GNU AGPLv3 license. Please consult us at hello@wultra.com for the software use. diff --git a/powerauth-test-server/docs/sql/postgresql/create-schema.sql b/powerauth-test-server/docs/sql/postgresql/create-schema.sql new file mode 100644 index 00000000..2c53600f --- /dev/null +++ b/powerauth-test-server/docs/sql/postgresql/create-schema.sql @@ -0,0 +1,22 @@ +CREATE TABLE pa_test_config +( + application_id INTEGER NOT NULL PRIMARY KEY, -- Application identifier + application_name VARCHAR(255) NOT NULL, -- Application name + application_key VARCHAR(255) NOT NULL, -- Application key + application_secret VARCHAR(255) NOT NULL, -- Application secret + master_public_key VARCHAR(255) NOT NULL -- Master public key in Base64 format +); + +CREATE TABLE pa_test_status +( + activation_id VARCHAR(255) NOT NULL PRIMARY KEY, -- Activation identifier + server_public_key VARCHAR(255) NOT NULL, -- Server public key in Base64 format + counter INTEGER NOT NULL, -- Numeric counter + ctr_data VARCHAR(255) NOT NULL, -- Hashed counter data + encrypted_device_private_key VARCHAR(255) NOT NULL, -- Encrypted device private key in Base64 format + signature_biometry_key VARCHAR(255) NOT NULL, -- Signature biometry key in Base64 format + signature_knowledge_key_encrypted VARCHAR(255) NOT NULL, -- Encrypted signature knowledge key in Base64 format + signature_knowledge_key_salt VARCHAR(255) NOT NULL, -- Signature knowledge key in Base64 format + signature_possession_key VARCHAR(255) NOT NULL, -- Signature possession key in Base64 format + transport_master_key VARCHAR(255) NOT NULL -- Transport master key in Base64 format +); diff --git a/powerauth-test-server/pom.xml b/powerauth-test-server/pom.xml new file mode 100644 index 00000000..53cb0c67 --- /dev/null +++ b/powerauth-test-server/pom.xml @@ -0,0 +1,159 @@ + + + + 4.0.0 + + powerauth-test-server + io.getlime.security + 1.3.0-SNAPSHOT + war + + powerauth-test-server + Test server which exposes REST API for easier testing of PowerAuth backends + + + org.springframework.boot + spring-boot-starter-parent + 2.6.4 + + + + 2022 + http://powerauth.com/ + + + Wultra s.r.o. + http://wultra.com + + + + + GNU Affero General Public License v3.0 + https://www.gnu.org/licenses/agpl-3.0.en.html + + + + + + Roman Strobl + roman.strobl@wultra.com + + developer + + + + + + UTF-8 + 11 + 11 + 3.3.1 + + + + + + org.springframework.boot + spring-boot-starter-web + + + log4j-to-slf4j + org.apache.logging.log4j + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-actuator + + + + + tomcat-embed-websocket + org.apache.tomcat.embed + provided + + + + com.h2database + h2 + 1.4.200 + + + + + io.getlime.security + powerauth-java-cmd-lib + 1.2.0 + + + io.getlime.security + powerauth-java-crypto + 1.2.0 + + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.13.2 + + + org.bouncycastle + bcprov-jdk15on + 1.70 + + + + + org.springdoc + springdoc-openapi-ui + 1.6.6 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + build-info + + build-info + + + + + + org.apache.maven.plugins + maven-war-plugin + ${maven-war-plugin.version} + + + + + diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/ServletInitializer.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/ServletInitializer.java new file mode 100644 index 00000000..697d1fe9 --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/ServletInitializer.java @@ -0,0 +1,39 @@ +/* + * Copyright 2022 Wultra s.r.o. + * + * Licensed 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 com.wultra.security.powerauth.app.testserver; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +import java.security.Security; + +/** + * Spring Boot servlet initializer. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +public class ServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + // Register BC provider + Security.addProvider(new BouncyCastleProvider()); + + return application.sources(TestServerApplication.class); + } + +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/TestServerApplication.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/TestServerApplication.java new file mode 100644 index 00000000..31a520c0 --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/TestServerApplication.java @@ -0,0 +1,32 @@ +/* + * Copyright 2022 Wultra s.r.o. + * + * Licensed 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 com.wultra.security.powerauth.app.testserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Spring Boot application main class. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +@SpringBootApplication +public class TestServerApplication { + + public static void main(String[] args) { + SpringApplication.run(TestServerApplication.class, args); + } +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/config/TestServerConfiguration.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/config/TestServerConfiguration.java new file mode 100644 index 00000000..c0b740ed --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/config/TestServerConfiguration.java @@ -0,0 +1,42 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +/** + * Test server configuration. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +@Configuration +@Data +@ComponentScan(basePackages = {"com.wultra.security", "io.getlime.security"}) +public class TestServerConfiguration { + + @Value("${powerauth.enrollment.service.url:http://localhost:8080/enrollment-server}") + private String enrollmentServiceUrl; + + @Value("${powerauth.version}") + private String version; + +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/controller/ActivationController.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/controller/ActivationController.java new file mode 100644 index 00000000..36f0bb9f --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/controller/ActivationController.java @@ -0,0 +1,69 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.controller; + +import com.wultra.security.powerauth.app.testserver.errorhandling.AppConfigNotFoundException; +import com.wultra.security.powerauth.app.testserver.errorhandling.GenericCryptographyException; +import com.wultra.security.powerauth.app.testserver.errorhandling.RemoteExecutionException; +import com.wultra.security.powerauth.app.testserver.model.request.CreateActivationRequest; +import com.wultra.security.powerauth.app.testserver.model.response.CreateActivationResponse; +import com.wultra.security.powerauth.app.testserver.service.ActivationService; +import io.getlime.core.rest.model.base.request.ObjectRequest; +import io.getlime.core.rest.model.base.response.ObjectResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * Controller for activation actions. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +@RestController +@RequestMapping("activation") +public class ActivationController { + + private final ActivationService activationService; + + /** + * Controller constructor. + * @param activationService Activation service. + */ + @Autowired + public ActivationController(ActivationService activationService) { + this.activationService = activationService; + } + + /** + * Create an activation. + * @param request Create activation request. + * @return Create activation response. + * @throws AppConfigNotFoundException Thrown when application configuration is not found. + * @throws GenericCryptographyException Thrown when cryptography computation fails. + * @throws RemoteExecutionException Thrown when remote execution fails. + */ + @RequestMapping(value = "create", method = RequestMethod.POST) + public ObjectResponse createActivation(@RequestBody ObjectRequest request) throws AppConfigNotFoundException, GenericCryptographyException, RemoteExecutionException { + // TODO - input validation + final CreateActivationResponse response = activationService.createActivation(request.getRequestObject()); + return new ObjectResponse<>(response); + } +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/TestConfigRepository.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/TestConfigRepository.java new file mode 100644 index 00000000..8f5d476c --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/TestConfigRepository.java @@ -0,0 +1,31 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.database; + +import com.wultra.security.powerauth.app.testserver.database.entity.TestConfigEntity; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +/** + * Repository for test application configuration. + */ +@Repository +public interface TestConfigRepository extends CrudRepository { + +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/TestStatusRepository.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/TestStatusRepository.java new file mode 100644 index 00000000..df97a72a --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/TestStatusRepository.java @@ -0,0 +1,31 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.database; + +import com.wultra.security.powerauth.app.testserver.database.entity.TestStatusEntity; +import org.springframework.data.repository.CrudRepository; + +/** + * Repository for test application status. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +public interface TestStatusRepository extends CrudRepository { + +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/entity/TestConfigEntity.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/entity/TestConfigEntity.java new file mode 100644 index 00000000..8bb0f311 --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/entity/TestConfigEntity.java @@ -0,0 +1,76 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.database.entity; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Objects; + +/** + * Test application configuration entity. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +@Getter +@Setter +@ToString +@NoArgsConstructor +@Entity +@Table(name = "pa_test_config") +public class TestConfigEntity implements Serializable { + + private static final long serialVersionUID = -7771850381097895836L; + + @Id + @Column(name = "application_id", nullable = false) + private Long applicationId; + + @Column(name = "application_name", nullable = false) + private String applicationName; + + @Column(name = "application_key", nullable = false) + private String applicationKey; + + @Column(name = "application_secret", nullable = false) + private String applicationSecret; + + @Column(name = "master_public_key", nullable = false) + private String masterPublicKey; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TestConfigEntity that = (TestConfigEntity) o; + return applicationKey.equals(that.applicationKey); + } + + @Override + public int hashCode() { + return Objects.hash(applicationKey); + } +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/entity/TestStatusEntity.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/entity/TestStatusEntity.java new file mode 100644 index 00000000..48e449ba --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/database/entity/TestStatusEntity.java @@ -0,0 +1,91 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.database.entity; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Objects; + +/** + * Test application status entity. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +@Getter +@Setter +@ToString +@NoArgsConstructor +@Entity +@Table(name = "pa_test_status") +public class TestStatusEntity implements Serializable { + + private static final long serialVersionUID = -6389531428000326009L; + + @Id + @Column(name = "activation_id", nullable = false) + private String activationId; + + @Column(name = "server_public_key", nullable = false) + private String serverPublicKey; + + @Column(name = "counter", nullable = false) + private Long counter; + + @Column(name = "ctr_data", nullable = false) + private String ctrData; + + @Column(name = "encrypted_device_private_key", nullable = false) + private String encryptedDevicePrivateKey; + + @Column(name = "signature_biometry_key", nullable = false) + private String signatureBiometryKey; + + @Column(name = "signature_knowledge_key_encrypted", nullable = false) + private String signatureKnowledgeKeyEncrypted; + + @Column(name = "signature_knowledge_key_salt", nullable = false) + private String signatureKnowledgeKeySalt; + + @Column(name = "signature_possession_key", nullable = false) + private String signaturePossessionKey; + + @Column(name = "transport_master_key", nullable = false) + private String transportMasterKey; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TestStatusEntity that = (TestStatusEntity) o; + return activationId.equals(that.activationId); + } + + @Override + public int hashCode() { + return Objects.hash(activationId); + } +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/AppConfigNotFoundException.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/AppConfigNotFoundException.java new file mode 100644 index 00000000..3dad2f6f --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/AppConfigNotFoundException.java @@ -0,0 +1,44 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.errorhandling; + +/** + * Exception for case when application configuration is not found. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +public class AppConfigNotFoundException extends Exception { + + private static final long serialVersionUID = -1073701264200048869L; + + /** + * Default exception constructor. + */ + public AppConfigNotFoundException() { + } + + /** + * Constructor with error message. + * @param message Error message. + */ + public AppConfigNotFoundException(String message) { + super(message); + } + +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/DefaultExceptionHandler.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/DefaultExceptionHandler.java new file mode 100644 index 00000000..d0085523 --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/DefaultExceptionHandler.java @@ -0,0 +1,88 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.errorhandling; + +import io.getlime.core.rest.model.base.response.ErrorResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Exception handler for RESTful API issues. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +@ControllerAdvice +public class DefaultExceptionHandler { + + private final static Logger logger = LoggerFactory.getLogger(DefaultExceptionHandler.class); + + /** + * Default exception handler, for unexpected errors. + * @param t Throwable. + * @return Response with error details. + */ + @ExceptionHandler(Throwable.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public @ResponseBody ErrorResponse handleDefaultException(Throwable t) { + logger.error("Error occurred when processing the request.", t); + return new ErrorResponse("ERROR_GENERIC", "Unknown error occurred while processing request."); + } + + /** + * Exception handler for application not found exception. + * @param ex Exception. + * @return Response with error details. + */ + @ExceptionHandler(AppConfigNotFoundException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public @ResponseBody ErrorResponse handleApplicationNotFoundException(AppConfigNotFoundException ex) { + logger.warn("Error occurred during application lookup.", ex); + return new ErrorResponse("APPLICATION_NOT_FOUND", "Application was not found."); + } + + /** + * Exception handler for generic cryptography exception. + * @param ex Exception. + * @return Response with error details. + */ + @ExceptionHandler(GenericCryptographyException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public @ResponseBody ErrorResponse handleGenericCryptographyException(GenericCryptographyException ex) { + logger.warn("Error occurred during cryptography computation.", ex); + return new ErrorResponse("GENERIC_CRYPTOGRAPHY_ERROR", "Generic cryptography error occurred."); + } + + /** + * Exception handler for remote execution exception. + * @param ex Exception. + * @return Response with error details. + */ + @ExceptionHandler(RemoteExecutionException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public @ResponseBody ErrorResponse handleRemoteExecutionException(RemoteExecutionException ex) { + logger.warn("Error occurred during remote execution.", ex); + return new ErrorResponse("REMOTE_EXECUTION_ERROR", "Remote execution error occurred."); + } + +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/GenericCryptographyException.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/GenericCryptographyException.java new file mode 100644 index 00000000..125cc4c7 --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/GenericCryptographyException.java @@ -0,0 +1,44 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.errorhandling; + +/** + * Exception for case when cryptography computation fails. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +public class GenericCryptographyException extends Exception { + + private static final long serialVersionUID = 4520301398113031036L; + + /** + * Default constructor. + */ + public GenericCryptographyException() { + } + + /** + * Constructor with error message. + * @param message Error message. + */ + public GenericCryptographyException(String message) { + super(message); + } + +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/RemoteExecutionException.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/RemoteExecutionException.java new file mode 100644 index 00000000..a26de2f8 --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/RemoteExecutionException.java @@ -0,0 +1,44 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.errorhandling; + +/** + * Exception for case when remote execution fails. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +public class RemoteExecutionException extends Exception { + + private static final long serialVersionUID = 4520301398113031036L; + + /** + * Default constructor. + */ + public RemoteExecutionException() { + } + + /** + * Constructor with error message. + * @param message Error message. + */ + public RemoteExecutionException(String message) { + super(message); + } + +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/model/request/CreateActivationRequest.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/model/request/CreateActivationRequest.java new file mode 100644 index 00000000..89558ca7 --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/model/request/CreateActivationRequest.java @@ -0,0 +1,36 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.model.request; + +import lombok.Data; + +/** + * Request for creating an activation. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +@Data +public class CreateActivationRequest { + + private Long applicationId; + private String activationName; + private String password; + private String activationCode; + +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/model/response/CreateActivationResponse.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/model/response/CreateActivationResponse.java new file mode 100644 index 00000000..e312b0c7 --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/model/response/CreateActivationResponse.java @@ -0,0 +1,33 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.model.response; + +import lombok.Data; + +/** + * Response for creating an activation. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +@Data +public class CreateActivationResponse { + + private String activationId; + +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ActivationService.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ActivationService.java new file mode 100644 index 00000000..3feb7f87 --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ActivationService.java @@ -0,0 +1,151 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.service; + +import com.google.common.io.BaseEncoding; +import com.wultra.security.powerauth.app.testserver.config.TestServerConfiguration; +import com.wultra.security.powerauth.app.testserver.database.TestConfigRepository; +import com.wultra.security.powerauth.app.testserver.database.entity.TestConfigEntity; +import com.wultra.security.powerauth.app.testserver.errorhandling.AppConfigNotFoundException; +import com.wultra.security.powerauth.app.testserver.errorhandling.GenericCryptographyException; +import com.wultra.security.powerauth.app.testserver.errorhandling.RemoteExecutionException; +import com.wultra.security.powerauth.app.testserver.model.request.CreateActivationRequest; +import com.wultra.security.powerauth.app.testserver.model.response.CreateActivationResponse; +import com.wultra.security.powerauth.app.testserver.util.ResultStatusUtil; +import io.getlime.security.powerauth.crypto.lib.util.KeyConvertor; +import io.getlime.security.powerauth.lib.cmd.logging.ObjectStepLogger; +import io.getlime.security.powerauth.lib.cmd.logging.model.StepItem; +import io.getlime.security.powerauth.lib.cmd.steps.model.PrepareActivationStepModel; +import io.getlime.security.powerauth.lib.cmd.steps.v3.PrepareActivationStep; +import org.json.simple.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.security.PublicKey; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Activation service. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +@Service +public class ActivationService { + + private final static Logger logger = LoggerFactory.getLogger(ActivationService.class); + + private final TestServerConfiguration config; + private final TestConfigRepository appConfigRepository; + private final ResultStatusUtil resultStatusUtil; + private final PrepareActivationStep prepareActivationStep; + + private static final KeyConvertor keyConvertor = new KeyConvertor(); + + /** + * Service constructor. + * @param config Test server configuration. + * @param configRepository Test application configuration repository. + * @param resultStatusUtil Result status utilities. + * @param prepareActivationStep Prepare activation step. + */ + @Autowired + public ActivationService(TestServerConfiguration config, TestConfigRepository configRepository, ResultStatusUtil resultStatusUtil, PrepareActivationStep prepareActivationStep) { + this.config = config; + this.appConfigRepository = configRepository; + this.resultStatusUtil = resultStatusUtil; + this.prepareActivationStep = prepareActivationStep; + } + + /** + * Create an activation using activation code. + * @param request Create activation request. + * @return Create activation response. + * @throws AppConfigNotFoundException Thrown when application configuration is not found. + * @throws GenericCryptographyException Thrown when cryptography computation fails. + */ + @Transactional + @SuppressWarnings("unchecked") + public CreateActivationResponse createActivation(CreateActivationRequest request) throws AppConfigNotFoundException, GenericCryptographyException, RemoteExecutionException { + // TODO - input validation + final Long applicationId = request.getApplicationId(); + final Optional appConfigOptional = appConfigRepository.findById(applicationId); + + if (appConfigOptional.isEmpty()) { + throw new AppConfigNotFoundException("Application configuration was not found for application ID: " + applicationId); + } + + final TestConfigEntity appConfig = appConfigOptional.get(); + final byte[] masterKeyBytes = BaseEncoding.base64().decode(appConfig.getMasterPublicKey()); + + final PublicKey publicKey; + try { + publicKey = keyConvertor.convertBytesToPublicKey(masterKeyBytes); + } catch (Exception ex) { + logger.warn("Key conversion failed, reason: {}", ex.getMessage()); + logger.debug(ex.getMessage(), ex); + throw new GenericCryptographyException("Key conversion failed"); + } + + final JSONObject resultStatusObject = new JSONObject(); + + // Prepare activation + final PrepareActivationStepModel model = new PrepareActivationStepModel(); + model.setActivationCode(request.getActivationCode()); + model.setActivationName(request.getActivationName()); + model.setApplicationKey(appConfig.getApplicationKey()); + model.setApplicationSecret(appConfig.getApplicationSecret()); + model.setMasterPublicKey(publicKey); + model.setHeaders(new HashMap<>()); + model.setPassword(request.getPassword()); + model.setResultStatusObject(resultStatusObject); + model.setUriString(config.getEnrollmentServiceUrl()); + model.setVersion(config.getVersion()); + model.setDeviceInfo("backend-tests"); + + String activationId = null; + try { + final ObjectStepLogger stepLogger = new ObjectStepLogger(); + prepareActivationStep.execute(stepLogger, model.toMap()); + for (StepItem item: stepLogger.getItems()) { + if ("Activation Done".equals(item.getName())) { + final Map responseMap = (Map) item.getObject(); + activationId = (String) responseMap.get("activationId"); + break; + } + } + } catch (Exception ex) { + logger.warn("Remote execution failed, reason: {}", ex.getMessage()); + logger.debug(ex.getMessage(), ex); + throw new RemoteExecutionException("Remote execution failed"); + } + + resultStatusUtil.persistResultStatus(resultStatusObject); + + // TODO - extract response from steps + final CreateActivationResponse response = new CreateActivationResponse(); + response.setActivationId(activationId); + return response; + } + +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/util/ResultStatusUtil.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/util/ResultStatusUtil.java new file mode 100644 index 00000000..e37d9e57 --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/util/ResultStatusUtil.java @@ -0,0 +1,89 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.util; + +import com.wultra.security.powerauth.app.testserver.database.TestStatusRepository; +import com.wultra.security.powerauth.app.testserver.database.entity.TestStatusEntity; +import org.json.simple.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +/** + * Utility service for persistence of result status. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +@Service +public class ResultStatusUtil { + + private final TestStatusRepository appStatusRepository; + + /** + * Utility service constructor. + * @param appStatusRepository Test application status repository. + */ + @Autowired + public ResultStatusUtil(TestStatusRepository appStatusRepository) { + this.appStatusRepository = appStatusRepository; + } + + /** + * Utility method for persisting results status object to database. + * @param resultStatusObject Result status object. + */ + public void persistResultStatus(JSONObject resultStatusObject) { + final String activationId = getStringValue(resultStatusObject, "activationId"); + final Optional statusOptional = appStatusRepository.findById(activationId); + final TestStatusEntity statusEntity = statusOptional.orElseGet(TestStatusEntity::new); + + final String serverPublicKey = getStringValue(resultStatusObject, "serverPublicKey"); + final Long counter = getLongValue(resultStatusObject, "counter"); + final String ctrData = getStringValue(resultStatusObject, "ctrData"); + final String encryptedDevicePrivateKey = getStringValue(resultStatusObject, "encryptedDevicePrivateKey"); + final String signatureBiometryKey = getStringValue(resultStatusObject, "signatureBiometryKey"); + final String signatureKnowledgeKeyEncrypted = getStringValue(resultStatusObject, "signatureKnowledgeKeyEncrypted"); + final String signatureKnowledgeKeySalt = getStringValue(resultStatusObject, "signatureKnowledgeKeySalt"); + final String signaturePossessionKey = getStringValue(resultStatusObject, "signaturePossessionKey"); + final String transportMasterKey = getStringValue(resultStatusObject, "transportMasterKey"); + + statusEntity.setActivationId(activationId); + statusEntity.setServerPublicKey(serverPublicKey); + statusEntity.setCounter(counter); + statusEntity.setCtrData(ctrData); + statusEntity.setEncryptedDevicePrivateKey(encryptedDevicePrivateKey); + statusEntity.setSignatureBiometryKey(signatureBiometryKey); + statusEntity.setSignatureKnowledgeKeyEncrypted(signatureKnowledgeKeyEncrypted); + statusEntity.setSignatureKnowledgeKeySalt(signatureKnowledgeKeySalt); + statusEntity.setSignaturePossessionKey(signaturePossessionKey); + statusEntity.setTransportMasterKey(transportMasterKey); + + appStatusRepository.save(statusEntity); + } + + private String getStringValue(JSONObject resultStatusObject, String key) { + return (String) resultStatusObject.get(key); + } + + private Long getLongValue(JSONObject resultStatusObject, String key) { + return (Long) resultStatusObject.get(key); + } + +} diff --git a/powerauth-test-server/src/main/resources/application.properties b/powerauth-test-server/src/main/resources/application.properties new file mode 100644 index 00000000..f0a689c1 --- /dev/null +++ b/powerauth-test-server/src/main/resources/application.properties @@ -0,0 +1,19 @@ +# Enrollment service URL +powerauth.enrollment.service.url=http://localhost:8080/enrollment-server + +# PowerAuth protocol version +powerauth.version=3.1 + +# Store result status in memory +resultstatus.persistenceType=memory + +# Default H2 database configuration for tests +spring.h2.console.enabled=true +spring.h2.console.path=/h2 +spring.datasource.url=jdbc:h2:file:~/powerauth-test;DB_CLOSE_ON_EXIT=FALSE;AUTO_SERVER=TRUE +spring.datasource.username=sa +spring.datasource.password= +spring.datasource.driver-class-name=org.h2.Driver + +# Hibernate Configuration - automatically create DB tables +spring.jpa.hibernate.ddl-auto=create-drop From adc893ba62967c3dad0d311213fc996872048c90 Mon Sep 17 00:00:00 2001 From: Roman Strobl Date: Mon, 14 Mar 2022 18:55:19 +0100 Subject: [PATCH 2/6] Add home page --- powerauth-test-server/pom.xml | 4 ++ .../testserver/controller/HomeController.java | 56 +++++++++++++++++++ .../src/main/resources/application.properties | 3 + .../src/main/resources/templates/index.html | 35 ++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/controller/HomeController.java create mode 100644 powerauth-test-server/src/main/resources/templates/index.html diff --git a/powerauth-test-server/pom.xml b/powerauth-test-server/pom.xml index 53cb0c67..454d15a4 100644 --- a/powerauth-test-server/pom.xml +++ b/powerauth-test-server/pom.xml @@ -87,6 +87,10 @@ org.springframework.boot spring-boot-starter-actuator + + org.springframework.boot + spring-boot-starter-thymeleaf + diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/controller/HomeController.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/controller/HomeController.java new file mode 100644 index 00000000..d606fcf6 --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/controller/HomeController.java @@ -0,0 +1,56 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.info.BuildProperties; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +/** + * Home controller to display nice welcome page. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +@Controller +public class HomeController { + + private BuildProperties buildProperties; + + @Autowired(required = false) + public void setBuildProperties(BuildProperties buildProperties) { + this.buildProperties = buildProperties; + } + + @SuppressWarnings("SameReturnValue") + @RequestMapping(value = "/", method = RequestMethod.GET) + public String home(Model model) { + if (buildProperties != null) { + model.addAttribute("version", buildProperties.getVersion()); + model.addAttribute("built", buildProperties.getTime()); + } else { + model.addAttribute("version", "UNKNOWN"); + model.addAttribute("built", "UNKNOWN"); + } + return "index"; + } + +} diff --git a/powerauth-test-server/src/main/resources/application.properties b/powerauth-test-server/src/main/resources/application.properties index f0a689c1..3e62be28 100644 --- a/powerauth-test-server/src/main/resources/application.properties +++ b/powerauth-test-server/src/main/resources/application.properties @@ -17,3 +17,6 @@ spring.datasource.driver-class-name=org.h2.Driver # Hibernate Configuration - automatically create DB tables spring.jpa.hibernate.ddl-auto=create-drop + +# Disable open session in view to avoid startup warning of Spring boot +spring.jpa.open-in-view=false diff --git a/powerauth-test-server/src/main/resources/templates/index.html b/powerauth-test-server/src/main/resources/templates/index.html new file mode 100644 index 00000000..f8b29520 --- /dev/null +++ b/powerauth-test-server/src/main/resources/templates/index.html @@ -0,0 +1,35 @@ + + + + + + + PowerAuth Test Server + + +

PowerAuth Test Server

+

+ Version version, built on + built. +

+

+ 2022, © Wultra s.r.o. +

+ + \ No newline at end of file From 261fb0b2c6953754f1fb95b40700faffb14b8d5b Mon Sep 17 00:00:00 2001 From: Roman Strobl Date: Fri, 18 Mar 2022 11:47:50 +0100 Subject: [PATCH 3/6] Improve error handling for failed activations, fix incorrect ddl-auto value --- .../controller/ActivationController.java | 4 +- .../ActivationFailedException.java | 44 +++++++++++++++++++ .../DefaultExceptionHandler.java | 11 +++++ .../testserver/service/ActivationService.java | 8 +++- .../src/main/resources/application.properties | 4 +- 5 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/ActivationFailedException.java diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/controller/ActivationController.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/controller/ActivationController.java index 36f0bb9f..2f370770 100644 --- a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/controller/ActivationController.java +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/controller/ActivationController.java @@ -18,6 +18,7 @@ package com.wultra.security.powerauth.app.testserver.controller; +import com.wultra.security.powerauth.app.testserver.errorhandling.ActivationFailedException; import com.wultra.security.powerauth.app.testserver.errorhandling.AppConfigNotFoundException; import com.wultra.security.powerauth.app.testserver.errorhandling.GenericCryptographyException; import com.wultra.security.powerauth.app.testserver.errorhandling.RemoteExecutionException; @@ -59,9 +60,10 @@ public ActivationController(ActivationService activationService) { * @throws AppConfigNotFoundException Thrown when application configuration is not found. * @throws GenericCryptographyException Thrown when cryptography computation fails. * @throws RemoteExecutionException Thrown when remote execution fails. + * @throws ActivationFailedException Thrown when activation fails. */ @RequestMapping(value = "create", method = RequestMethod.POST) - public ObjectResponse createActivation(@RequestBody ObjectRequest request) throws AppConfigNotFoundException, GenericCryptographyException, RemoteExecutionException { + public ObjectResponse createActivation(@RequestBody ObjectRequest request) throws AppConfigNotFoundException, GenericCryptographyException, RemoteExecutionException, ActivationFailedException { // TODO - input validation final CreateActivationResponse response = activationService.createActivation(request.getRequestObject()); return new ObjectResponse<>(response); diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/ActivationFailedException.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/ActivationFailedException.java new file mode 100644 index 00000000..3ce13dd2 --- /dev/null +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/ActivationFailedException.java @@ -0,0 +1,44 @@ +/* + * PowerAuth test and related software components + * Copyright (C) 2022 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.wultra.security.powerauth.app.testserver.errorhandling; + +/** + * Exception for case when activation could not be created. + * + * @author Roman Strobl, roman.strobl@wultra.com + */ +public class ActivationFailedException extends Exception { + + private static final long serialVersionUID = -1691643671398216316L; + + /** + * Default exception constructor. + */ + public ActivationFailedException() { + } + + /** + * Constructor with error message. + * @param message Error message. + */ + public ActivationFailedException(String message) { + super(message); + } + +} diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/DefaultExceptionHandler.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/DefaultExceptionHandler.java index d0085523..30c18fad 100644 --- a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/DefaultExceptionHandler.java +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/errorhandling/DefaultExceptionHandler.java @@ -85,4 +85,15 @@ public class DefaultExceptionHandler { return new ErrorResponse("REMOTE_EXECUTION_ERROR", "Remote execution error occurred."); } + /** + * Exception handler for activation failed execution. + * @param ex Exception. + * @return Response with error details. + */ + @ExceptionHandler(ActivationFailedException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public @ResponseBody ErrorResponse handleActivationFailedException(ActivationFailedException ex) { + logger.warn("Error occurred during activation.", ex); + return new ErrorResponse("ACTIVATION_FAILED", "Activation failed."); + } } diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ActivationService.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ActivationService.java index 3feb7f87..3618317f 100644 --- a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ActivationService.java +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ActivationService.java @@ -22,6 +22,7 @@ import com.wultra.security.powerauth.app.testserver.config.TestServerConfiguration; import com.wultra.security.powerauth.app.testserver.database.TestConfigRepository; import com.wultra.security.powerauth.app.testserver.database.entity.TestConfigEntity; +import com.wultra.security.powerauth.app.testserver.errorhandling.ActivationFailedException; import com.wultra.security.powerauth.app.testserver.errorhandling.AppConfigNotFoundException; import com.wultra.security.powerauth.app.testserver.errorhandling.GenericCryptographyException; import com.wultra.security.powerauth.app.testserver.errorhandling.RemoteExecutionException; @@ -86,7 +87,7 @@ public ActivationService(TestServerConfiguration config, TestConfigRepository co */ @Transactional @SuppressWarnings("unchecked") - public CreateActivationResponse createActivation(CreateActivationRequest request) throws AppConfigNotFoundException, GenericCryptographyException, RemoteExecutionException { + public CreateActivationResponse createActivation(CreateActivationRequest request) throws AppConfigNotFoundException, GenericCryptographyException, RemoteExecutionException, ActivationFailedException { // TODO - input validation final Long applicationId = request.getApplicationId(); final Optional appConfigOptional = appConfigRepository.findById(applicationId); @@ -140,6 +141,11 @@ public CreateActivationResponse createActivation(CreateActivationRequest request throw new RemoteExecutionException("Remote execution failed"); } + if (activationId == null) { + logger.warn("Activation failed"); + throw new ActivationFailedException("Activation failed"); + } + resultStatusUtil.persistResultStatus(resultStatusObject); // TODO - extract response from steps diff --git a/powerauth-test-server/src/main/resources/application.properties b/powerauth-test-server/src/main/resources/application.properties index 3e62be28..9ceda941 100644 --- a/powerauth-test-server/src/main/resources/application.properties +++ b/powerauth-test-server/src/main/resources/application.properties @@ -8,7 +8,7 @@ powerauth.version=3.1 resultstatus.persistenceType=memory # Default H2 database configuration for tests -spring.h2.console.enabled=true +spring.h2.console.enabled=false spring.h2.console.path=/h2 spring.datasource.url=jdbc:h2:file:~/powerauth-test;DB_CLOSE_ON_EXIT=FALSE;AUTO_SERVER=TRUE spring.datasource.username=sa @@ -16,7 +16,7 @@ spring.datasource.password= spring.datasource.driver-class-name=org.h2.Driver # Hibernate Configuration - automatically create DB tables -spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.hibernate.ddl-auto=create # Disable open session in view to avoid startup warning of Spring boot spring.jpa.open-in-view=false From fff4fd9b6c3e34d746a9cfd69e4e87761eaecd74 Mon Sep 17 00:00:00 2001 From: Roman Strobl Date: Mon, 21 Mar 2022 17:03:11 +0100 Subject: [PATCH 4/6] Avoid deleting data in test database --- .../src/main/resources/application.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerauth-test-server/src/main/resources/application.properties b/powerauth-test-server/src/main/resources/application.properties index 9ceda941..e7bf1e0b 100644 --- a/powerauth-test-server/src/main/resources/application.properties +++ b/powerauth-test-server/src/main/resources/application.properties @@ -15,8 +15,8 @@ spring.datasource.username=sa spring.datasource.password= spring.datasource.driver-class-name=org.h2.Driver -# Hibernate Configuration - automatically create DB tables -spring.jpa.hibernate.ddl-auto=create +# Hibernate Configuration - automatically update DB schema +spring.jpa.hibernate.ddl-auto=update # Disable open session in view to avoid startup warning of Spring boot spring.jpa.open-in-view=false From 9d055224676ec5059ed5e0bd4f5e97e9fe0e9d3f Mon Sep 17 00:00:00 2001 From: Roman Strobl Date: Mon, 4 Apr 2022 15:40:42 +0200 Subject: [PATCH 5/6] Update Spring Boot version --- powerauth-test-server/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerauth-test-server/pom.xml b/powerauth-test-server/pom.xml index 454d15a4..968a0264 100644 --- a/powerauth-test-server/pom.xml +++ b/powerauth-test-server/pom.xml @@ -31,7 +31,7 @@ org.springframework.boot spring-boot-starter-parent - 2.6.4 + 2.6.6 From 618c53cc8f958c1d872bd8beae78cae0ab7ff0db Mon Sep 17 00:00:00 2001 From: Roman Strobl Date: Mon, 4 Apr 2022 16:19:27 +0200 Subject: [PATCH 6/6] Fix issues found in pull request review --- .../testserver/service/ActivationService.java | 58 ++++++++++++------- .../ResultStatusService.java} | 6 +- 2 files changed, 40 insertions(+), 24 deletions(-) rename powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/{util/ResultStatusUtil.java => service/ResultStatusService.java} (95%) diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ActivationService.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ActivationService.java index 3618317f..c630d915 100644 --- a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ActivationService.java +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ActivationService.java @@ -28,7 +28,6 @@ import com.wultra.security.powerauth.app.testserver.errorhandling.RemoteExecutionException; import com.wultra.security.powerauth.app.testserver.model.request.CreateActivationRequest; import com.wultra.security.powerauth.app.testserver.model.response.CreateActivationResponse; -import com.wultra.security.powerauth.app.testserver.util.ResultStatusUtil; import io.getlime.security.powerauth.crypto.lib.util.KeyConvertor; import io.getlime.security.powerauth.lib.cmd.logging.ObjectStepLogger; import io.getlime.security.powerauth.lib.cmd.logging.model.StepItem; @@ -58,7 +57,7 @@ public class ActivationService { private final TestServerConfiguration config; private final TestConfigRepository appConfigRepository; - private final ResultStatusUtil resultStatusUtil; + private final ResultStatusService resultStatusUtil; private final PrepareActivationStep prepareActivationStep; private static final KeyConvertor keyConvertor = new KeyConvertor(); @@ -71,7 +70,7 @@ public class ActivationService { * @param prepareActivationStep Prepare activation step. */ @Autowired - public ActivationService(TestServerConfiguration config, TestConfigRepository configRepository, ResultStatusUtil resultStatusUtil, PrepareActivationStep prepareActivationStep) { + public ActivationService(TestServerConfiguration config, TestConfigRepository configRepository, ResultStatusService resultStatusUtil, PrepareActivationStep prepareActivationStep) { this.config = config; this.appConfigRepository = configRepository; this.resultStatusUtil = resultStatusUtil; @@ -90,24 +89,8 @@ public ActivationService(TestServerConfiguration config, TestConfigRepository co public CreateActivationResponse createActivation(CreateActivationRequest request) throws AppConfigNotFoundException, GenericCryptographyException, RemoteExecutionException, ActivationFailedException { // TODO - input validation final Long applicationId = request.getApplicationId(); - final Optional appConfigOptional = appConfigRepository.findById(applicationId); - - if (appConfigOptional.isEmpty()) { - throw new AppConfigNotFoundException("Application configuration was not found for application ID: " + applicationId); - } - - final TestConfigEntity appConfig = appConfigOptional.get(); - final byte[] masterKeyBytes = BaseEncoding.base64().decode(appConfig.getMasterPublicKey()); - - final PublicKey publicKey; - try { - publicKey = keyConvertor.convertBytesToPublicKey(masterKeyBytes); - } catch (Exception ex) { - logger.warn("Key conversion failed, reason: {}", ex.getMessage()); - logger.debug(ex.getMessage(), ex); - throw new GenericCryptographyException("Key conversion failed"); - } - + final TestConfigEntity appConfig = getTestAppConfig(applicationId); + final PublicKey publicKey = getMasterPublicKey(appConfig); final JSONObject resultStatusObject = new JSONObject(); // Prepare activation @@ -154,4 +137,37 @@ public CreateActivationResponse createActivation(CreateActivationRequest request return response; } + /** + * Get test application configuration. + * @param applicationId Application identifier. + * @return Test application configuration. + * @throws AppConfigNotFoundException Thrown when application configuration is not found. + */ + private TestConfigEntity getTestAppConfig(Long applicationId) throws AppConfigNotFoundException { + final Optional appConfigOptional = appConfigRepository.findById(applicationId); + + if (appConfigOptional.isEmpty()) { + throw new AppConfigNotFoundException("Application configuration was not found for application ID: " + applicationId); + } + + return appConfigOptional.get(); + } + + /** + * Get master public key from test application configuration. + * @param appConfig Test application configuration. + * @return Master public key. + * @throws GenericCryptographyException Thrown in case public key conversion fails. + */ + private PublicKey getMasterPublicKey(TestConfigEntity appConfig) throws GenericCryptographyException { + final byte[] masterKeyBytes = BaseEncoding.base64().decode(appConfig.getMasterPublicKey()); + + try { + return keyConvertor.convertBytesToPublicKey(masterKeyBytes); + } catch (Exception ex) { + logger.warn("Key conversion failed, reason: {}", ex.getMessage()); + logger.debug(ex.getMessage(), ex); + throw new GenericCryptographyException("Key conversion failed"); + } + } } diff --git a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/util/ResultStatusUtil.java b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ResultStatusService.java similarity index 95% rename from powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/util/ResultStatusUtil.java rename to powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ResultStatusService.java index e37d9e57..33b7dfab 100644 --- a/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/util/ResultStatusUtil.java +++ b/powerauth-test-server/src/main/java/com/wultra/security/powerauth/app/testserver/service/ResultStatusService.java @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package com.wultra.security.powerauth.app.testserver.util; +package com.wultra.security.powerauth.app.testserver.service; import com.wultra.security.powerauth.app.testserver.database.TestStatusRepository; import com.wultra.security.powerauth.app.testserver.database.entity.TestStatusEntity; @@ -32,7 +32,7 @@ * @author Roman Strobl, roman.strobl@wultra.com */ @Service -public class ResultStatusUtil { +public class ResultStatusService { private final TestStatusRepository appStatusRepository; @@ -41,7 +41,7 @@ public class ResultStatusUtil { * @param appStatusRepository Test application status repository. */ @Autowired - public ResultStatusUtil(TestStatusRepository appStatusRepository) { + public ResultStatusService(TestStatusRepository appStatusRepository) { this.appStatusRepository = appStatusRepository; }