diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml new file mode 100644 index 000000000..8a887f339 --- /dev/null +++ b/.github/workflows/coverity-scan.yml @@ -0,0 +1,16 @@ +name: Run Coverity scan and upload results + +on: + workflow_dispatch: + schedule: + - cron: '0 10 1 * *' # monthly + + +jobs: + coverity-scan: + uses: wultra/wultra-infrastructure/.github/workflows/coverity-scan.yml@develop + secrets: inherit + with: + project-name: ${{ github.event.repository.name }} + version: ${{ github.sha }} + description: ${{ github.ref }} diff --git a/.github/workflows/maven-deploy.yml b/.github/workflows/maven-deploy.yml new file mode 100644 index 000000000..8f31c6b75 --- /dev/null +++ b/.github/workflows/maven-deploy.yml @@ -0,0 +1,50 @@ +name: Deploy with Maven + +on: + workflow_dispatch: + branches: + - 'develop' + - 'master' + - 'releases/*' + - 'test/ci' + inputs: + release_type: + type: choice + description: + default: snapshot + options: + - snapshot + - release + environment: + type: environment + default: internal-publish + description: internal or external repository + push: + branches: + - 'develop' + - 'test/ci' + +jobs: + maven-deploy-jfrog: + if: ${{ github.event_name == 'push' }} + name: Deploy to jfrog + uses: wultra/wultra-infrastructure/.github/workflows/maven-deploy.yml@develop + with: + environment: internal-publish + release_type: snapshot + secrets: + username: ${{ secrets.MAVEN_CENTRAL_USERNAME }} + password: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} + + maven-deploy-manual: + if: ${{ github.event_name == 'workflow_dispatch' }} + name: Deploy by parameter + uses: wultra/wultra-infrastructure/.github/workflows/maven-deploy.yml@develop + with: + environment: ${{ inputs.environment }} + release_type: ${{ inputs.release_type }} + secrets: + username: ${{ secrets.MAVEN_CENTRAL_USERNAME }} + password: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} + gpg_passphrase: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} + gpg_key: ${{ secrets.OSSRH_GPG_SECRET_KEY }} \ No newline at end of file diff --git a/.github/workflows/maven-test.yml b/.github/workflows/maven-test.yml new file mode 100644 index 000000000..6bdada9fe --- /dev/null +++ b/.github/workflows/maven-test.yml @@ -0,0 +1,18 @@ +name: Test with Maven + +on: + workflow_dispatch: + push: + branches: + - 'master' + - 'releases/**' + pull_request: + branches: + - 'develop' + - 'master' + - 'releases/**' + +jobs: + maven-tests: + uses: wultra/wultra-infrastructure/.github/workflows/maven-test.yml@develop + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/owas-dependecy-check.yml b/.github/workflows/owas-dependecy-check.yml new file mode 100644 index 000000000..c79178b2a --- /dev/null +++ b/.github/workflows/owas-dependecy-check.yml @@ -0,0 +1,12 @@ +name: Run OWASP Dependency Check +on: + workflow_dispatch: + + push: + branches: + - 'develop' + +jobs: + owasp-check: + uses: wultra/wultra-infrastructure/.github/workflows/owasp-dependency-check.yml@develop + secrets: inherit \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b4831bf4f..000000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: java -jdk: - - openjdk11 -branches: - only: - - master - - coverity_scan -env: - global: - - secure: "t7YtG0/8BDsI8hfb4430jOXQy4n5lwFaxz2o8tTk9c+QisNqAUvUkWabk8XkuBJMb7OwHwEC40s729oFYu9AD1Ul+M/BiyRQXm+tBoZvQESQqRrRlcJfYFbuKVcA4XBIeI1F+j+QUaKdzFYsPDOR3aOtK9wN+UtSHn9Dtq2Up++aifF8S7+GIsW6aJ1Y4oWPbkTPzAVfJ2UP5euv9EsWGYGIueJ0aryoi8J/lCnEU5dsMVooa9S8gGaByeEdF+lRvNiNzli5nuRiYXISFtFC9Ge/TaO3BFHa5HUJ8CLtNfZX9PCLgr78BmRo5iAI45ri8RwtEDeV7HJAXr76UCAxQa0K537LtV4uvJRCWTDg6fY6kAfE1CuYAKOzX6CffeHmakuBsgh0nI1OFjwj2nsPCw7wsYC6uWDlbTge9Hq/smQWQxnUaLKxVWollrsB/N5sxcLbemJSy59O56CWQV97oLN+0et+FBG10I3Xkcy++oz96ZdsX6Vo3VTfLeQXk1u4Irvw8gssJZW/Jv7R6IEwTD73gNl382UuDHUyPPZg59iaJ/GzkqKtba0oKBDzddR2z9F8NswF2s9dpYhsp37bvjYlphdoyjj6SBk7f7qehKSvw3hMCS1Nb91b1HWPWzKMko08HGzqnS1ViyE+ElEPvHk0HUWGETJBPGFH3JqCbNo=" - -before_install: - - echo -n | openssl s_client -connect https://scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- - -addons: - coverity_scan: - project: - name: "wultra/powerauth-server" - description: "Build submitted via Travis CI" - notification_email: roman.strobl@wultra.com - build_command_prepend: "mvn clean" - build_command: "mvn -DskipTests=true compile" - branch_pattern: coverity_scan \ No newline at end of file diff --git a/README.md b/README.md index 948362da0..4f2dbdeb4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PowerAuth Server -[![Build Status](https://travis-ci.org/wultra/powerauth-server.svg?branch=master)](https://travis-ci.org/wultra/powerauth-server) +[![Build Status](https://github.com/wultra/powerauth-server/actions/workflows/maven-test.yml/badge.svg?branch=master)](https://github.com/wultra/powerauth-server/actions/workflows/maven-test.yml?query=branch%3Amaster) [![Status](https://scan.coverity.com/projects/16632/badge.svg)](https://scan.coverity.com/projects/wultra-powerauth-server) [![GitHub issues](https://img.shields.io/github/issues/wultra/powerauth-server.svg?maxAge=2592000)](https://github.com/wultra/powerauth-server/issues) [![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0) diff --git a/docs/powerauth-admin/Activation-Recovery.md b/docs/Activation-Recovery.md similarity index 100% rename from docs/powerauth-admin/Activation-Recovery.md rename to docs/Activation-Recovery.md diff --git a/docs/powerauth-admin/Admin-Deploying-Wildfly.md b/docs/Admin-Deploying-Wildfly.md similarity index 100% rename from docs/powerauth-admin/Admin-Deploying-Wildfly.md rename to docs/Admin-Deploying-Wildfly.md diff --git a/docs/powerauth-admin/Configuration-Properties.md b/docs/Configuration-Properties-Admin.md similarity index 100% rename from docs/powerauth-admin/Configuration-Properties.md rename to docs/Configuration-Properties-Admin.md diff --git a/docs/powerauth-server/Configuration-Properties.md b/docs/Configuration-Properties.md similarity index 100% rename from docs/powerauth-server/Configuration-Properties.md rename to docs/Configuration-Properties.md diff --git a/docs/powerauth-server/Configuring-REST-Client-for-Spring.md b/docs/Configuring-REST-Client-for-Spring.md similarity index 100% rename from docs/powerauth-server/Configuring-REST-Client-for-Spring.md rename to docs/Configuring-REST-Client-for-Spring.md diff --git a/docs/powerauth-server/Database-Sizing.md b/docs/Database-Sizing.md similarity index 100% rename from docs/powerauth-server/Database-Sizing.md rename to docs/Database-Sizing.md diff --git a/docs/powerauth-server/Database-Structure.md b/docs/Database-Structure.md similarity index 98% rename from docs/powerauth-server/Database-Structure.md rename to docs/Database-Structure.md index 76124cb7e..264f4d21e 100644 --- a/docs/powerauth-server/Database-Structure.md +++ b/docs/Database-Structure.md @@ -492,7 +492,8 @@ CREATE TABLE "pa_operation" ( "max_failure_count" BIGINT NOT NULL, "timestamp_created" TIMESTAMP NOT NULL, "timestamp_expires" TIMESTAMP NOT NULL, - "timestamp_finalized" TIMESTAMP + "timestamp_finalized" TIMESTAMP, + "risk_flags" VARCHAR(255) ); ``` @@ -514,6 +515,7 @@ CREATE TABLE "pa_operation" ( | timestamp_created | timestamp | - | Timestamp of when the operation was created. | | timestamp_expires | timestamp | - | Timestamp of when the operation will expire. | | timestamp_finalized | timestamp | - | Timestamp of when the operation reached the terminal state (approved, rejected, expired, etc.). | +| risk_flages | varchar(255) | - | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | @@ -531,7 +533,8 @@ CREATE TABLE "pa_operation_template" ( "data_template" VARCHAR(255) NOT NULL, "signature_type" VARCHAR(255) NOT NULL, "max_failure_count" BIGINT NOT NULL, - "expiration" BIGINT NOT NULL + "expiration" BIGINT NOT NULL, + "risk_flags" VARCHAR(255) ); ``` @@ -546,6 +549,7 @@ CREATE TABLE "pa_operation_template" ( | signature_type | varchar(255) | - | Comma-separated list of allowed signature types. | | max_failure_count | bigint | - | Maximum allowed number of failed attempts when approving the operation. | | expiration | bigint | - | Operation expiration in seconds (300 = 5 minutes). | +| risk_flages | varchar(255) | - | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | diff --git a/docs/powerauth-admin/Deploying-PowerAuth-Admin.md b/docs/Deploying-PowerAuth-Admin.md similarity index 75% rename from docs/powerauth-admin/Deploying-PowerAuth-Admin.md rename to docs/Deploying-PowerAuth-Admin.md index a36d0968d..3603acf39 100644 --- a/docs/powerauth-admin/Deploying-PowerAuth-Admin.md +++ b/docs/Deploying-PowerAuth-Admin.md @@ -2,9 +2,12 @@ This chapter explains how to deploy PowerAuth Admin. -PowerAuth Admin is a Java EE application (packaged as an executable WAR file) that you can use to work with the PowerAuth Server services in a easy to use visual way. Also, PowerAuth Admin project may serve as a simple example application for the Internet banking integrators, since in essence, it performs the very same tasks. +PowerAuth Admin is a web administration console for the [PowerAuth Server](https://github.com/wultra/powerauth-server). +It allows an easy application setup, an activation management and integration configurations. -*__Important note: Since PowerAuth Admin is a very simple application with direct access to the PowerAuth Server REST services, it must not be under any circumstances published publicly and must be constrained to the in-house closed infrastructure.__* + +Important note: Since PowerAuth Admin is a very simple application with direct access to the PowerAuth Server REST services, it must not be under any circumstances published publicly and must be constrained to the in-house closed infrastructure. + ## Downloading PowerAuth Admin @@ -29,7 +32,9 @@ powerauth.service.security.clientSecret= The credentials are stored in the `pa_integration` table. -_Note: The RESTful interface is secured using Basic HTTP Authentication (pre-emptive)._ + +Note: The RESTful interface is secured using Basic HTTP Authentication (pre-emptive). + ## Disabling SSL Validation During Development @@ -55,7 +60,9 @@ To deploy PowerAuth Admin to Apache Tomcat, simply copy the WAR file in your `we Running PowerAuth Admin application from console using the `java -jar` command is not supported. -*__Important note: Since PowerAuth Admin is a very simple application with direct access to the PowerAuth Server REST services, it must not be under any circumstances published publicly and must be constrained to the in-house closed infrastructure.__* + +Important note: Since PowerAuth Admin is a very simple application with direct access to the PowerAuth Server REST services, it must not be under any circumstances published publicly and must be constrained to the in-house closed infrastructure. + ## Deploying PowerAuth Admin On JBoss / Wildfly diff --git a/docs/powerauth-server/Deploying-PowerAuth-Server.md b/docs/Deploying-PowerAuth-Server.md similarity index 96% rename from docs/powerauth-server/Deploying-PowerAuth-Server.md rename to docs/Deploying-PowerAuth-Server.md index c107f4c79..8c427f55b 100644 --- a/docs/powerauth-server/Deploying-PowerAuth-Server.md +++ b/docs/Deploying-PowerAuth-Server.md @@ -258,3 +258,12 @@ Follow the extra instructions in chapter [Deploying PowerAuth Server on JBoss / PowerAuth Server uses Bouncy Castle as a Java cryptography provider. If you encounter any issues that may point to an incorrectly installed cryptography provider, please follow our tutorial [how to configure Bouncy Castle](./Installing-Bouncy-Castle.md). +### How to Disable Display of Tomcat Version + +It case you do not want to show Tomcat version on error pages when deploying PowerAuth server, you can use the following configuration: + +- Edit the file `/conf/server.xml`. +- Search for the parameters ``. +- Just below that line, insert the following parameters ``. +- Restart Tomcat. + diff --git a/docs/powerauth-server/Deploying-Wildfly.md b/docs/Deploying-Wildfly.md similarity index 98% rename from docs/powerauth-server/Deploying-Wildfly.md rename to docs/Deploying-Wildfly.md index b25ad17f5..d233ca35e 100644 --- a/docs/powerauth-server/Deploying-Wildfly.md +++ b/docs/Deploying-Wildfly.md @@ -19,7 +19,7 @@ PowerAuth server contains the following configuration in `jboss-deployment-struc - + diff --git a/docs/powerauth-server/Encrypting-Records-in-Database.md b/docs/Encrypting-Records-in-Database.md similarity index 100% rename from docs/powerauth-server/Encrypting-Records-in-Database.md rename to docs/Encrypting-Records-in-Database.md diff --git a/docs/powerauth-server/Installing-Bouncy-Castle.md b/docs/Installing-Bouncy-Castle.md similarity index 91% rename from docs/powerauth-server/Installing-Bouncy-Castle.md rename to docs/Installing-Bouncy-Castle.md index 2f7bc246b..366d41188 100644 --- a/docs/powerauth-server/Installing-Bouncy-Castle.md +++ b/docs/Installing-Bouncy-Castle.md @@ -11,7 +11,7 @@ Bouncy Castle library installation depends on Java version and used web containe PowerAuth server uses dynamic initialization of Bouncy Castle provider, so it is not required to configure security provider statically in the Java Runtime configuration. You can get the Bouncy Castle provider here: -https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on +https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk18on ### Installing on Java 11 @@ -19,7 +19,7 @@ Java 11 no longer provides a library extension mechanism and thus Bouncy Castle #### Bouncy Castle on Tomcat -Copy [`bcprov-jdk15on-168.jar`](https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on) to your `${CATALINA_HOME}/lib` folder. +Copy [`bcprov-jdk18on-172.jar`](https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk18on) to your `${CATALINA_HOME}/lib` folder. Bouncy Castle library will not work properly in case any war file deployed to Tomcat contains another copy of the Bouncy Castle library, even if the war file is not related to PowerAuth. @@ -28,7 +28,7 @@ Bouncy Castle library must be only present in the `${CATALINA_HOME}/lib` folder. #### Bouncy Castle on JBoss / Wildfly -PowerAuth server requires a specific version of Bouncy Castle library: `bcprov-jdk15on-168.jar` +PowerAuth server requires a specific version of Bouncy Castle library: `bcprov-jdk18on-172.jar` In order to make PowerAuth Server work on JBoss / Wildfly, you need to add and enable the external Bouncy Castle module on the server by adding the `` element in the `standalone.xml` file: @@ -46,12 +46,12 @@ The module should be defined using a new module XML file in JBoss folder `module - + ``` -Finally, copy the Bouncy Castle library `bcprov-jdk15on-167.jar` into folder `modules/system/layers/base/org/bouncycastle/external/main` so that it is available for the module. +Finally, copy the Bouncy Castle library `bcprov-jdk18on-172.jar` into folder `modules/system/layers/base/org/bouncycastle/external/main` so that it is available for the module. Do not reuse Bouncy Castle module `org.bouncycastle` from JBoss, because version of library provided by JBoss may differ from version required by PowerAuth. @@ -82,7 +82,7 @@ Java 8 provides a library extension mechanism which can be used to installed Bou ##### Standalone Tomcat -When running a standalone Tomcat instance, all you need to do is to copy [`bcprov-jdk15on-167.jar`](https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on) to your `${JDK_HOME}/jre/lib/ext` folder. +When running a standalone Tomcat instance, all you need to do is to copy [`bcprov-jdk18on-172.jar`](https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk18on) to your `${JDK_HOME}/jre/lib/ext` folder. ##### Embedded Tomcat @@ -96,7 +96,7 @@ Make sure to add the provider to the top of the list (ideally, N=2). #### Bouncy Castle on JBoss / Wildfly -PowerAuth server requires a specific version of Bouncy Castle library: `bcprov-jdk15on-167.jar` +PowerAuth server requires a specific version of Bouncy Castle library: `bcprov-jdk18on-172.jar` In order to make PowerAuth Server work on JBoss / Wildfly, you need to add and enable the external Bouncy Castle module on the server by adding the `` element in the `standalone.xml` file: @@ -114,12 +114,12 @@ The module should be defined using a new module XML file in JBoss folder `module - + ``` -Finally, copy the Bouncy Castle library `bcprov-jdk15on-167.jar` into folder `modules/system/layers/base/org/bouncycastle/external/main` so that it is available for the module. +Finally, copy the Bouncy Castle library `bcprov-jdk18on-172.jar` into folder `modules/system/layers/base/org/bouncycastle/external/main` so that it is available for the module. Do not reuse Bouncy Castle module `org.bouncycastle` from JBoss, because version of library provided by JBoss may differ from version required by PowerAuth. diff --git a/docs/powerauth-server/Migration-Instructions.md b/docs/Migration-Instructions.md similarity index 93% rename from docs/powerauth-server/Migration-Instructions.md rename to docs/Migration-Instructions.md index 0a6842976..e5b43aefa 100644 --- a/docs/powerauth-server/Migration-Instructions.md +++ b/docs/Migration-Instructions.md @@ -6,6 +6,7 @@ This page contains PowerAuth Server migration instructions. When updating across multiple versions, you need to perform all migration steps additively. +- [PowerAuth Server 1.3.0](./PowerAuth-Server-1.3.0.md) - [PowerAuth Server 1.2.5](./PowerAuth-Server-1.2.5.md) - [PowerAuth Server 1.2.0](./PowerAuth-Server-1.2.0.md) - [PowerAuth Server 1.1.0](./PowerAuth-Server-1.1.0.md) diff --git a/docs/powerauth-server/Offline-Signatures.md b/docs/Offline-Signatures.md similarity index 100% rename from docs/powerauth-server/Offline-Signatures.md rename to docs/Offline-Signatures.md diff --git a/docs/powerauth-server/PowerAuth-Server-0.18.0.md b/docs/PowerAuth-Server-0.18.0.md similarity index 100% rename from docs/powerauth-server/PowerAuth-Server-0.18.0.md rename to docs/PowerAuth-Server-0.18.0.md diff --git a/docs/powerauth-server/PowerAuth-Server-0.19.0.md b/docs/PowerAuth-Server-0.19.0.md similarity index 100% rename from docs/powerauth-server/PowerAuth-Server-0.19.0.md rename to docs/PowerAuth-Server-0.19.0.md diff --git a/docs/powerauth-server/PowerAuth-Server-0.21.0.md b/docs/PowerAuth-Server-0.21.0.md similarity index 100% rename from docs/powerauth-server/PowerAuth-Server-0.21.0.md rename to docs/PowerAuth-Server-0.21.0.md diff --git a/docs/powerauth-server/PowerAuth-Server-0.22.0.md b/docs/PowerAuth-Server-0.22.0.md similarity index 100% rename from docs/powerauth-server/PowerAuth-Server-0.22.0.md rename to docs/PowerAuth-Server-0.22.0.md diff --git a/docs/powerauth-server/PowerAuth-Server-0.23.0.md b/docs/PowerAuth-Server-0.23.0.md similarity index 100% rename from docs/powerauth-server/PowerAuth-Server-0.23.0.md rename to docs/PowerAuth-Server-0.23.0.md diff --git a/docs/powerauth-server/PowerAuth-Server-0.24.0.md b/docs/PowerAuth-Server-0.24.0.md similarity index 100% rename from docs/powerauth-server/PowerAuth-Server-0.24.0.md rename to docs/PowerAuth-Server-0.24.0.md diff --git a/docs/powerauth-server/PowerAuth-Server-1.0.0.md b/docs/PowerAuth-Server-1.0.0.md similarity index 100% rename from docs/powerauth-server/PowerAuth-Server-1.0.0.md rename to docs/PowerAuth-Server-1.0.0.md diff --git a/docs/powerauth-server/PowerAuth-Server-1.1.0.md b/docs/PowerAuth-Server-1.1.0.md similarity index 100% rename from docs/powerauth-server/PowerAuth-Server-1.1.0.md rename to docs/PowerAuth-Server-1.1.0.md diff --git a/docs/powerauth-server/PowerAuth-Server-1.2.0.md b/docs/PowerAuth-Server-1.2.0.md similarity index 100% rename from docs/powerauth-server/PowerAuth-Server-1.2.0.md rename to docs/PowerAuth-Server-1.2.0.md diff --git a/docs/PowerAuth-Server-1.2.5.md b/docs/PowerAuth-Server-1.2.5.md new file mode 100644 index 000000000..8f3928e9c --- /dev/null +++ b/docs/PowerAuth-Server-1.2.5.md @@ -0,0 +1,65 @@ +# Migration from 1.2.x to 1.2.5 + +This guide contains instructions for migration from PowerAuth Server version `1.2.x` to version `1.2.5`. + +_Warning: release `1.2.5` of PowerAuth server requires application of database migration steps. Usually we do not require a database migration for minor releases, however in release `1.2.5` such migration is necessary._ + +## Create New Columns in Operation Table + +Create a new columns in the operations table: + +- `template_name` - Stores the original template name. +- `activation_flag` - Stores the activation flag that must be present on activation in order to return / approve / reject the operation. +- `additional_data` - Stores attributes related to the approval / rejection / cancellation event. + +### Oracle + +```sql +ALTER TABLE PA_OPERATION ADD TEMPLATE_NAME VARCHAR2(255); + +ALTER TABLE PA_OPERATION ADD ACTIVATION_FLAG VARCHAR2(255); + +ALTER TABLE PA_OPERATION ADD ADDITIONAL_DATA CLOB; +``` + +### PostgreSQL + +```sql +ALTER TABLE pa_operation ADD template_name VARCHAR(255); + +ALTER TABLE pa_operation ADD activation_flag VARCHAR(255); + +ALTER TABLE pa_operation ADD additional_data TEXT; +``` + +### MySQL + +```sql +ALTER TABLE pa_operation ADD template_name VARCHAR(255) NULL; + +ALTER TABLE pa_operation ADD activation_flag VARCHAR(255) NULL; + +ALTER TABLE pa_operation ADD additional_data TEXT NULL; +``` + +## Create New Column in Activation History Table + +The `pa_activation_history` table was updated to include activation version. + +### Oracle + +```sql +ALTER TABLE PA_ACTIVATION_HISTORY ADD activation_version NUMBER(2,0); +``` + +### PostgreSQL + +```sql +ALTER TABLE pa_activation_history ADD activation_version INTEGER; +``` + +### MySQL + +```sql +ALTER TABLE pa_activation_history ADD activation_version int(2); +``` diff --git a/docs/powerauth-server/PowerAuth-Server-1.3.0.md b/docs/PowerAuth-Server-1.3.0.md similarity index 79% rename from docs/powerauth-server/PowerAuth-Server-1.3.0.md rename to docs/PowerAuth-Server-1.3.0.md index e3deda44b..8b89356c5 100644 --- a/docs/powerauth-server/PowerAuth-Server-1.3.0.md +++ b/docs/PowerAuth-Server-1.3.0.md @@ -7,6 +7,18 @@ Migration from release `1.2.x` of PowerAuth server to release `1.3.x` is split i - [Migration from 1.2.x to 1.2.5](./PowerAuth-Server-1.2.5.md) - apply these steps for upgrade to version `1.2.5` - Migration from 1.2.5 to 1.3.x (this document) - apply steps below for upgrade from version `1.2.5` to version `1.3.x` +## Change in Application ID (breaking change) + +In earlier versions of PowerAuth Server, we addressed applications via their numeric (`Long`) database record ID. This proved to be problematic, since the same application had different IDs on different environments. In 1.3.x and further, we now address applications via ID equal to their string name (contents of the `name` column in `pa_application` table), and we now call this value "application ID". + + +We highly recommend renaming the application in the database by editing `pa_application.name` column, so that the name is in a technical format, i.e., "mobile-token-retail", rather than in human readable name, such as "Mobile Token For Retail Clients". + + +This change is consistently reflected in all other parts of PowerAuth stack, i.e., in Push Server or Web Flow. + +In case your system needs to store IDs of application, you need to reflect this change. It is no longer possible to access applications using their numeric database record ID. + ## Database Changes ### Relation Between Operations and Applications @@ -112,6 +124,10 @@ Create tables for auditing: - `audit_log` - table used to store audit logs - `audit_param` - table used to store detailed parameters for audit logs + +The auditing tables may be already present in your database schema in case the database schema is not separated for different PowerAuth applications. You might have also added these tables as part of migration to release `1.2.5` before these duplicate migration instructions were removed. In case tables `audit_log` and `audit_param` are already present, you can safely skip this migration step. + + ### Oracle ```sql @@ -163,7 +179,7 @@ CREATE INDEX audit_param_value ON audit_param (param_value); -- -- Create audit log table. -- -CREATE TABLE audit_log ( +CREATE TABLE IF NOT EXISTS audit_log ( audit_log_id VARCHAR(36) PRIMARY KEY, application_name VARCHAR(256) NOT NULL, audit_level VARCHAR(32) NOT NULL, @@ -182,7 +198,7 @@ CREATE TABLE audit_log ( -- -- Create audit parameters table. -- -CREATE TABLE audit_param ( +CREATE TABLE IF NOT EXISTS audit_param ( audit_log_id VARCHAR(36), timestamp_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, param_key VARCHAR(256), @@ -208,7 +224,7 @@ CREATE INDEX audit_param_value ON audit_param (param_value); -- -- Create audit log table. -- -CREATE TABLE audit_log ( +CREATE TABLE IF NOT EXISTS audit_log ( audit_log_id VARCHAR(36) PRIMARY KEY, application_name VARCHAR(256) NOT NULL, audit_level VARCHAR(32) NOT NULL, @@ -227,7 +243,7 @@ CREATE TABLE audit_log ( -- -- Create audit parameters table. -- -CREATE TABLE audit_param ( +CREATE TABLE IF NOT EXISTS audit_param ( audit_log_id VARCHAR(36), timestamp_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, param_key VARCHAR(256), @@ -260,3 +276,18 @@ We consider the 5-minute interval to still be safe, since the relatively high ac ``` powerauth.service.crypto.activationValidityInMilliseconds=120000 ``` + +## Database Dialect Configuration + +The latest release of PowerAuth requires configuration of database dialect. + +The dialect is specified using following configuration property: +```properties +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL95Dialect +``` + +Use the most specific dialect, if possible, such as: +- `org.hibernate.dialect.Oracle12cDialect` for Oracle 12c or higher +- `org.hibernate.dialect.PostgreSQL95Dialect` for PostgreSQL 9.5 or higher + +You can find additional database dialects in Hibernate documentation. diff --git a/docs/PowerAuth-Server-1.4.0.md b/docs/PowerAuth-Server-1.4.0.md new file mode 100644 index 000000000..ada7b0aa3 --- /dev/null +++ b/docs/PowerAuth-Server-1.4.0.md @@ -0,0 +1,74 @@ +# Migration from 1.3.x to 1.4.0 + +This guide contains instructions for migration from PowerAuth Server version `1.3.x` to version `1.4.0`. + +## Change in PowerAuth Token Verification + +In earlier versions of PowerAuth Server, the token verification endpoint `/rest/v3/token/validate` returned an error in case the activation used by the token was not active. In order to always return activation status as part of the response, we changed the endpoint behaviour and removed the error handling for inactive activations. This change unifies the business logic with signature verification endpoint. + +Before change: + +```java +try { + final ValidateTokenResponse response = powerauthClient.validateToken(request); + // regular business logic +} catch (PowerAuthClientException ex) { + // error handling for inactive activation and all other errors +} +``` + +After change: +```java +try { + final ValidateTokenResponse response = powerauthClient.validateToken(request); + if (response.getActivationStatus() != ActivationStatus.ACTIVE) { + // error handling for inactive activations + } +} catch (PowerAuthClientException ex) { + // error handling for all other errors +} +``` + +Adaptation to this change is required only in case this endpoint is called directly on PowerAuth server. In case you use the `@PowerAuthToken` annotation for token validation, no changes are required. + +## Database Changes + +### Add Risk Flags to Operations and Templates + +Add a column `risk_flags` to the templates and operations. + +#### PostgreSQL + +```sql +ALTER TABLE pa_operation + ADD COLUMN risk_flags VARCHAR(255); + +ALTER TABLE pa_operation_template + ADD COLUMN risk_flags VARCHAR(255); +``` + +#### Oracle + +```sql +ALTER TABLE pa_operation + ADD risk_flags VARCHAR2(255 CHAR); + +ALTER TABLE pa_operation_template + ADD risk_flags VARCHAR2(255 CHAR); +``` + +#### MySQL + +```sql +ALTER TABLE pa_operation + ADD COLUMN risk_flags varchar(255); + +ALTER TABLE pa_operation_template + ADD COLUMN risk_flags varchar(255); +``` + +### Added Database Indexes + +```sql +CREATE INDEX pa_activation_expiration on pa_activation (activation_status, timestamp_activation_expire); +``` diff --git a/docs/powerauth-server/Readme.md b/docs/Readme.md similarity index 100% rename from docs/powerauth-server/Readme.md rename to docs/Readme.md diff --git a/docs/powerauth-server/Server-Error-Codes.md b/docs/Server-Error-Codes.md similarity index 100% rename from docs/powerauth-server/Server-Error-Codes.md rename to docs/Server-Error-Codes.md diff --git a/docs/Setting-Up-Active-Directory-Authentication.md b/docs/Setting-Up-Active-Directory-Authentication.md new file mode 100644 index 000000000..0389727eb --- /dev/null +++ b/docs/Setting-Up-Active-Directory-Authentication.md @@ -0,0 +1,38 @@ +# Setting Up Active Directory Authentication + +PowerAuth Admin supports optional authentication using Active Directory. This option is disabled by default. + + +In case you are using Linux/Unix LDAP implementation, please follow the [separate documentation](./Setting-Up-LDAP-Authentication.md). + + +## Using Custom Active Directory Properties + +In case you already have your own Active Directory server and you would like to use it as an authentication provider for PowerAuth Admin, you can configure all required properties. Namely, these properties are available for configuration: + +```sh +# Enable Active Directory Authentication +powerauth.admin.security.method=active-directory + +# Set Properties +powerauth.admin.security.activeDirectory.domain=wultra.com +powerauth.admin.security.activeDirectory.url=ldap://1.2.3.4:389 +powerauth.admin.security.activeDirectory.root=dc=wultra,dc=com +powerauth.admin.security.activeDirectory.userSearchFilter= +``` + +These properties should be sufficient to configure all parameters required for an Active Directory based authentication. + +## Restricting Authentication to a Groups + +The default value for the `powerauth.admin.security.activeDirectory.userSearchFilter` property is: + +``` +(&(objectClass=user)(userPrincipalName={0})) +``` + +To customize the user lookup query, you can change the property to include custom value (the `{0}` will be replaced by the `username@domain`), for example: + +``` +powerauth.admin.security.activeDirectory.userSearchFilter=(&(objectClass=user)(userPrincipalName={0})(memberOf=CN=MyCustomGroup,CN=Users,DC=wultra,DC=com)) +``` diff --git a/docs/powerauth-admin/Setting-Up-LDAP-Authentication.md b/docs/Setting-Up-LDAP-Authentication.md similarity index 91% rename from docs/powerauth-admin/Setting-Up-LDAP-Authentication.md rename to docs/Setting-Up-LDAP-Authentication.md index 1565060ac..3f9d45752 100644 --- a/docs/powerauth-admin/Setting-Up-LDAP-Authentication.md +++ b/docs/Setting-Up-LDAP-Authentication.md @@ -1,6 +1,10 @@ # Setting Up LDAP Authentication -PowerAuth Admin supports optional authentication using the LDAP protocol. This option is disabled by default, but we recommend setting up LDAP based authentication at least for the production environment. +PowerAuth Admin supports optional authentication using the LDAP protocol. This option is disabled by default. In case you are using PowerAuth Admin in a multi-user account, we recommend setting up at least the file-based LDAP authentication. + + +In case you are using Active Directory, please follow the [separate documentation](./Setting-Up-Active-Directory-Authentication.md). + ## Enabling LDAP Authentication diff --git a/docs/powerauth-server/System-Requirements.md b/docs/System-Requirements.md similarity index 100% rename from docs/powerauth-server/System-Requirements.md rename to docs/System-Requirements.md diff --git a/docs/powerauth-server/Using-HashiCorp-Vault.md b/docs/Using-HashiCorp-Vault.md similarity index 100% rename from docs/powerauth-server/Using-HashiCorp-Vault.md rename to docs/Using-HashiCorp-Vault.md diff --git a/docs/powerauth-server/WebServices-Client.md b/docs/WebServices-Client.md similarity index 100% rename from docs/powerauth-server/WebServices-Client.md rename to docs/WebServices-Client.md diff --git a/docs/powerauth-server/WebServices-Method-Compatibility.md b/docs/WebServices-Method-Compatibility.md similarity index 100% rename from docs/powerauth-server/WebServices-Method-Compatibility.md rename to docs/WebServices-Method-Compatibility.md diff --git a/docs/powerauth-server/WebServices-Methods.md b/docs/WebServices-Methods.md similarity index 98% rename from docs/powerauth-server/WebServices-Methods.md rename to docs/WebServices-Methods.md index 588ca781f..34d068bed 100644 --- a/docs/powerauth-server/WebServices-Methods.md +++ b/docs/WebServices-Methods.md @@ -1994,6 +1994,7 @@ REST endpoint: `POST /rest/v3/operation/create` | `Date` | `timestampCreated` | Timestamp of when the operation was created | | `Date` | `timestampExpires` | Timestamp of when the operation will expires / expired | | `Date` | `timestampFinalized` | Timestamp of when the operation was switched to a terminating status | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | ### Method 'operationDetail' @@ -2029,6 +2030,7 @@ REST endpoint: `POST /rest/v3/operation/detail` | `Date` | `timestampCreated` | Timestamp of when the operation was created | | `Date` | `timestampExpires` | Timestamp of when the operation will expires / expired | | `Date` | `timestampFinalized` | Timestamp of when the operation was switched to a terminating status | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | ### Method 'findPendingOperationsForUser' @@ -2068,6 +2070,7 @@ A collection of records with the following structure: | `Date` | `timestampCreated` | Timestamp of when the operation was created | | `Date` | `timestampExpires` | Timestamp of when the operation will expires / expired | | `Date` | `timestampFinalized` | Timestamp of when the operation was switched to a terminating status | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | ### Method 'findAllOperationsForUser' @@ -2107,6 +2110,7 @@ A collection of records with the following structure: | `Date` | `timestampCreated` | Timestamp of when the operation was created | | `Date` | `timestampExpires` | Timestamp of when the operation will expires / expired | | `Date` | `timestampFinalized` | Timestamp of when the operation was switched to a terminating status | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | ### Method 'findAllOperationsByExternalID' @@ -2146,6 +2150,7 @@ A collection of records with the following structure: | `Date` | `timestampCreated` | Timestamp of when the operation was created | | `Date` | `timestampExpires` | Timestamp of when the operation will expires / expired | | `Date` | `timestampFinalized` | Timestamp of when the operation was switched to a terminating status | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | ### Method 'cancelOperation' @@ -2181,6 +2186,7 @@ REST endpoint: `POST /rest/v3/operation/cancel` | `Date` | `timestampCreated` | Timestamp of when the operation was created | | `Date` | `timestampExpires` | Timestamp of when the operation will expires / expired | | `Date` | `timestampFinalized` | Timestamp of when the operation was switched to a terminating status | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | ### Method 'approveOperation' @@ -2228,6 +2234,7 @@ REST endpoint: `POST /rest/v3/operation/approve` | `Date` | `timestampCreated` | Timestamp of when the operation was created | | `Date` | `timestampExpires` | Timestamp of when the operation will expires / expired | | `Date` | `timestampFinalized` | Timestamp of when the operation was switched to a terminating status | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | ### Method 'failApproveOperation' @@ -2271,6 +2278,7 @@ REST endpoint: `POST /rest/v3/operation/approve/fail` | `Date` | `timestampCreated` | Timestamp of when the operation was created | | `Date` | `timestampExpires` | Timestamp of when the operation will expires / expired | | `Date` | `timestampFinalized` | Timestamp of when the operation was switched to a terminating status | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | ### Method 'rejectOperation' @@ -2316,6 +2324,7 @@ REST endpoint: `POST /rest/v3/operation/reject` | `Date` | `timestampCreated` | Timestamp of when the operation was created | | `Date` | `timestampExpires` | Timestamp of when the operation will expires / expired | | `Date` | `timestampFinalized` | Timestamp of when the operation was switched to a terminating status | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | ## Operation Templates @@ -2337,6 +2346,7 @@ REST endpoint: `POST /rest/v3/operation/template/create` | `List` | `signatureType` | Allowed signature types | | `Long` | `maxFailureCount` | How many failed attempts should be allowed for th operation | | `Long` | `expiration` | Operation expiration period in seconds | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | #### Response @@ -2351,6 +2361,7 @@ REST endpoint: `POST /rest/v3/operation/template/create` | `List` | `signatureType` | Allowed signature types | | `Long` | `maxFailureCount` | How many failed attempts should be allowed for th operation | | `Long` | `expiration` | Operation expiration period in seconds | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | ### Method 'getAllTemplates' @@ -2377,6 +2388,7 @@ Collection of items with the following structure: | `List` | `signatureType` | Allowed signature types | | `Long` | `maxFailureCount` | How many failed attempts should be allowed for th operation | | `Long` | `expiration` | Operation expiration period in seconds | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | ### Method 'getTemplateDetail' @@ -2405,6 +2417,7 @@ REST endpoint: `POST /rest/v3/operation/template/detail` | `List` | `signatureType` | Allowed signature types | | `Long` | `maxFailureCount` | How many failed attempts should be allowed for th operation | | `Long` | `expiration` | Operation expiration period in seconds | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | ### Method 'updateOperationTemplate' @@ -2424,6 +2437,7 @@ REST endpoint: `POST /rest/v3/operation/template/update` | `List` | `signatureType` | Allowed signature types | | `Long` | `maxFailureCount` | How many failed attempts should be allowed for th operation | | `Long` | `expiration` | Operation expiration period in seconds | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | #### Response @@ -2438,6 +2452,7 @@ REST endpoint: `POST /rest/v3/operation/template/update` | `List` | `signatureType` | Allowed signature types | | `Long` | `maxFailureCount` | How many failed attempts should be allowed for th operation | | `Long` | `expiration` | Operation expiration period in seconds | +| `String` | `riskFlags` | Risk flags for offline QR code. Uppercase letters without separator, e.g. `XFC`. | ### Method 'removeOperationTemplate' diff --git a/docs/powerauth-server/_Footer.md b/docs/_Footer.md similarity index 100% rename from docs/powerauth-server/_Footer.md rename to docs/_Footer.md diff --git a/docs/powerauth-server/_Sidebar.md b/docs/_Sidebar.md similarity index 69% rename from docs/powerauth-server/_Sidebar.md rename to docs/_Sidebar.md index 1320a5db2..07944aef0 100644 --- a/docs/powerauth-server/_Sidebar.md +++ b/docs/_Sidebar.md @@ -21,9 +21,18 @@ - [Database Structure](./Database-Structure.md) - [Error Codes](./Server-Error-Codes.md) +**PowerAuth Admin** + +- [Deploying PowerAuth Admin](./Deploying-PowerAuth-Admin.md) +- [Deploying PowerAuth Admin on JBoss/Wildfly](./Admin-Deploying-Wildfly.md) +- [Setting Up LDAP Authentication](./Setting-Up-LDAP-Authentication.md) +- [Setting Up AD Authentication](./Setting-Up-Active-Directory-Authentication.md) +- [Configuration of Activation Recovery](./Activation-Recovery.md) +- [Configuration Properties](./Configuration-Properties-Admin.md) + **Advanced Topics** - [Encrypting DB Records](./Encrypting-Records-in-Database.md) - [Offline Signatures](./Offline-Signatures.md) - [Integrating with HashiCorp Vault](./Using-HashiCorp-Vault.md) -- [Database Sizing](./Database-Sizing.md) \ No newline at end of file +- [Database Sizing](./Database-Sizing.md) diff --git a/docs/powerauth-server/images/arch_db_structure.png b/docs/images/arch_db_structure.png similarity index 100% rename from docs/powerauth-server/images/arch_db_structure.png rename to docs/images/arch_db_structure.png diff --git a/docs/powerauth-admin/Readme.md b/docs/powerauth-admin/Readme.md deleted file mode 100644 index d58f4b51e..000000000 --- a/docs/powerauth-admin/Readme.md +++ /dev/null @@ -1,17 +0,0 @@ -# PowerAuth Admin Documentation - -PowerAuth Admin is a web administration console for the [PowerAuth Server](https://github.com/wultra/powerauth-server). -It allows an easy application setup, an activation management and integration configurations. - - -## Deployment Tutorials - -- [Deploying PowerAuth Admin](./Deploying-PowerAuth-Admin.md) -- [Setting Up LDAP Authentication](./Setting-Up-LDAP-Authentication.md) -- [Deploying PowerAuth Admin on JBoss/Wildfly](./Admin-Deploying-Wildfly.md) -- [Configuration of Activation Recovery](./Activation-Recovery.md) - - -## License - -All sources are licensed using Apache 2.0 license, you can use them with no restriction. If you are using PowerAuth, please let us know. We will be happy to share and promote your project. diff --git a/docs/powerauth-admin/_Sidebar.md b/docs/powerauth-admin/_Sidebar.md deleted file mode 100644 index ff7623144..000000000 --- a/docs/powerauth-admin/_Sidebar.md +++ /dev/null @@ -1,11 +0,0 @@ -**Deployment Tutorials** - -- [Deploying PowerAuth Admin](./Deploying-PowerAuth-Admin.md) -- [Deploying PowerAuth Admin on JBoss/Wildfly](./Admin-Deploying-Wildfly.md) -- [Setting Up LDAP Authentication](./Setting-Up-LDAP-Authentication.md) -- [Configuration of Activation Recovery](./Activation-Recovery.md) -- [Configuration Properties](./Configuration-Properties.md) - -**Implementation Tutorials** - -- [Authentication in Mobile Banking Apps (SCA)](https://developers.wultra.com/products/mobile-security-suite/develop/tutorials/Authentication-in-Mobile-Apps) diff --git a/docs/powerauth-server/PowerAuth-Server-1.2.5.md b/docs/powerauth-server/PowerAuth-Server-1.2.5.md deleted file mode 100644 index fd5fee16a..000000000 --- a/docs/powerauth-server/PowerAuth-Server-1.2.5.md +++ /dev/null @@ -1,207 +0,0 @@ -# Migration from 1.2.x to 1.2.5 - -This guide contains instructions for migration from PowerAuth Server version `1.2.x` to version `1.2.5`. - -_Warning: release `1.2.5` of PowerAuth server requires application of database migration steps. Usually we do not require a database migration for minor releases, however in release `1.2.5` such migration is necessary._ - -## Create New Columns in Operation Table - -Create a new columns in the operations table: - -- `template_name` - Stores the original template name. -- `activation_flag` - Stores the activation flag that must be present on activation in order to return / approve / reject the operation. -- `additional_data` - Stores attributes related to the approval / rejection / cancellation event. - -### Oracle - -```sql -ALTER TABLE PA_OPERATION ADD TEMPLATE_NAME VARCHAR2(255); - -ALTER TABLE PA_OPERATION ADD ACTIVATION_FLAG VARCHAR2(255); - -ALTER TABLE PA_OPERATION ADD ADDITIONAL_DATA CLOB; -``` - -### PostgreSQL - -```sql -ALTER TABLE pa_operation ADD template_name VARCHAR(255); - -ALTER TABLE pa_operation ADD activation_flag VARCHAR(255); - -ALTER TABLE pa_operation ADD additional_data TEXT; -``` - -### MySQL - -```sql -ALTER TABLE pa_operation ADD template_name VARCHAR(255) NULL; - -ALTER TABLE pa_operation ADD activation_flag VARCHAR(255) NULL; - -ALTER TABLE pa_operation ADD additional_data TEXT NULL; -``` - -## Create Auditing Tables - -Create tables for auditing: -- `audit_log` - table used to store audit logs -- `audit_param` - table used to store detailed parameters for audit logs - -### Oracle - -```sql --- --- Create audit log table. --- -CREATE TABLE audit_log ( - audit_log_id VARCHAR2(36 CHAR) PRIMARY KEY, - application_name VARCHAR2(256 CHAR) NOT NULL, - audit_level VARCHAR2(32 CHAR) NOT NULL, - audit_type VARCHAR2(256 CHAR), - timestamp_created TIMESTAMP, - message CLOB NOT NULL, - exception_message CLOB, - stack_trace CLOB, - param CLOB, - calling_class VARCHAR2(256 CHAR) NOT NULL, - thread_name VARCHAR2(256 CHAR) NOT NULL, - version VARCHAR2(256 CHAR), - build_time TIMESTAMP -); - --- --- Create audit parameters table. --- -CREATE TABLE audit_param ( - audit_log_id VARCHAR2(36 CHAR), - timestamp_created TIMESTAMP, - param_key VARCHAR2(256 CHAR), - param_value VARCHAR2(4000 CHAR) -); - --- --- Create indexes. --- -CREATE INDEX audit_log_timestamp ON audit_log (timestamp_created); -CREATE INDEX audit_log_application ON audit_log (application_name); -CREATE INDEX audit_log_level ON audit_log (audit_level); -CREATE INDEX audit_log_type ON audit_log (audit_type); -CREATE INDEX audit_param_log ON audit_param (audit_log_id); -CREATE INDEX audit_param_timestamp ON audit_param (timestamp_created); -CREATE INDEX audit_param_key ON audit_param (param_key); -CREATE INDEX audit_param_value ON audit_param (param_value); -``` - -### PostgreSQL - -```sql --- --- Create audit log table. --- -CREATE TABLE audit_log ( - audit_log_id VARCHAR(36) PRIMARY KEY, - application_name VARCHAR(256) NOT NULL, - audit_level VARCHAR(32) NOT NULL, - audit_type VARCHAR(256), - timestamp_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - message TEXT NOT NULL, - exception_message TEXT, - stack_trace TEXT, - param TEXT, - calling_class VARCHAR(256) NOT NULL, - thread_name VARCHAR(256) NOT NULL, - version VARCHAR(256), - build_time TIMESTAMP -); - --- --- Create audit parameters table. --- -CREATE TABLE audit_param ( - audit_log_id VARCHAR(36), - timestamp_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - param_key VARCHAR(256), - param_value VARCHAR(4000) -); - --- --- Create indexes. --- -CREATE INDEX audit_log_timestamp ON audit_log (timestamp_created); -CREATE INDEX audit_log_application ON audit_log (application_name); -CREATE INDEX audit_log_level ON audit_log (audit_level); -CREATE INDEX audit_log_type ON audit_log (audit_type); -CREATE INDEX audit_param_log ON audit_param (audit_log_id); -CREATE INDEX audit_param_timestamp ON audit_param (timestamp_created); -CREATE INDEX audit_param_key ON audit_param (param_key); -CREATE INDEX audit_param_value ON audit_param (param_value); -``` - -### MySQL - -```sql --- --- Create audit log table. --- -CREATE TABLE audit_log ( - audit_log_id VARCHAR(36) PRIMARY KEY, - application_name VARCHAR(256) NOT NULL, - audit_level VARCHAR(32) NOT NULL, - audit_type VARCHAR(256), - timestamp_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - message TEXT NOT NULL, - exception_message TEXT, - stack_trace TEXT, - param TEXT, - calling_class VARCHAR(256) NOT NULL, - thread_name VARCHAR(256) NOT NULL, - version VARCHAR(256), - build_time TIMESTAMP NULL -) ENGINE=InnoDB AUTO_INCREMENT=1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- --- Create audit parameters table. --- -CREATE TABLE audit_param ( - audit_log_id VARCHAR(36), - timestamp_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - param_key VARCHAR(256), - param_value VARCHAR(3072) -) ENGINE=InnoDB AUTO_INCREMENT=1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- --- Create indexes. --- -CREATE INDEX audit_log_timestamp ON audit_log (timestamp_created); -CREATE INDEX audit_log_application ON audit_log (application_name); -CREATE INDEX audit_log_level ON audit_log (audit_level); -CREATE INDEX audit_log_type ON audit_log (audit_type); -CREATE INDEX audit_param_log ON audit_param (audit_log_id); -CREATE INDEX audit_param_timestamp ON audit_param (timestamp_created); -CREATE INDEX audit_param_key ON audit_param (param_key); -CREATE FULLTEXT INDEX audit_param_value ON audit_param (param_value); -``` - - -## Create New Column in Activation History Table - -The `pa_activation_history` table was updated to include activation version. - -### Oracle - -```sql -ALTER TABLE PA_ACTIVATION_HISTORY ADD activation_version NUMBER(2,0); -``` - -### PostgreSQL - -```sql -ALTER TABLE pa_activation_history ADD activation_version INTEGER; -``` - -### MySQL - -```sql -ALTER TABLE pa_activation_history ADD activation_version int(2); -``` diff --git a/docs/powerauth-server/sql/mysql/create_schema.sql b/docs/sql/mysql/create_schema.sql similarity index 72% rename from docs/powerauth-server/sql/mysql/create_schema.sql rename to docs/sql/mysql/create_schema.sql index 86679e8dd..ce157165e 100644 --- a/docs/powerauth-server/sql/mysql/create_schema.sql +++ b/docs/sql/mysql/create_schema.sql @@ -227,8 +227,8 @@ CREATE TABLE pa_operation ( timestamp_created datetime NOT NULL, timestamp_expires datetime NOT NULL, timestamp_finalized datetime NULL, - PRIMARY KEY (id), - CONSTRAINT `FK_OPERATION_APPLICATION` FOREIGN KEY (`application_id`) REFERENCES `pa_application` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION + risk_flags varchar(255), + PRIMARY KEY (id) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- @@ -242,6 +242,7 @@ CREATE TABLE pa_operation_template ( signature_type varchar(255) NOT NULL, max_failure_count bigint(20) NOT NULL, expiration bigint(20) NOT NULL, + risk_flags varchar(255), PRIMARY KEY (id) ) ENGINE=InnoDB AUTO_INCREMENT=1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; @@ -257,7 +258,7 @@ CREATE TABLE pa_operation_application ( -- -- DDL for Table SHEDLOCK -- -CREATE TABLE shedlock ( +CREATE TABLE IF NOT EXISTS shedlock ( name VARCHAR(64) NOT NULL, lock_until TIMESTAMP(3) NOT NULL, locked_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), @@ -265,36 +266,101 @@ CREATE TABLE shedlock ( PRIMARY KEY (name) ) ENGINE=InnoDB AUTO_INCREMENT=1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +-- +-- Create audit log table. +-- +CREATE TABLE IF NOT EXISTS audit_log ( + audit_log_id VARCHAR(36) PRIMARY KEY, + application_name VARCHAR(256) NOT NULL, + audit_level VARCHAR(32) NOT NULL, + audit_type VARCHAR(256), + timestamp_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + message TEXT NOT NULL, + exception_message TEXT, + stack_trace TEXT, + param TEXT, + calling_class VARCHAR(256) NOT NULL, + thread_name VARCHAR(256) NOT NULL, + version VARCHAR(256), + build_time TIMESTAMP NULL +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- +-- Create audit parameters table. +-- +CREATE TABLE IF NOT EXISTS audit_param ( + audit_log_id VARCHAR(36), + timestamp_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + param_key VARCHAR(256), + param_value VARCHAR(3072) +) ENGINE=InnoDB AUTO_INCREMENT=1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + -- -- Indexes for better performance. InnoDB engine creates indexes on foreign keys automatically, so they are not included. -- -CREATE INDEX `pa_activation_code` ON `pa_activation`(`activation_code`); +CREATE INDEX pa_activation_application ON pa_activation(application_id); + +CREATE INDEX pa_activation_keypair ON pa_activation(master_keypair_id); + +CREATE INDEX pa_activation_code ON pa_activation(activation_code); + +CREATE INDEX pa_activation_user_id ON pa_activation(user_id); + +CREATE INDEX pa_activation_expiration on pa_activation (activation_status, timestamp_activation_expire); -CREATE INDEX `pa_activation_user_id` ON `pa_activation`(`user_id`); +CREATE INDEX pa_activation_history_act ON pa_activation_history(activation_id); -CREATE INDEX `pa_activation_history_created` ON `pa_activation_history`(`timestamp_created`); +CREATE INDEX pa_activation_history_created ON pa_activation_history(timestamp_created); -CREATE UNIQUE INDEX `pa_app_version_app_key` ON `pa_application_version`(`application_key`); +CREATE INDEX pa_application_version_app ON pa_application_version(application_id); -CREATE INDEX `pa_app_callback_app` ON `pa_application_callback`(`application_id`); +CREATE INDEX pa_master_keypair_application ON pa_master_keypair(application_id); -CREATE UNIQUE INDEX `pa_integration_token` ON `pa_integration`(`client_token`); +CREATE UNIQUE INDEX pa_app_version_app_key ON pa_application_version(application_key); -CREATE INDEX `pa_signature_audit_created` ON `pa_signature_audit`(`timestamp_created`); +CREATE INDEX pa_app_callback_app ON pa_application_callback(application_id); -CREATE INDEX `pa_recovery_code` ON `pa_recovery_code`(`recovery_code`); +CREATE UNIQUE INDEX pa_integration_token ON pa_integration(client_token); -CREATE INDEX `pa_recovery_code_user` ON `pa_recovery_code`(`user_id`); +CREATE INDEX pa_signature_audit_activation ON pa_signature_audit(activation_id); -CREATE INDEX `pa_operation_user` ON `pa_operation`(`user_id`); +CREATE INDEX pa_signature_audit_created ON pa_signature_audit(timestamp_created); -CREATE INDEX `pa_operation_ts_created_idx` ON `pa_operation`(`timestamp_created`); +CREATE INDEX pa_token_activation ON pa_token(activation_id); -CREATE INDEX `pa_operation_ts_expires_idx` ON `pa_operation`(`timestamp_expires`); +CREATE INDEX pa_recovery_code_code ON pa_recovery_code(recovery_code); -CREATE INDEX `pa_operation_template_name_idx` ON `pa_operation_template` (`template_name`); +CREATE INDEX pa_recovery_code_app ON pa_recovery_code(application_id); -CREATE UNIQUE INDEX `pa_recovery_code_puk` ON `pa_recovery_puk`(`recovery_code_id`, `puk_index`); +CREATE INDEX pa_recovery_code_user ON pa_recovery_code(user_id); -CREATE UNIQUE INDEX `pa_application_name` ON `pa_application`(`name`); +CREATE INDEX pa_recovery_code_act ON pa_recovery_code(activation_id); + +CREATE UNIQUE INDEX pa_recovery_code_puk ON pa_recovery_puk(recovery_code_id, puk_index); + +CREATE INDEX pa_recovery_puk_code ON pa_recovery_puk(recovery_code_id); + +CREATE UNIQUE INDEX pa_recovery_config_app ON pa_recovery_config(application_id); + +CREATE UNIQUE INDEX pa_application_name ON pa_application(name); + +CREATE INDEX pa_operation_user ON pa_operation(user_id); + +CREATE INDEX pa_operation_ts_created_idx ON pa_operation(timestamp_created); + +CREATE INDEX pa_operation_ts_expires_idx ON pa_operation(timestamp_expires); + +CREATE INDEX pa_operation_template_name_idx ON pa_operation_template(template_name); + +-- +-- Auditing indexes. +-- +CREATE INDEX IF NOT EXISTS audit_log_timestamp ON audit_log (timestamp_created); +CREATE INDEX IF NOT EXISTS audit_log_application ON audit_log (application_name); +CREATE INDEX IF NOT EXISTS audit_log_level ON audit_log (audit_level); +CREATE INDEX IF NOT EXISTS audit_log_type ON audit_log (audit_type); +CREATE INDEX IF NOT EXISTS audit_param_log ON audit_param (audit_log_id); +CREATE INDEX IF NOT EXISTS audit_param_timestamp ON audit_param (timestamp_created); +CREATE INDEX IF NOT EXISTS audit_param_key ON audit_param (param_key); +CREATE FULLTEXT INDEX audit_param_value ON audit_param (param_value); \ No newline at end of file diff --git a/docs/powerauth-server/sql/mysql/delete_schema.sql b/docs/sql/mysql/delete_schema.sql similarity index 100% rename from docs/powerauth-server/sql/mysql/delete_schema.sql rename to docs/sql/mysql/delete_schema.sql diff --git a/docs/powerauth-server/sql/mysql/schema_boot.sql b/docs/sql/mysql/schema_boot.sql similarity index 100% rename from docs/powerauth-server/sql/mysql/schema_boot.sql rename to docs/sql/mysql/schema_boot.sql diff --git a/docs/powerauth-server/sql/oracle/create_schema.sql b/docs/sql/oracle/create_schema.sql similarity index 83% rename from docs/powerauth-server/sql/oracle/create_schema.sql rename to docs/sql/oracle/create_schema.sql index 3a1fcb2e9..0b713e0fb 100755 --- a/docs/powerauth-server/sql/oracle/create_schema.sql +++ b/docs/sql/oracle/create_schema.sql @@ -222,7 +222,8 @@ CREATE TABLE "PA_OPERATION" ( "MAX_FAILURE_COUNT" NUMBER(19,0) NOT NULL, "TIMESTAMP_CREATED" TIMESTAMP(6) NOT NULL, "TIMESTAMP_EXPIRES" TIMESTAMP(6) NOT NULL, - "TIMESTAMP_FINALIZED" TIMESTAMP(6) + "TIMESTAMP_FINALIZED" TIMESTAMP(6), + "RISK_FLAGS" VARCHAR2(255 CHAR) ); -- @@ -235,7 +236,8 @@ CREATE TABLE "PA_OPERATION_TEMPLATE" ( "DATA_TEMPLATE" VARCHAR2(255 CHAR) NOT NULL, "SIGNATURE_TYPE" VARCHAR2(255 CHAR) NOT NULL, "MAX_FAILURE_COUNT" NUMBER(19,0) NOT NULL, - "EXPIRATION" NUMBER(19,0) NOT NULL + "EXPIRATION" NUMBER(19,0) NOT NULL, + "RISK_FLAGS" VARCHAR2(255 CHAR) ); -- @@ -250,12 +252,47 @@ CREATE TABLE pa_operation_application ( -- -- DDL for Table SHEDLOCK -- -CREATE TABLE shedlock ( +BEGIN EXECUTE IMMEDIATE 'CREATE TABLE shedlock ( name VARCHAR(64) NOT NULL PRIMARY KEY, lock_until TIMESTAMP(3) NOT NULL, locked_at TIMESTAMP(3) NOT NULL, locked_by VARCHAR(255) NOT NULL -); +)'; +EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END; +/ + +-- +-- Create audit log table. +-- +BEGIN EXECUTE IMMEDIATE 'CREATE TABLE audit_log ( + audit_log_id VARCHAR2(36 CHAR) PRIMARY KEY, + application_name VARCHAR2(256 CHAR) NOT NULL, + audit_level VARCHAR2(32 CHAR) NOT NULL, + audit_type VARCHAR2(256 CHAR), + timestamp_created TIMESTAMP, + message CLOB NOT NULL, + exception_message CLOB, + stack_trace CLOB, + param CLOB, + calling_class VARCHAR2(256 CHAR) NOT NULL, + thread_name VARCHAR2(256 CHAR) NOT NULL, + version VARCHAR2(256 CHAR), + build_time TIMESTAMP +)'; +EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END; +/ + +-- +-- Create audit parameters table. +-- +BEGIN EXECUTE IMMEDIATE 'CREATE TABLE audit_param ( + audit_log_id VARCHAR2(36 CHAR), + timestamp_created TIMESTAMP, + param_key VARCHAR2(256 CHAR), + param_value VARCHAR2(4000 CHAR) +)'; +EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END; +/ -- -- Ref Constraints for Table PA_ACTIVATION @@ -309,11 +346,6 @@ ALTER TABLE "PA_RECOVERY_PUK" ADD CONSTRAINT "RECOVERY_PUK_CODE_FK" FOREIGN KEY -- ALTER TABLE "PA_RECOVERY_CONFIG" ADD CONSTRAINT "RECOVERY_CONFIG_APP_FK" FOREIGN KEY ("APPLICATION_ID") REFERENCES "PA_APPLICATION" ("ID") ENABLE; --- --- Ref Constraints for Table PA_OPERATION --- -ALTER TABLE "PA_OPERATION" ADD CONSTRAINT "OPERATION_APPLICATION_FK" FOREIGN KEY ("APPLICATION_ID") REFERENCES "PA_APPLICATION" ("ID") ENABLE; - --- --- Indexes for better performance. Oracle does not create indexes on foreign key automatically. @@ -327,6 +359,8 @@ CREATE INDEX PA_ACTIVATION_CODE ON PA_ACTIVATION(ACTIVATION_CODE); CREATE INDEX PA_ACTIVATION_USER_ID ON PA_ACTIVATION(USER_ID); +CREATE INDEX PA_ACTIVATION_EXPIRATION ON PA_ACTIVATION (ACTIVATION_STATUS, TIMESTAMP_ACTIVATION_EXPIRE); + CREATE INDEX PA_ACTIVATION_HISTORY_ACT ON PA_ACTIVATION_HISTORY(ACTIVATION_ID); CREATE INDEX PA_ACTIVATION_HISTORY_CREATED ON PA_ACTIVATION_HISTORY(TIMESTAMP_CREATED); @@ -370,3 +404,38 @@ CREATE INDEX PA_OPERATION_TS_CREATED_IDX ON PA_OPERATION(TIMESTAMP_CREATED); CREATE INDEX PA_OPERATION_TS_EXPIRES_IDX ON PA_OPERATION(TIMESTAMP_EXPIRES); CREATE INDEX PA_OPERATION_TEMPLATE_NAME_IDX ON PA_OPERATION_TEMPLATE(TEMPLATE_NAME); + +-- +-- Auditing indexes. +-- +BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_log_timestamp ON audit_log (timestamp_created)'; +EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END; +/ + +BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_log_application ON audit_log (application_name)'; +EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END; +/ + +BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_log_level ON audit_log (audit_level)'; +EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END; +/ + +BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_log_type ON audit_log (audit_type)'; +EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END; +/ + +BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_param_log ON audit_param (audit_log_id)'; +EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END; +/ + +BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_param_timestamp ON audit_param (timestamp_created)'; +EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END; +/ + +BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_param_key ON audit_param (param_key)'; +EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END; +/ + +BEGIN EXECUTE IMMEDIATE 'CREATE INDEX audit_param_value ON audit_param (param_value)'; +EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END; +/ diff --git a/docs/powerauth-server/sql/oracle/delete_schema.sql b/docs/sql/oracle/delete_schema.sql similarity index 100% rename from docs/powerauth-server/sql/oracle/delete_schema.sql rename to docs/sql/oracle/delete_schema.sql diff --git a/docs/powerauth-server/sql/postgresql/create_schema.sql b/docs/sql/postgresql/create_schema.sql similarity index 74% rename from docs/powerauth-server/sql/postgresql/create_schema.sql rename to docs/sql/postgresql/create_schema.sql index 724a7503c..c39b93eaa 100644 --- a/docs/powerauth-server/sql/postgresql/create_schema.sql +++ b/docs/sql/postgresql/create_schema.sql @@ -223,7 +223,8 @@ CREATE TABLE pa_operation ( max_failure_count BIGINT NOT NULL, timestamp_created TIMESTAMP NOT NULL, timestamp_expires TIMESTAMP NOT NULL, - timestamp_finalized TIMESTAMP + timestamp_finalized TIMESTAMP, + risk_flags VARCHAR(255) ); -- @@ -236,7 +237,8 @@ CREATE TABLE pa_operation_template ( data_template VARCHAR(255) NOT NULL, signature_type VARCHAR(255) NOT NULL, max_failure_count BIGINT NOT NULL, - expiration BIGINT NOT NULL + expiration BIGINT NOT NULL, + risk_flags VARCHAR(255) ); -- @@ -251,13 +253,42 @@ CREATE TABLE pa_operation_application ( -- -- DDL for Table SHEDLOCK -- -CREATE TABLE shedlock ( +CREATE TABLE IF NOT EXISTS shedlock ( name VARCHAR(64) NOT NULL PRIMARY KEY, lock_until TIMESTAMP NOT NULL, locked_at TIMESTAMP NOT NULL, locked_by VARCHAR(255) NOT NULL ); +-- +-- Create audit log table. +-- +CREATE TABLE IF NOT EXISTS audit_log ( + audit_log_id VARCHAR(36) PRIMARY KEY, + application_name VARCHAR(256) NOT NULL, + audit_level VARCHAR(32) NOT NULL, + audit_type VARCHAR(256), + timestamp_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + message TEXT NOT NULL, + exception_message TEXT, + stack_trace TEXT, + param TEXT, + calling_class VARCHAR(256) NOT NULL, + thread_name VARCHAR(256) NOT NULL, + version VARCHAR(256), + build_time TIMESTAMP +); + +-- +-- Create audit parameters table. +-- +CREATE TABLE IF NOT EXISTS audit_param ( + audit_log_id VARCHAR(36), + timestamp_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + param_key VARCHAR(256), + param_value VARCHAR(4000) +); + -- -- Ref Constraints for Table PA_ACTIVATION -- @@ -310,64 +341,73 @@ ALTER TABLE pa_recovery_puk ADD CONSTRAINT recovery_puk_code_fk FOREIGN KEY (rec -- ALTER TABLE pa_recovery_config ADD CONSTRAINT recovery_config_app_fk FOREIGN KEY (application_id) REFERENCES pa_application (id); --- --- Ref Constraints for Table PA_OPERATION --- -ALTER TABLE pa_operation ADD CONSTRAINT operation_application_fk FOREIGN KEY (application_id) REFERENCES pa_application (id); - --- ---- Indexes for better performance. PostgreSQL does not create indexes on foreign key automatically. +--- Indexes for better performance. PostgreSQL does not CREATE INDEXes ON foreign key automatically. --- -CREATE INDEX PA_ACTIVATION_APPLICATION ON PA_ACTIVATION(APPLICATION_ID); +CREATE INDEX pa_activation_application ON pa_activation(application_id); + +CREATE INDEX pa_activation_keypair ON pa_activation(master_keypair_id); -CREATE INDEX PA_ACTIVATION_KEYPAIR ON PA_ACTIVATION(MASTER_KEYPAIR_ID); +CREATE INDEX pa_activation_code ON pa_activation(activation_code); -CREATE INDEX PA_ACTIVATION_CODE ON PA_ACTIVATION(ACTIVATION_CODE); +CREATE INDEX pa_activation_user_id ON pa_activation(user_id); -CREATE INDEX PA_ACTIVATION_USER_ID ON PA_ACTIVATION(USER_ID); +CREATE INDEX pa_activation_expiration on pa_activation (activation_status, timestamp_activation_expire); -CREATE INDEX PA_ACTIVATION_HISTORY_ACT ON PA_ACTIVATION_HISTORY(ACTIVATION_ID); +CREATE INDEX pa_activation_history_act ON pa_activation_history(activation_id); -CREATE INDEX PA_ACTIVATION_HISTORY_CREATED ON PA_ACTIVATION_HISTORY(TIMESTAMP_CREATED); +CREATE INDEX pa_activation_history_created ON pa_activation_history(timestamp_created); -CREATE INDEX PA_APPLICATION_VERSION_APP ON PA_APPLICATION_VERSION(APPLICATION_ID); +CREATE INDEX pa_application_version_app ON pa_application_version(application_id); -CREATE INDEX PA_MASTER_KEYPAIR_APPLICATION ON PA_MASTER_KEYPAIR(APPLICATION_ID); +CREATE INDEX pa_master_keypair_application ON pa_master_keypair(application_id); -CREATE UNIQUE INDEX PA_APP_VERSION_APP_KEY ON PA_APPLICATION_VERSION(APPLICATION_KEY); +CREATE UNIQUE INDEX pa_app_version_app_key ON pa_application_version(application_key); -CREATE INDEX PA_APP_CALLBACK_APP ON PA_APPLICATION_CALLBACK(APPLICATION_ID); +CREATE INDEX pa_app_callback_app ON pa_application_callback(application_id); -CREATE UNIQUE INDEX PA_INTEGRATION_TOKEN ON PA_INTEGRATION(CLIENT_TOKEN); +CREATE UNIQUE INDEX pa_integration_token ON pa_integration(client_token); -CREATE INDEX PA_SIGNATURE_AUDIT_ACTIVATION ON PA_SIGNATURE_AUDIT(ACTIVATION_ID); +CREATE INDEX pa_signature_audit_activation ON pa_signature_audit(activation_id); -CREATE INDEX PA_SIGNATURE_AUDIT_CREATED ON PA_SIGNATURE_AUDIT(TIMESTAMP_CREATED); +CREATE INDEX pa_signature_audit_created ON pa_signature_audit(timestamp_created); -CREATE INDEX PA_TOKEN_ACTIVATION ON PA_TOKEN(ACTIVATION_ID); +CREATE INDEX pa_token_activation ON pa_token(activation_id); -CREATE INDEX PA_RECOVERY_CODE_CODE ON PA_RECOVERY_CODE(RECOVERY_CODE); +CREATE INDEX pa_recovery_code_code ON pa_recovery_code(recovery_code); -CREATE INDEX PA_RECOVERY_CODE_APP ON PA_RECOVERY_CODE(APPLICATION_ID); +CREATE INDEX pa_recovery_code_app ON pa_recovery_code(application_id); -CREATE INDEX PA_RECOVERY_CODE_USER ON PA_RECOVERY_CODE(USER_ID); +CREATE INDEX pa_recovery_code_user ON pa_recovery_code(user_id); -CREATE INDEX PA_RECOVERY_CODE_ACT ON PA_RECOVERY_CODE(ACTIVATION_ID); +CREATE INDEX pa_recovery_code_act ON pa_recovery_code(activation_id); -CREATE UNIQUE INDEX PA_RECOVERY_CODE_PUK ON PA_RECOVERY_PUK(RECOVERY_CODE_ID, PUK_INDEX); +CREATE UNIQUE INDEX pa_recovery_code_puk ON pa_recovery_puk(recovery_code_id, puk_index); -CREATE INDEX PA_RECOVERY_PUK_CODE ON PA_RECOVERY_PUK(RECOVERY_CODE_ID); +CREATE INDEX pa_recovery_puk_code ON pa_recovery_puk(recovery_code_id); -CREATE UNIQUE INDEX PA_RECOVERY_CONFIG_APP ON PA_RECOVERY_CONFIG(APPLICATION_ID); +CREATE UNIQUE INDEX pa_recovery_config_app ON pa_recovery_config(application_id); -CREATE UNIQUE INDEX PA_APPLICATION_NAME ON PA_APPLICATION(NAME); +CREATE UNIQUE INDEX pa_application_name ON pa_application(name); -CREATE INDEX PA_OPERATION_USER ON PA_OPERATION(USER_ID); +CREATE INDEX pa_operation_user ON pa_operation(user_id); -CREATE INDEX PA_OPERATION_TS_CREATED_IDX ON PA_OPERATION(TIMESTAMP_CREATED); +CREATE INDEX pa_operation_ts_created_idx ON pa_operation(timestamp_created); -CREATE INDEX PA_OPERATION_TS_EXPIRES_IDX ON PA_OPERATION(TIMESTAMP_EXPIRES); +CREATE INDEX pa_operation_ts_expires_idx ON pa_operation(timestamp_expires); -CREATE INDEX PA_OPERATION_TEMPLATE_NAME_IDX ON PA_OPERATION_TEMPLATE(TEMPLATE_NAME); +CREATE INDEX pa_operation_template_name_idx ON pa_operation_template(template_name); + +-- +-- Auditing indexes. +-- +CREATE INDEX IF NOT EXISTS audit_log_timestamp ON audit_log (timestamp_created); +CREATE INDEX IF NOT EXISTS audit_log_application ON audit_log (application_name); +CREATE INDEX IF NOT EXISTS audit_log_level ON audit_log (audit_level); +CREATE INDEX IF NOT EXISTS audit_log_type ON audit_log (audit_type); +CREATE INDEX IF NOT EXISTS audit_param_log ON audit_param (audit_log_id); +CREATE INDEX IF NOT EXISTS audit_param_timestamp ON audit_param (timestamp_created); +CREATE INDEX IF NOT EXISTS audit_param_key ON audit_param (param_key); +CREATE INDEX IF NOT EXISTS audit_param_value ON audit_param (param_value); diff --git a/docs/powerauth-server/sql/postgresql/delete_schema.sql b/docs/sql/postgresql/delete_schema.sql similarity index 100% rename from docs/powerauth-server/sql/postgresql/delete_schema.sql rename to docs/sql/postgresql/delete_schema.sql diff --git a/docs/powerauth-server/util/check-bc.jar b/docs/util/check-bc.jar similarity index 100% rename from docs/powerauth-server/util/check-bc.jar rename to docs/util/check-bc.jar diff --git a/lombok.config b/lombok.config new file mode 100644 index 000000000..2bb794fd6 --- /dev/null +++ b/lombok.config @@ -0,0 +1 @@ +lombok.log.fieldName=logger diff --git a/pom.xml b/pom.xml index 1075fa58a..3b7388302 100644 --- a/pom.xml +++ b/pom.xml @@ -26,13 +26,13 @@ io.getlime.security powerauth-server-parent - 1.3.0 + 1.4.0 pom org.springframework.boot spring-boot-starter-parent - 2.6.8 + 2.6.14 @@ -85,19 +85,19 @@ 1.8 1.8 - 3.2.2 - 3.0.0-M2 - 3.4.0 - 3.3.2 + 3.3.0 + 3.0.0 + 3.4.1 + 3.1.0 3.1.1 - 1.3.0 - 1.5.1 - 1.5.1 - 1.70 + 1.4.0 + 1.6.0 + 1.6.0 + 1.72 2.3.1 @@ -112,18 +112,34 @@ 1.6.9 - 4.36.0 + 4.43.0 5.8.2 - 2.1.212 + 2.1.214 - 2.13.3 - 1.9 + 1.10.0 + + + + + io.getlime.security + powerauth-client-model + ${project.version} + + + + io.getlime.security + powerauth-rest-client-spring + ${project.version} + + + + @@ -139,16 +155,6 @@ - - org.apache.maven.plugins - maven-jar-plugin - ${maven-jar-plugin.version} - - - org.apache.maven.plugins - maven-war-plugin - ${maven-war-plugin.version} - org.apache.maven.plugins maven-javadoc-plugin @@ -170,10 +176,30 @@ + org.apache.maven.plugins - maven-deploy-plugin - ${maven-deploy-plugin.version} + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-banned-dependencies + + enforce + + + + + + org.apache.tomcat.embed:*:*:*:compile + org.bouncycastle:bcpkix-jdk15on:*:*:compile + org.bouncycastle:bcprov-jdk15on:*:*:compile + + + + + + @@ -204,6 +230,66 @@ + + internal-repository + + + useInternalRepo + true + + + + + + + jfrog-central + Wultra Artifactory-releases + https://wultra.jfrog.io/artifactory/internal-maven-repository + + + jfrog-central + Wultra Artifactory-snapshots + https://wultra.jfrog.io/artifactory/internal-maven-repository + + + + + jfrog-central + Wultra Artifactory-releases + https://wultra.jfrog.io/artifactory/internal-maven-repository + + + ossrh-snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + false + + + true + + + + + + public-repository + + + !useInternalRepo + + + + + + + ossrh-snapshots-distribution + https://oss.sonatype.org/content/repositories/snapshots/ + + + ossrh-staging-distribution + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + @@ -219,15 +305,5 @@ - - - ossrh-snapshots-distribution - https://oss.sonatype.org/content/repositories/snapshots/ - - - ossrh-staging-distribution - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - diff --git a/powerauth-admin/pom.xml b/powerauth-admin/pom.xml index a4ea382ef..eb1e79f3b 100644 --- a/powerauth-admin/pom.xml +++ b/powerauth-admin/pom.xml @@ -6,14 +6,12 @@ powerauth-admin PowerAuth Server Admin Console powerauth-admin - 1.3.0 war io.getlime.security powerauth-server-parent - 1.3.0 - ../pom.xml + 1.4.0 @@ -37,6 +35,11 @@ org.springframework.boot spring-boot-starter-actuator + + org.springframework.boot + spring-boot-starter-tomcat + provided + @@ -56,7 +59,6 @@ io.getlime.security powerauth-rest-client-spring - 1.3.0 @@ -135,6 +137,27 @@ + + test-repository + + + !useInternalRepo + + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + @@ -151,13 +174,6 @@ - - org.apache.maven.plugins - maven-deploy-plugin - - true - - diff --git a/powerauth-admin/src/main/java/io/getlime/security/app/admin/configuration/ActiveDirectoryConfiguration.java b/powerauth-admin/src/main/java/io/getlime/security/app/admin/configuration/ActiveDirectoryConfiguration.java new file mode 100644 index 000000000..88166d20a --- /dev/null +++ b/powerauth-admin/src/main/java/io/getlime/security/app/admin/configuration/ActiveDirectoryConfiguration.java @@ -0,0 +1,74 @@ +/* + * PowerAuth Server 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 io.getlime.security.app.admin.configuration; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +/** + * Active directory configuration. + * @author Petr Dvorak, petr@wultra.com + */ +@Configuration +public class ActiveDirectoryConfiguration { + + @Value("${powerauth.admin.security.activeDirectory.domain}") + private String activeDirectoryDomain; + + @Value("${powerauth.admin.security.activeDirectory.url}") + private String activeDirectoryUrl; + + @Value("${powerauth.admin.security.activeDirectory.root}") + private String activeDirectoryRoot; + + @Value("${powerauth.admin.security.activeDirectory.userSearchFilter}") + private String activeDirectoryUserSearchFilter; + + public String getActiveDirectoryDomain() { + return activeDirectoryDomain; + } + + public void setActiveDirectoryDomain(String activeDirectoryDomain) { + this.activeDirectoryDomain = activeDirectoryDomain; + } + + public String getActiveDirectoryUrl() { + return activeDirectoryUrl; + } + + public void setActiveDirectoryUrl(String activeDirectoryUrl) { + this.activeDirectoryUrl = activeDirectoryUrl; + } + + public String getActiveDirectoryRoot() { + return activeDirectoryRoot; + } + + public void setActiveDirectoryRoot(String activeDirectoryRoot) { + this.activeDirectoryRoot = activeDirectoryRoot; + } + + public String getActiveDirectoryUserSearchFilter() { + return activeDirectoryUserSearchFilter; + } + + public void setActiveDirectoryUserSearchFilter(String activeDirectoryUserSearchFilter) { + this.activeDirectoryUserSearchFilter = activeDirectoryUserSearchFilter; + } +} diff --git a/powerauth-admin/src/main/java/io/getlime/security/app/admin/configuration/ApplicationConfiguration.java b/powerauth-admin/src/main/java/io/getlime/security/app/admin/configuration/ApplicationConfiguration.java index 971ca7970..fb18fb8da 100644 --- a/powerauth-admin/src/main/java/io/getlime/security/app/admin/configuration/ApplicationConfiguration.java +++ b/powerauth-admin/src/main/java/io/getlime/security/app/admin/configuration/ApplicationConfiguration.java @@ -46,42 +46,6 @@ public class ApplicationConfiguration { @Value("${powerauth.admin.security.method}") private String securityMethod; - @Value("${powerauth.admin.security.ldap.userDNPatterns}") - private String ldapUserDNPatterns; - - @Value("${powerauth.admin.security.ldap.userSearchBase}") - private String ldapUserSearchBase; - - @Value("${powerauth.admin.security.ldap.userSearchFilter}") - private String ldapUserSearchFilter; - - @Value("${powerauth.admin.security.ldap.groupSearchBase}") - private String ldapGroupSearchBase; - - @Value("${powerauth.admin.security.ldap.groupSearchFilter}") - private String ldapGroupSearchFilter; - - @Value("${powerauth.admin.security.ldap.groupRoleAttribute}") - private String ldapGroupRoleAttribute; - - @Value("${powerauth.admin.security.ldap.ldif}") - private String ldapLdif; - - @Value("${powerauth.admin.security.ldap.url}") - private String ldapUrl; - - @Value("${powerauth.admin.security.ldap.port}") - private String ldapPort; - - @Value("${powerauth.admin.security.ldap.root}") - private String ldapRoot; - - @Value("${powerauth.admin.security.ldap.managerDN}") - private String ldapManagerDN; - - @Value("${powerauth.admin.security.ldap.managerPassword}") - private String ldapManagerPassword; - @Value("${powerauth.admin.service.applicationName}") private String applicationName; @@ -133,102 +97,6 @@ public void setSecurityMethod(String securityMethod) { this.securityMethod = securityMethod; } - public String getLdapUserDNPatterns() { - return ldapUserDNPatterns; - } - - public void setLdapUserDNPatterns(String ldapUserDNPatterns) { - this.ldapUserDNPatterns = ldapUserDNPatterns; - } - - public String getLdapUserSearchBase() { - return ldapUserSearchBase; - } - - public void setLdapUserSearchBase(String ldapUserSearchBase) { - this.ldapUserSearchBase = ldapUserSearchBase; - } - - public String getLdapUserSearchFilter() { - return ldapUserSearchFilter; - } - - public void setLdapUserSearchFilter(String ldapUserSearchFilter) { - this.ldapUserSearchFilter = ldapUserSearchFilter; - } - - public String getLdapGroupSearchBase() { - return ldapGroupSearchBase; - } - - public void setLdapGroupSearchBase(String ldapGroupSearchBase) { - this.ldapGroupSearchBase = ldapGroupSearchBase; - } - - public String getLdapGroupSearchFilter() { - return ldapGroupSearchFilter; - } - - public void setLdapGroupSearchFilter(String ldapGroupSearchFilter) { - this.ldapGroupSearchFilter = ldapGroupSearchFilter; - } - - public String getLdapGroupRoleAttribute() { - return ldapGroupRoleAttribute; - } - - public void setLdapGroupRoleAttribute(String ldapGroupRoleAttribute) { - this.ldapGroupRoleAttribute = ldapGroupRoleAttribute; - } - - public String getLdapLdif() { - return ldapLdif; - } - - public void setLdapLdif(String ldapLdif) { - this.ldapLdif = ldapLdif; - } - - public String getLdapUrl() { - return ldapUrl; - } - - public void setLdapUrl(String ldapUrl) { - this.ldapUrl = ldapUrl; - } - - public String getLdapPort() { - return ldapPort; - } - - public void setLdapPort(String ldapPort) { - this.ldapPort = ldapPort; - } - - public String getLdapRoot() { - return ldapRoot; - } - - public void setLdapRoot(String ldapRoot) { - this.ldapRoot = ldapRoot; - } - - public String getLdapManagerDN() { - return ldapManagerDN; - } - - public void setLdapManagerDN(String ldapManagerDN) { - this.ldapManagerDN = ldapManagerDN; - } - - public String getLdapManagerPassword() { - return ldapManagerPassword; - } - - public void setLdapManagerPassword(String ldapManagerPassword) { - this.ldapManagerPassword = ldapManagerPassword; - } - public String getApplicationName() { return applicationName; } diff --git a/powerauth-admin/src/main/java/io/getlime/security/app/admin/configuration/LdapConfiguration.java b/powerauth-admin/src/main/java/io/getlime/security/app/admin/configuration/LdapConfiguration.java new file mode 100644 index 000000000..b3183190f --- /dev/null +++ b/powerauth-admin/src/main/java/io/getlime/security/app/admin/configuration/LdapConfiguration.java @@ -0,0 +1,163 @@ +/* + * PowerAuth Server 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 io.getlime.security.app.admin.configuration; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +/** + * LDAP Configuration + * @author Petr Dvorak, petr@wultra.com + */ +@Configuration +public class LdapConfiguration { + + @Value("${powerauth.admin.security.ldap.userDNPatterns}") + private String ldapUserDNPatterns; + + @Value("${powerauth.admin.security.ldap.userSearchBase}") + private String ldapUserSearchBase; + + @Value("${powerauth.admin.security.ldap.userSearchFilter}") + private String ldapUserSearchFilter; + + @Value("${powerauth.admin.security.ldap.groupSearchBase}") + private String ldapGroupSearchBase; + + @Value("${powerauth.admin.security.ldap.groupSearchFilter}") + private String ldapGroupSearchFilter; + + @Value("${powerauth.admin.security.ldap.groupRoleAttribute}") + private String ldapGroupRoleAttribute; + + @Value("${powerauth.admin.security.ldap.ldif}") + private String ldapLdif; + + @Value("${powerauth.admin.security.ldap.url}") + private String ldapUrl; + + @Value("${powerauth.admin.security.ldap.port}") + private String ldapPort; + + @Value("${powerauth.admin.security.ldap.root}") + private String ldapRoot; + + @Value("${powerauth.admin.security.ldap.managerDN}") + private String ldapManagerDN; + + @Value("${powerauth.admin.security.ldap.managerPassword}") + private String ldapManagerPassword; + + public String getLdapUserDNPatterns() { + return ldapUserDNPatterns; + } + + public void setLdapUserDNPatterns(String ldapUserDNPatterns) { + this.ldapUserDNPatterns = ldapUserDNPatterns; + } + + public String getLdapUserSearchBase() { + return ldapUserSearchBase; + } + + public void setLdapUserSearchBase(String ldapUserSearchBase) { + this.ldapUserSearchBase = ldapUserSearchBase; + } + + public String getLdapUserSearchFilter() { + return ldapUserSearchFilter; + } + + public void setLdapUserSearchFilter(String ldapUserSearchFilter) { + this.ldapUserSearchFilter = ldapUserSearchFilter; + } + + public String getLdapGroupSearchBase() { + return ldapGroupSearchBase; + } + + public void setLdapGroupSearchBase(String ldapGroupSearchBase) { + this.ldapGroupSearchBase = ldapGroupSearchBase; + } + + public String getLdapGroupSearchFilter() { + return ldapGroupSearchFilter; + } + + public void setLdapGroupSearchFilter(String ldapGroupSearchFilter) { + this.ldapGroupSearchFilter = ldapGroupSearchFilter; + } + + public String getLdapGroupRoleAttribute() { + return ldapGroupRoleAttribute; + } + + public void setLdapGroupRoleAttribute(String ldapGroupRoleAttribute) { + this.ldapGroupRoleAttribute = ldapGroupRoleAttribute; + } + + public String getLdapLdif() { + return ldapLdif; + } + + public void setLdapLdif(String ldapLdif) { + this.ldapLdif = ldapLdif; + } + + public String getLdapUrl() { + return ldapUrl; + } + + public void setLdapUrl(String ldapUrl) { + this.ldapUrl = ldapUrl; + } + + public String getLdapPort() { + return ldapPort; + } + + public void setLdapPort(String ldapPort) { + this.ldapPort = ldapPort; + } + + public String getLdapRoot() { + return ldapRoot; + } + + public void setLdapRoot(String ldapRoot) { + this.ldapRoot = ldapRoot; + } + + public String getLdapManagerDN() { + return ldapManagerDN; + } + + public void setLdapManagerDN(String ldapManagerDN) { + this.ldapManagerDN = ldapManagerDN; + } + + public String getLdapManagerPassword() { + return ldapManagerPassword; + } + + public void setLdapManagerPassword(String ldapManagerPassword) { + this.ldapManagerPassword = ldapManagerPassword; + } + +} diff --git a/powerauth-admin/src/main/java/io/getlime/security/app/admin/configuration/WebSecurityConfig.java b/powerauth-admin/src/main/java/io/getlime/security/app/admin/configuration/WebSecurityConfig.java index 23ad2876b..432ca9323 100644 --- a/powerauth-admin/src/main/java/io/getlime/security/app/admin/configuration/WebSecurityConfig.java +++ b/powerauth-admin/src/main/java/io/getlime/security/app/admin/configuration/WebSecurityConfig.java @@ -16,11 +16,10 @@ package io.getlime.security.app.admin.configuration; -import io.getlime.security.app.admin.security.SecurityMethod; +import io.getlime.security.app.admin.util.SecurityUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @@ -33,10 +32,14 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final ApplicationConfiguration configuration; + private final LdapConfiguration ldapConfiguration; + private final ActiveDirectoryConfiguration activeDirectoryConfiguration; @Autowired - public WebSecurityConfig(ApplicationConfiguration configuration) { + public WebSecurityConfig(ApplicationConfiguration configuration, LdapConfiguration ldapConfiguration, ActiveDirectoryConfiguration activeDirectoryConfiguration) { this.configuration = configuration; + this.ldapConfiguration = ldapConfiguration; + this.activeDirectoryConfiguration = activeDirectoryConfiguration; } @Override @@ -54,49 +57,13 @@ protected void configure(HttpSecurity http) throws Exception { } } - @Autowired + @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { - if (SecurityMethod.isLdap(configuration.getSecurityMethod())) { - - LdapAuthenticationProviderConfigurer ldapAuthentication = auth.ldapAuthentication(); - if (!configuration.getLdapGroupRoleAttribute().isEmpty()) { - ldapAuthentication = ldapAuthentication.groupRoleAttribute(configuration.getLdapGroupRoleAttribute()); - } - if (!configuration.getLdapGroupSearchBase().isEmpty()) { - ldapAuthentication = ldapAuthentication.groupSearchBase(configuration.getLdapGroupSearchBase()); - } - if (!configuration.getLdapGroupSearchFilter().isEmpty()) { - ldapAuthentication = ldapAuthentication.groupSearchFilter(configuration.getLdapGroupSearchFilter()); - } - if (!configuration.getLdapUserDNPatterns().isEmpty()) { - ldapAuthentication = ldapAuthentication.userDnPatterns(configuration.getLdapUserDNPatterns()); - } - if (!configuration.getLdapUserSearchBase().isEmpty()) { - ldapAuthentication = ldapAuthentication.userSearchBase(configuration.getLdapUserSearchBase()); - } - if (!configuration.getLdapUserSearchFilter().isEmpty()) { - ldapAuthentication = ldapAuthentication.userSearchFilter(configuration.getLdapUserSearchFilter()); - } - - LdapAuthenticationProviderConfigurer.ContextSourceBuilder contextSource = ldapAuthentication.contextSource(); - if (!configuration.getLdapUrl().isEmpty()) { - contextSource.url(configuration.getLdapUrl()); - } - if (!configuration.getLdapPort().isEmpty()) { - contextSource.port(Integer.parseInt(configuration.getLdapPort())); - } - if (!configuration.getLdapRoot().isEmpty()) { - contextSource.root(configuration.getLdapRoot()); - } - if (!configuration.getLdapLdif().isEmpty()) { - contextSource.ldif(configuration.getLdapLdif()); - } - if (!configuration.getLdapManagerDN().isEmpty()) { - contextSource.managerDn(configuration.getLdapManagerDN()); - } - if (!configuration.getLdapManagerPassword().isEmpty()) { - contextSource.managerPassword(configuration.getLdapManagerPassword()); - } + final String securityMethod = configuration.getSecurityMethod(); + if (SecurityUtil.isLdap(securityMethod)) { + SecurityUtil.configureLdap(auth, ldapConfiguration); + } else if (SecurityUtil.isActiveDirectory(securityMethod)) { + SecurityUtil.configureActiveDirectory(auth, activeDirectoryConfiguration); } } diff --git a/powerauth-admin/src/main/java/io/getlime/security/app/admin/security/SecurityMethod.java b/powerauth-admin/src/main/java/io/getlime/security/app/admin/security/SecurityMethod.java deleted file mode 100644 index 596fe4fd9..000000000 --- a/powerauth-admin/src/main/java/io/getlime/security/app/admin/security/SecurityMethod.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 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 io.getlime.security.app.admin.security; - -/** - * Helper class that determines which security method is used. - * - * Currently, only LDAP is supported. - * - * @author Petr Dvorak, petr@wultra.com - */ -public class SecurityMethod { - - /** - * Authentication via LDAP. - */ - public static final String LDAP = "ldap"; - - /** - * Checks if a provided security method is LDAP authentication. - * @param securityMethod Security method to be tested. - * @return True in case given method represents LDAP authentication, false otherwise. - */ - public static boolean isLdap(String securityMethod) { - return securityMethod != null && securityMethod - .trim() - .toLowerCase() - .equals(LDAP); - } -} diff --git a/powerauth-admin/src/main/java/io/getlime/security/app/admin/util/SecurityUtil.java b/powerauth-admin/src/main/java/io/getlime/security/app/admin/util/SecurityUtil.java new file mode 100644 index 000000000..28f85ded5 --- /dev/null +++ b/powerauth-admin/src/main/java/io/getlime/security/app/admin/util/SecurityUtil.java @@ -0,0 +1,121 @@ +/* + * PowerAuth Server 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 io.getlime.security.app.admin.util; + +import io.getlime.security.app.admin.configuration.ActiveDirectoryConfiguration; +import io.getlime.security.app.admin.configuration.LdapConfiguration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer; +import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider; +import org.springframework.util.StringUtils; + +/** + * Utility class for various security related tasks. + * + * @author Petr Dvorak, petr@wultra.com + */ +public class SecurityUtil { + + /** + * Authentication via LDAP. + */ + public static final String LDAP = "ldap"; + + /** + * Authentication via Active Directory. + */ + public static final String ACTIVE_DIRECTORY = "active-directory"; + + /** + * Checks if a provided security method is LDAP authentication. + * @param securityMethod Security method to be tested. + * @return True in case given method represents LDAP authentication, false otherwise. + */ + public static boolean isLdap(String securityMethod) { + return securityMethod != null && securityMethod + .trim() + .equalsIgnoreCase(LDAP); + } + + /** + * Checks if a provided security method is Active Directory authentication. + * @param securityMethod Security method to be tested. + * @return True in case given method represents Active Directory authentication, false otherwise. + */ + public static boolean isActiveDirectory(String securityMethod) { + return securityMethod != null && securityMethod + .trim() + .equalsIgnoreCase(ACTIVE_DIRECTORY); + } + + public static void configureLdap(AuthenticationManagerBuilder auth, LdapConfiguration configuration) throws Exception { + final LdapAuthenticationProviderConfigurer ldapAuthenticationBuilder = auth.ldapAuthentication(); + if (!configuration.getLdapGroupRoleAttribute().isEmpty()) { + ldapAuthenticationBuilder.groupRoleAttribute(configuration.getLdapGroupRoleAttribute()); + } + if (!configuration.getLdapGroupSearchBase().isEmpty()) { + ldapAuthenticationBuilder.groupSearchBase(configuration.getLdapGroupSearchBase()); + } + if (!configuration.getLdapGroupSearchFilter().isEmpty()) { + ldapAuthenticationBuilder.groupSearchFilter(configuration.getLdapGroupSearchFilter()); + } + if (!configuration.getLdapUserDNPatterns().isEmpty()) { + ldapAuthenticationBuilder.userDnPatterns(configuration.getLdapUserDNPatterns()); + } + if (!configuration.getLdapUserSearchBase().isEmpty()) { + ldapAuthenticationBuilder.userSearchBase(configuration.getLdapUserSearchBase()); + } + if (!configuration.getLdapUserSearchFilter().isEmpty()) { + ldapAuthenticationBuilder.userSearchFilter(configuration.getLdapUserSearchFilter()); + } + + final LdapAuthenticationProviderConfigurer.ContextSourceBuilder contextSource = ldapAuthenticationBuilder.contextSource(); + if (!configuration.getLdapUrl().isEmpty()) { + contextSource.url(configuration.getLdapUrl()); + } + if (!configuration.getLdapPort().isEmpty()) { + contextSource.port(Integer.parseInt(configuration.getLdapPort())); + } + if (!configuration.getLdapRoot().isEmpty()) { + contextSource.root(configuration.getLdapRoot()); + } + if (!configuration.getLdapLdif().isEmpty()) { + contextSource.ldif(configuration.getLdapLdif()); + } + if (!configuration.getLdapManagerDN().isEmpty()) { + contextSource.managerDn(configuration.getLdapManagerDN()); + } + if (!configuration.getLdapManagerPassword().isEmpty()) { + contextSource.managerPassword(configuration.getLdapManagerPassword()); + } + } + + public static void configureActiveDirectory(AuthenticationManagerBuilder auth, ActiveDirectoryConfiguration configuration) { + final String activeDirectoryDomain = configuration.getActiveDirectoryDomain(); + final String ldapUrl = configuration.getActiveDirectoryUrl(); + final String ldapRoot = configuration.getActiveDirectoryRoot(); + final ActiveDirectoryLdapAuthenticationProvider authenticationProvider = new ActiveDirectoryLdapAuthenticationProvider(activeDirectoryDomain, ldapUrl, ldapRoot); + final String userSearchFilter = configuration.getActiveDirectoryUserSearchFilter(); + if (StringUtils.hasText(userSearchFilter)) { + authenticationProvider.setSearchFilter(userSearchFilter); + } + auth.authenticationProvider(authenticationProvider); + } + +} diff --git a/powerauth-admin/src/main/resources/application.properties b/powerauth-admin/src/main/resources/application.properties index 1d88aca2b..a4ace6ed5 100644 --- a/powerauth-admin/src/main/resources/application.properties +++ b/powerauth-admin/src/main/resources/application.properties @@ -13,7 +13,7 @@ powerauth.service.security.clientSecret= # HTTPS configuration powerauth.service.ssl.acceptInvalidSslCertificate=false -# PowerAuth Admin Security Settings +# PowerAuth Admin Security Settings - 'ldap' or 'active-directory' powerauth.admin.security.method= # LDAP Security @@ -30,6 +30,12 @@ powerauth.admin.security.ldap.ldif= powerauth.admin.security.ldap.managerDN= powerauth.admin.security.ldap.managerPassword= +# Active Directory Security +powerauth.admin.security.activeDirectory.domain= +powerauth.admin.security.activeDirectory.url= +powerauth.admin.security.activeDirectory.root= +powerauth.admin.security.activeDirectory.userSearchFilter= + # Application Service Configuration powerauth.admin.service.applicationName=powerauth-admin powerauth.admin.service.applicationDisplayName=PowerAuth Admin diff --git a/powerauth-admin/src/main/webapp/WEB-INF/jsp/activations.jsp b/powerauth-admin/src/main/webapp/WEB-INF/jsp/activations.jsp index 500a4f7dc..4c004a102 100644 --- a/powerauth-admin/src/main/webapp/WEB-INF/jsp/activations.jsp +++ b/powerauth-admin/src/main/webapp/WEB-INF/jsp/activations.jsp @@ -194,8 +194,8 @@ - - + + diff --git a/powerauth-admin/src/main/webapp/WEB-INF/jsp/header.jsp b/powerauth-admin/src/main/webapp/WEB-INF/jsp/header.jsp index 63cfbf0b5..e84dfe5d4 100644 --- a/powerauth-admin/src/main/webapp/WEB-INF/jsp/header.jsp +++ b/powerauth-admin/src/main/webapp/WEB-INF/jsp/header.jsp @@ -44,6 +44,9 @@ + diff --git a/powerauth-admin/src/main/webapp/WEB-INF/jsp/headerSimple.jsp b/powerauth-admin/src/main/webapp/WEB-INF/jsp/headerSimple.jsp index 2e75f7245..779e4087b 100644 --- a/powerauth-admin/src/main/webapp/WEB-INF/jsp/headerSimple.jsp +++ b/powerauth-admin/src/main/webapp/WEB-INF/jsp/headerSimple.jsp @@ -32,6 +32,9 @@ + diff --git a/powerauth-client-model/pom.xml b/powerauth-client-model/pom.xml index 3b17cc303..1610d81f2 100644 --- a/powerauth-client-model/pom.xml +++ b/powerauth-client-model/pom.xml @@ -22,15 +22,13 @@ 4.0.0 powerauth-client-model - 1.3.0 powerauth-client-model PowerAuth Server Client Model io.getlime.security powerauth-server-parent - 1.3.0 - ../pom.xml + 1.4.0 @@ -42,7 +40,6 @@ com.fasterxml.jackson.core jackson-databind - ${jackson.version} javax.xml.bind diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateCreateRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateCreateRequest.java index 8209902c5..f5100af13 100644 --- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateCreateRequest.java +++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateCreateRequest.java @@ -36,6 +36,7 @@ public class OperationTemplateCreateRequest { private final List signatureType = new ArrayList<>(); private Long maxFailureCount; private Long expiration; + private String riskFlags; public String getTemplateName() { return templateName; @@ -81,6 +82,14 @@ public void setExpiration(Long expiration) { this.expiration = expiration; } + public String getRiskFlags() { + return riskFlags; + } + + public void setRiskFlags(String riskFlags) { + this.riskFlags = riskFlags; + } + @Override public String toString() { return "OperationTemplateCreateRequest{" + @@ -90,6 +99,7 @@ public String toString() { ", signatureType=" + signatureType + ", maxFailureCount=" + maxFailureCount + ", expiration=" + expiration + + ", riskFlags=" + riskFlags + '}'; } } diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateUpdateRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateUpdateRequest.java index 2dc828d1b..9b8f54aa9 100644 --- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateUpdateRequest.java +++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/OperationTemplateUpdateRequest.java @@ -36,6 +36,7 @@ public class OperationTemplateUpdateRequest { private final List signatureType = new ArrayList<>(); private Long maxFailureCount; private Long expiration; + private String riskFlags; public Long getId() { return id; @@ -81,6 +82,14 @@ public void setExpiration(Long expiration) { this.expiration = expiration; } + public String getRiskFlags() { + return riskFlags; + } + + public void setRiskFlags(String riskFlags) { + this.riskFlags = riskFlags; + } + @Override public String toString() { return "OperationTemplateUpdateRequest{" + @@ -90,6 +99,7 @@ public String toString() { ", signatureType=" + signatureType + ", maxFailureCount=" + maxFailureCount + ", expiration=" + expiration + + ", riskFlags=" + riskFlags + '}'; } } diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/OperationDetailResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/OperationDetailResponse.java index e54084f1d..a74857d7c 100644 --- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/OperationDetailResponse.java +++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/OperationDetailResponse.java @@ -49,6 +49,7 @@ public class OperationDetailResponse { private Date timestampCreated; private Date timestampExpires; private Date timestampFinalized; + private String riskFlags; public void setId(String id) { this.id = id; @@ -185,4 +186,12 @@ public void setTimestampFinalized(Date timestampFinalized) { public Date getTimestampFinalized() { return timestampFinalized; } + + public String getRiskFlags() { + return riskFlags; + } + + public void setRiskFlags(String riskFlags) { + this.riskFlags = riskFlags; + } } diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/OperationTemplateDetailResponse.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/OperationTemplateDetailResponse.java index 1ddc7e6a7..3904e0194 100644 --- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/OperationTemplateDetailResponse.java +++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/response/OperationTemplateDetailResponse.java @@ -36,6 +36,7 @@ public class OperationTemplateDetailResponse { private List signatureType; private Long maxFailureCount; private Long expiration; + private String riskFlags; public Long getId() { return id; @@ -92,4 +93,12 @@ public Long getExpiration() { public void setExpiration(Long expiration) { this.expiration = expiration; } + + public String getRiskFlags() { + return riskFlags; + } + + public void setRiskFlags(String riskFlags) { + this.riskFlags = riskFlags; + } } diff --git a/powerauth-java-server/pom.xml b/powerauth-java-server/pom.xml index 15f1f6514..660de6c01 100644 --- a/powerauth-java-server/pom.xml +++ b/powerauth-java-server/pom.xml @@ -24,14 +24,12 @@ powerauth-java-server PowerAuth Server powerauth-java-server - 1.3.0 war io.getlime.security powerauth-server-parent - 1.3.0 - ../pom.xml + 1.4.0 @@ -88,6 +86,12 @@ org.springframework.boot spring-boot-starter-validation + + + org.apache.tomcat.embed + tomcat-embed-el + + org.springframework.boot @@ -102,6 +106,12 @@ org.springframework.cloud spring-cloud-starter-vault-config ${spring-cloud-vault.version} + + + org.bouncycastle + bcpkix-jdk15on + + org.springframework.boot @@ -112,7 +122,6 @@ io.getlime.security powerauth-client-model - 1.3.0 io.getlime.security @@ -154,8 +163,8 @@ org.bouncycastle - bcprov-jdk15on - ${bcprov-jdk15on.version} + bcprov-jdk18on + ${bcprov-jdk18on.version} @@ -192,7 +201,6 @@ com.h2database h2 - ${h2.version} test @@ -238,14 +246,6 @@ - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - @@ -259,6 +259,27 @@ + + test-repository + + + !useInternalRepo + + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/PowerAuthServiceConfiguration.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/PowerAuthServiceConfiguration.java index 96098c837..f495aa13a 100644 --- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/PowerAuthServiceConfiguration.java +++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/PowerAuthServiceConfiguration.java @@ -117,6 +117,13 @@ public class PowerAuthServiceConfiguration { @Min(0) private int activationValidityBeforeActive; + /** + * How many milliseconds should the activation cleanup job look to the past. + */ + @Value("${powerauth.service.scheduled.job.activationsCleanup.lookBackInMilliseconds:3600000}") + @Min(0) + private int activationsCleanupLookBackInMilliseconds; + /** * How many failed signatures cause activation record blocking. The maximum supported value is 64. */ @@ -400,6 +407,24 @@ public void setActivationValidityBeforeActive(int activationValidityBeforeActive this.activationValidityBeforeActive = activationValidityBeforeActive; } + /** + * Get look-back milliseconds for activation cleanup. + * @return How long the activation cleanup job should look back in time. + */ + public int getActivationsCleanupLookBackInMilliseconds() { + return activationsCleanupLookBackInMilliseconds; + } + + /** + * Set look-back milliseconds for activation cleanup. + * @param activationsCleanupLookBackInMilliseconds How long the activation cleanup job should look back in time. + */ + public void setActivationsCleanupLookBackInMilliseconds(int activationsCleanupLookBackInMilliseconds) { + this.activationsCleanupLookBackInMilliseconds = activationsCleanupLookBackInMilliseconds; + } + + + /** * Get the signature validation lookahead. * @return Signature validation lookahead. diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/ScheduledJobConfiguration.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/ScheduledJobConfiguration.java index ad0f521b2..ef067f2c5 100644 --- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/ScheduledJobConfiguration.java +++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/configuration/ScheduledJobConfiguration.java @@ -17,8 +17,11 @@ */ package io.getlime.security.powerauth.app.server.configuration; +import lombok.extern.slf4j.Slf4j; import net.javacrumbs.shedlock.core.LockProvider; import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; @@ -31,16 +34,26 @@ * @author Petr Dvorak, petr@wultra.com */ @Configuration +@Slf4j public class ScheduledJobConfiguration { + @Value("${spring.jpa.properties.hibernate.default_schema:}") + private String defaultSchema; + + private static final String SHEDLOCK_TABLE_NAME = "shedlock"; + @Bean public LockProvider lockProvider(DataSource dataSource) { + final String tableName = StringUtils.isBlank(defaultSchema) + ? SHEDLOCK_TABLE_NAME + : defaultSchema + "." + SHEDLOCK_TABLE_NAME; + logger.info("Following database table will be used by shedlock: {}", tableName); return new JdbcTemplateLockProvider( JdbcTemplateLockProvider.Configuration.builder() .withJdbcTemplate(new JdbcTemplate(dataSource)) + .withTableName(tableName) .usingDbTime() - .build() - ); + .build()); } } diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/converter/v3/OperationTemplateConverter.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/converter/v3/OperationTemplateConverter.java index 37101c6b6..f6d97bcd5 100644 --- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/converter/v3/OperationTemplateConverter.java +++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/converter/v3/OperationTemplateConverter.java @@ -47,6 +47,7 @@ public OperationTemplateEntity convertToDB(OperationTemplateCreateRequest source destination.setDataTemplate(source.getDataTemplate()); destination.setMaxFailureCount(source.getMaxFailureCount()); destination.setExpiration(source.getExpiration()); + destination.setRiskFlags(source.getRiskFlags()); final List signatureTypes = new ArrayList<>(); for (final SignatureType type : source.getSignatureType()) { @@ -70,6 +71,7 @@ public OperationTemplateEntity convertToDB(OperationTemplateEntity original, Ope original.setDataTemplate(source.getDataTemplate()); original.setMaxFailureCount(source.getMaxFailureCount()); original.setExpiration(source.getExpiration()); + original.setRiskFlags(source.getRiskFlags()); final List signatureTypes = new ArrayList<>(); for (final SignatureType type : source.getSignatureType()) { @@ -92,6 +94,7 @@ public OperationTemplateDetailResponse convertFromDB(OperationTemplateEntity sou destination.setDataTemplate(source.getDataTemplate()); destination.setExpiration(source.getExpiration()); destination.setMaxFailureCount(source.getMaxFailureCount()); + destination.setRiskFlags(source.getRiskFlags()); final List signatureTypesResponse = new ArrayList<>(); for (final PowerAuthSignatureTypes type : source.getSignatureType()) { final SignatureType signatureType = SignatureType.enumFromString(type.toString()); diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/CallbackUrlAuthenticationEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/CallbackUrlAuthenticationEntity.java index 5bb7769c9..2586efdb0 100644 --- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/CallbackUrlAuthenticationEntity.java +++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/CallbackUrlAuthenticationEntity.java @@ -67,7 +67,9 @@ public void setHttpBasic(HttpBasic httpBasic) { /** * Inner-class with certificate authentication credentials. */ - public static class Certificate { + public static class Certificate implements Serializable { + + private static final long serialVersionUID = -3123397103510377094L; protected boolean enabled; protected boolean useCustomKeyStore; @@ -155,7 +157,9 @@ public void setTrustStorePassword(String trustStorePassword) { /** * Inner-class with Basic HTTP authentication credentials. */ - public static class HttpBasic { + public static class HttpBasic implements Serializable { + + private static final long serialVersionUID = 4449327538548490513L; protected boolean enabled; protected String username; diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationEntity.java index aadd49728..f45d031c2 100644 --- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationEntity.java +++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationEntity.java @@ -99,6 +99,9 @@ public class OperationEntity implements Serializable { @Column(name = "timestamp_finalized") private Date timestampFinalized; + @Column(name = "risk_flags") + private String riskFlags; + /** * Get operation ID. * @return Operation ID. @@ -371,6 +374,24 @@ public void setTimestampFinalized(Date timestampFinalized) { this.timestampFinalized = timestampFinalized; } + /** + * Get risk flags. + * + * @return Risk flags. + */ + public String getRiskFlags() { + return riskFlags; + } + + /** + * Set risk flags. + * + * @param riskFlags Risk flags. + */ + public void setRiskFlags(String riskFlags) { + this.riskFlags = riskFlags; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -384,13 +405,14 @@ public boolean equals(Object o) { && templateName.equals(that.templateName) && data.equals(that.data) && Objects.equals(parameters, that.parameters) - && Objects.equals(additionalData, that.additionalData); + && Objects.equals(additionalData, that.additionalData) + && Objects.equals(riskFlags, that.riskFlags); } @Override public int hashCode() { return Objects.hash( - id, userId, applications, activationFlag, operationType, templateName, data, parameters, additionalData + id, userId, applications, activationFlag, operationType, templateName, data, parameters, additionalData, riskFlags ); } @@ -414,6 +436,7 @@ public String toString() { ", timestampCreated=" + timestampCreated + ", timestampExpires=" + timestampExpires + ", timestampFinalized=" + timestampFinalized + + ", riskFlags=" + riskFlags + '}'; } } diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationTemplateEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationTemplateEntity.java index 346897e1f..aedf58ca4 100644 --- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationTemplateEntity.java +++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationTemplateEntity.java @@ -62,6 +62,9 @@ public class OperationTemplateEntity implements Serializable { @Column(name = "expiration", nullable=false) private Long expiration; + @Column(name = "risk_flags") + private String riskFlags; + /** * Default constructor. */ @@ -180,6 +183,24 @@ public void setExpiration(Long expiration) { this.expiration = expiration; } + /** + * Get risk flags. + * + * @return Risk flags. + */ + public String getRiskFlags() { + return riskFlags; + } + + /** + * Set risk flags. + * + * @param riskFlags Risk flags. + */ + public void setRiskFlags(String riskFlags) { + this.riskFlags = riskFlags; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof OperationTemplateEntity)) return false; @@ -189,12 +210,13 @@ public void setExpiration(Long expiration) { && Objects.equals(dataTemplate, that.dataTemplate) && Arrays.equals(signatureType, that.signatureType) && Objects.equals(maxFailureCount, that.maxFailureCount) - && Objects.equals(expiration, that.expiration); + && Objects.equals(expiration, that.expiration) + && Objects.equals(riskFlags, that.riskFlags); } @Override public int hashCode() { int result = Objects.hash( - templateName, operationType, dataTemplate, maxFailureCount, expiration + templateName, operationType, dataTemplate, maxFailureCount, expiration, riskFlags ); result = 31 * result + Arrays.hashCode(signatureType); return result; @@ -209,6 +231,7 @@ public void setExpiration(Long expiration) { ", signatureType=" + Arrays.toString(signatureType) + ", maxFailureCount=" + maxFailureCount + ", expiration=" + expiration + + ", riskFlags=" + riskFlags + '}'; } } diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/ActivationRepository.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/ActivationRepository.java index df7d2c67b..52701e481 100644 --- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/ActivationRepository.java +++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/repository/ActivationRepository.java @@ -28,6 +28,7 @@ import java.util.Collection; import java.util.Date; import java.util.List; +import java.util.stream.Stream; /** * Database repository for activation entities. @@ -167,4 +168,16 @@ default Long getActivationCountByActivationCode(String applicationId, String act */ @Query("SELECT a FROM ActivationRecordEntity a WHERE a.userId IN :userIds AND ((:applicationIds) IS NULL OR a.application.id IN :applicationIds) AND a.timestampLastUsed < :timestampLastUsedBefore AND a.timestampLastUsed >= :timestampLastUsedAfter AND a.activationStatus IN :states") List lookupActivations(Collection userIds, Collection applicationIds, Date timestampLastUsedBefore, Date timestampLastUsedAfter, Collection states); + + /** + * Fetch all activations that are in a given state, were expired after a specified timestamp, and are already expired according to a provided current timestamp. + * The activations are locked in DB in PESSIMISTIC_WRITE mode to avoid concurrency issues. + * @param states Activation states that are used for the lookup. + * @param startingTimestamp Timestamp after which the activation was expired. + * @param currentTimestamp Current timestamp, to identify already expired operations. + * @return Stream of activations. + */ + @Lock(LockModeType.PESSIMISTIC_WRITE) + @Query("SELECT a FROM ActivationRecordEntity a WHERE a.activationStatus IN :states AND a.timestampActivationExpire >= :startingTimestamp AND a.timestampActivationExpire < :currentTimestamp") + Stream findAbandonedActivations(Collection states, Date startingTimestamp, Date currentTimestamp); } diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/v3/ActivationServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/v3/ActivationServiceBehavior.java index 1af02772b..a0c74d3b3 100644 --- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/v3/ActivationServiceBehavior.java +++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/v3/ActivationServiceBehavior.java @@ -56,10 +56,14 @@ import io.getlime.security.powerauth.crypto.lib.util.PasswordHash; import io.getlime.security.powerauth.crypto.server.activation.PowerAuthServerActivation; import io.getlime.security.powerauth.crypto.server.keyfactory.PowerAuthServerKeyFactory; +import net.javacrumbs.shedlock.core.LockAssert; +import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import javax.crypto.SecretKey; import javax.validation.constraints.NotNull; @@ -75,6 +79,7 @@ import java.security.spec.InvalidKeySpecException; import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Behavior class implementing processes related with activations. Used to move the @@ -158,13 +163,12 @@ public void setRecoveryPukConverter(RecoveryPukConverter recoveryPukConverter) { */ private void deactivatePendingActivation(Date timestamp, ActivationRecordEntity activation, boolean isActivationLocked) { if ((activation.getActivationStatus().equals(ActivationStatus.CREATED) || activation.getActivationStatus().equals(ActivationStatus.PENDING_COMMIT)) && (timestamp.getTime() > activation.getTimestampActivationExpire().getTime())) { + logger.info("Deactivating pending activation, activation ID: {}", activation.getActivationId()); if (!isActivationLocked) { // Make sure activation is locked until the end of transaction in case it was not locked yet activation = repositoryCatalogue.getActivationRepository().findActivationWithLock(activation.getActivationId()); } - activation.setActivationStatus(io.getlime.security.powerauth.app.server.database.model.ActivationStatus.REMOVED); - activationHistoryServiceBehavior.saveActivationAndLogChange(activation); - callbackUrlBehavior.notifyCallbackListenersOnActivationChange(activation); + removeActivationInternal(activation, null, true); } } @@ -1447,29 +1451,8 @@ public RemoveActivationResponse removeActivation(String activationId, String ext * @return Response with confirmation of removal. */ public RemoveActivationResponse removeActivation(@NotNull ActivationRecordEntity activation, String externalUserId, boolean revokeRecoveryCodes) { - activation.setActivationStatus(io.getlime.security.powerauth.app.server.database.model.ActivationStatus.REMOVED); - if (revokeRecoveryCodes) { - final RecoveryCodeRepository recoveryCodeRepository = repositoryCatalogue.getRecoveryCodeRepository(); - final List recoveryCodeEntities = recoveryCodeRepository.findAllByActivationId(activation.getActivationId()); - final Date now = new Date(); - for (RecoveryCodeEntity recoveryCode : recoveryCodeEntities) { - // revoke only codes that are not yet revoked, to avoid messing up with timestamp - if (!RecoveryCodeStatus.REVOKED.equals(recoveryCode.getStatus())) { - recoveryCode.setStatus(RecoveryCodeStatus.REVOKED); - recoveryCode.setTimestampLastChange(now); - // Change status of PUKs with status VALID to INVALID - for (RecoveryPukEntity puk : recoveryCode.getRecoveryPuks()) { - if (RecoveryPukStatus.VALID.equals(puk.getStatus())) { - puk.setStatus(RecoveryPukStatus.INVALID); - puk.setTimestampLastChange(now); - } - } - recoveryCodeRepository.save(recoveryCode); - } - } - } - activationHistoryServiceBehavior.saveActivationAndLogChange(activation, externalUserId); - callbackUrlBehavior.notifyCallbackListenersOnActivationChange(activation); + logger.info("Processing activation removal, activation ID: {}", activation.getActivationId()); + removeActivationInternal(activation, externalUserId, revokeRecoveryCodes); RemoveActivationResponse response = new RemoveActivationResponse(); response.setActivationId(activation.getActivationId()); response.setRemoved(true); @@ -1957,4 +1940,69 @@ private ActivationRecovery createRecoveryCodeForActivation(ActivationRecordEntit } } + /** + * Internal logic for processing activation removal. + * @param activation Activation entity. + * @param externalUserId External user identifier. + * @param revokeRecoveryCodes Whether associated recovery codes should be revoked. + */ + private void removeActivationInternal(final ActivationRecordEntity activation, final String externalUserId, final boolean revokeRecoveryCodes) { + activation.setActivationStatus(io.getlime.security.powerauth.app.server.database.model.ActivationStatus.REMOVED); + // Recovery codes are revoked in case revocation is requested, or always when the activation is in CREATED or PENDING_COMMIT state + if (revokeRecoveryCodes + || activation.getActivationStatus() == ActivationStatus.CREATED + || activation.getActivationStatus() == ActivationStatus.PENDING_COMMIT) { + revokeRecoveryCodes(activation.getActivationId()); + } + activationHistoryServiceBehavior.saveActivationAndLogChange(activation, externalUserId); + callbackUrlBehavior.notifyCallbackListenersOnActivationChange(activation); + } + + /** + * Revoke recovery codes for an activation entity. + * @param activationId Activation identifier. + */ + private void revokeRecoveryCodes(String activationId) { + logger.info("Revoking recovery codes for activation ID: {}", activationId); + final RecoveryCodeRepository recoveryCodeRepository = repositoryCatalogue.getRecoveryCodeRepository(); + final List recoveryCodeEntities = recoveryCodeRepository.findAllByActivationId(activationId); + final Date now = new Date(); + for (RecoveryCodeEntity recoveryCode : recoveryCodeEntities) { + logger.debug("Revoking recovery code: {} for activation ID: {}", recoveryCode.getRecoveryCode(), activationId); + // revoke only codes that are not yet revoked, to avoid messing up with timestamp + if (!RecoveryCodeStatus.REVOKED.equals(recoveryCode.getStatus())) { + recoveryCode.setStatus(RecoveryCodeStatus.REVOKED); + recoveryCode.setTimestampLastChange(now); + // Change status of PUKs with status VALID to INVALID + for (RecoveryPukEntity puk : recoveryCode.getRecoveryPuks()) { + if (RecoveryPukStatus.VALID.equals(puk.getStatus())) { + puk.setStatus(RecoveryPukStatus.INVALID); + puk.setTimestampLastChange(now); + } + } + recoveryCodeRepository.save(recoveryCode); + } + } + } + + // Scheduled tasks + + @Scheduled(fixedRateString = "${powerauth.service.scheduled.job.activationsCleanup:5000}") + @SchedulerLock(name = "expireActivationsTask") + @Transactional + public void expireActivations() { + LockAssert.assertLocked(); + final Date currentTimestamp = new Date(); + final Date lookBackTimestamp = new Date(currentTimestamp.getTime() - powerAuthServiceConfiguration.getActivationsCleanupLookBackInMilliseconds()); + logger.debug("Running scheduled task for expiring activations"); + final ActivationRepository activationRepository = repositoryCatalogue.getActivationRepository(); + final ImmutableSet activationStatuses = ImmutableSet.of(ActivationStatus.CREATED, ActivationStatus.PENDING_COMMIT); + try (final Stream abandonedActivations = activationRepository.findAbandonedActivations(activationStatuses, lookBackTimestamp, currentTimestamp)) { + abandonedActivations.forEach(activation -> { + logger.info("Removing abandoned activation with ID: {}", activation.getActivationId()); + deactivatePendingActivation(currentTimestamp, activation, true); + }); + } + } + } diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/v3/OperationServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/v3/OperationServiceBehavior.java index bc716a721..3b07a7f6f 100644 --- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/v3/OperationServiceBehavior.java +++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/v3/OperationServiceBehavior.java @@ -166,6 +166,7 @@ public OperationDetailResponse createOperation(OperationCreateRequest request) t operationEntity.setTimestampCreated(currentTimestamp); operationEntity.setTimestampExpires(timestampExpiration); operationEntity.setTimestampFinalized(null); // empty initially + operationEntity.setRiskFlags(templateEntity.getRiskFlags()); final AuditDetail auditDetail = AuditDetail.builder() .type("operation") @@ -632,6 +633,8 @@ private OperationDetailResponse convertFromEntity(OperationEntity source) { destination.setTimestampCreated(source.getTimestampCreated()); destination.setTimestampExpires(source.getTimestampExpires()); destination.setTimestampFinalized(source.getTimestampFinalized()); + destination.setRiskFlags(source.getRiskFlags()); + switch (source.getStatus()) { case PENDING: destination.setStatus(OperationStatus.PENDING); @@ -716,7 +719,7 @@ private Map mapMerge(Map m1, Map // Scheduled tasks - @Scheduled(fixedRateString = "${powerauth.service.scheduled.job.operationCleanup}") + @Scheduled(fixedRateString = "${powerauth.service.scheduled.job.operationCleanup:5000}") @SchedulerLock(name = "expireOperationsTask") @Transactional public void expireOperations() { diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/v3/TokenBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/v3/TokenBehavior.java index 52259ce72..eb29930c3 100644 --- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/v3/TokenBehavior.java +++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/v3/TokenBehavior.java @@ -22,6 +22,7 @@ import com.google.common.io.BaseEncoding; import com.wultra.security.powerauth.client.v3.*; import io.getlime.security.powerauth.app.server.configuration.PowerAuthServiceConfiguration; +import io.getlime.security.powerauth.app.server.converter.v3.ActivationStatusConverter; import io.getlime.security.powerauth.app.server.converter.v3.ServerPrivateKeyConverter; import io.getlime.security.powerauth.app.server.converter.v3.SignatureTypeConverter; import io.getlime.security.powerauth.app.server.database.RepositoryCatalogue; @@ -82,6 +83,7 @@ public class TokenBehavior { // Helper classes private final SignatureTypeConverter signatureTypeConverter = new SignatureTypeConverter(); + private final ActivationStatusConverter activationStatusConverter = new ActivationStatusConverter(); private final ObjectMapper objectMapper; @@ -274,31 +276,26 @@ public ValidateTokenResponse validateToken(ValidateTokenRequest request) throws // Check if the activation is in correct state final ActivationRecordEntity activation = token.getActivation(); + final byte[] tokenSecret = BaseEncoding.base64().decode(token.getTokenSecret()); + final boolean isTokenValid; if (!ActivationStatus.ACTIVE.equals(activation.getActivationStatus())) { logger.info("Activation is not ACTIVE, activation ID: {}", activation.getActivationId()); - // Rollback is not required, database is not used for writing - throw localizationProvider.buildExceptionForCode(ServiceError.ACTIVATION_INCORRECT_STATE); - } - - final byte[] tokenSecret = BaseEncoding.base64().decode(token.getTokenSecret()); - - final boolean isTokenValid = tokenVerifier.validateTokenDigest(nonce, timestamp, tokenSecret, tokenDigest); - - if (isTokenValid) { - final ValidateTokenResponse response = new ValidateTokenResponse(); - response.setTokenValid(true); - response.setActivationId(activation.getActivationId()); - response.setApplicationId(activation.getApplication().getId()); - response.getApplicationRoles().addAll(activation.getApplication().getRoles()); - response.getActivationFlags().addAll(activation.getFlags()); - response.setUserId(activation.getUserId()); - response.setSignatureType(signatureTypeConverter.convertFrom(token.getSignatureTypeCreated())); - return response; + isTokenValid = false; } else { - final ValidateTokenResponse response = new ValidateTokenResponse(); - response.setTokenValid(false); - return response; + isTokenValid = tokenVerifier.validateTokenDigest(nonce, timestamp, tokenSecret, tokenDigest); } + + final ValidateTokenResponse response = new ValidateTokenResponse(); + response.setTokenValid(isTokenValid); + response.setActivationStatus(activationStatusConverter.convert(activation.getActivationStatus())); + response.setBlockedReason(activation.getBlockedReason()); + response.setActivationId(activation.getActivationId()); + response.setApplicationId(activation.getApplication().getId()); + response.getApplicationRoles().addAll(activation.getApplication().getRoles()); + response.getActivationFlags().addAll(activation.getFlags()); + response.setUserId(activation.getUserId()); + response.setSignatureType(signatureTypeConverter.convertFrom(token.getSignatureTypeCreated())); + return response; } catch (GenericCryptoException ex) { logger.error(ex.getMessage(), ex); // Rollback is not required, database is not used for writing diff --git a/powerauth-java-server/src/main/resources/application.properties b/powerauth-java-server/src/main/resources/application.properties index f5f16481f..974c11acf 100644 --- a/powerauth-java-server/src/main/resources/application.properties +++ b/powerauth-java-server/src/main/resources/application.properties @@ -87,6 +87,8 @@ powerauth.service.secureVault.enableBiometricAuthentication=false # PowerAuth Service Scheduled Jobs powerauth.service.scheduled.job.operationCleanup=5000 +powerauth.service.scheduled.job.activationsCleanup=5000 +powerauth.service.scheduled.job.activationsCleanup.lookBackInMilliseconds=3600000 # Database Lock Timeout Configuration spring.jpa.properties.lock.timeout=10000 diff --git a/powerauth-java-server/src/main/resources/xsd/PowerAuth-3.0.xsd b/powerauth-java-server/src/main/resources/xsd/PowerAuth-3.0.xsd index 0adb02fef..bac77ab76 100644 --- a/powerauth-java-server/src/main/resources/xsd/PowerAuth-3.0.xsd +++ b/powerauth-java-server/src/main/resources/xsd/PowerAuth-3.0.xsd @@ -1237,6 +1237,8 @@ + + diff --git a/powerauth-java-server/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/powerauth-java-server/src/main/webapp/WEB-INF/jboss-deployment-structure.xml index 07eb48f50..45f68d9cf 100644 --- a/powerauth-java-server/src/main/webapp/WEB-INF/jboss-deployment-structure.xml +++ b/powerauth-java-server/src/main/webapp/WEB-INF/jboss-deployment-structure.xml @@ -12,7 +12,7 @@ - + diff --git a/powerauth-java-server/src/test/resources/application.properties b/powerauth-java-server/src/test/resources/application.properties index 4738485c5..450a6caa3 100644 --- a/powerauth-java-server/src/test/resources/application.properties +++ b/powerauth-java-server/src/test/resources/application.properties @@ -66,6 +66,8 @@ powerauth.service.secureVault.enableBiometricAuthentication=false # PowerAuth Service Scheduled Jobs powerauth.service.scheduled.job.operationCleanup=5000 +powerauth.service.scheduled.job.activationsCleanup=5000 +powerauth.service.scheduled.job.activationsCleanup.lookBackInMilliseconds=3600000 # Database Lock Timeout Configuration spring.jpa.properties.lock.timeout=10000 diff --git a/powerauth-rest-client-spring/pom.xml b/powerauth-rest-client-spring/pom.xml index 6c5c2aef1..5ea2e93b6 100644 --- a/powerauth-rest-client-spring/pom.xml +++ b/powerauth-rest-client-spring/pom.xml @@ -22,15 +22,13 @@ 4.0.0 powerauth-rest-client-spring - 1.3.0 powerauth-rest-client-spring PowerAuth Server REST Service Client io.getlime.security powerauth-server-parent - 1.3.0 - ../pom.xml + 1.4.0 @@ -38,7 +36,6 @@ io.getlime.security powerauth-client-model - 1.3.0 io.getlime.core