From 5d67820cc0b8ba8bc95b1b390a6cf6fa62d7d781 Mon Sep 17 00:00:00 2001 From: Lubos Racansky Date: Fri, 26 Jul 2024 11:30:57 +0200 Subject: [PATCH 01/21] Fix #856: Set develop version to 1.9.0-SNAPSHOT --- pom.xml | 10 +++++----- powerauth-push-client/pom.xml | 2 +- powerauth-push-model/pom.xml | 2 +- powerauth-push-server/pom.xml | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index b8bae66fd..3dec5e178 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ io.getlime.security powerauth-push-server-parent - 1.8.0 + 1.9.0-SNAPSHOT pom @@ -69,10 +69,10 @@ - 1.10.0 - 1.8.0 - 1.8.0 - 1.8.0 + 1.11.0-SNAPSHOT + 1.9.0-SNAPSHOT + 1.9.0-SNAPSHOT + 1.9.0-SNAPSHOT 0.15.4 diff --git a/powerauth-push-client/pom.xml b/powerauth-push-client/pom.xml index 811553898..16628c204 100644 --- a/powerauth-push-client/pom.xml +++ b/powerauth-push-client/pom.xml @@ -10,7 +10,7 @@ powerauth-push-server-parent io.getlime.security - 1.8.0 + 1.9.0-SNAPSHOT diff --git a/powerauth-push-model/pom.xml b/powerauth-push-model/pom.xml index dada08e8d..83e2083f0 100644 --- a/powerauth-push-model/pom.xml +++ b/powerauth-push-model/pom.xml @@ -11,7 +11,7 @@ powerauth-push-server-parent io.getlime.security - 1.8.0 + 1.9.0-SNAPSHOT diff --git a/powerauth-push-server/pom.xml b/powerauth-push-server/pom.xml index ff1d29afa..145d6cbe8 100644 --- a/powerauth-push-server/pom.xml +++ b/powerauth-push-server/pom.xml @@ -11,7 +11,7 @@ io.getlime.security powerauth-push-server-parent - 1.8.0 + 1.9.0-SNAPSHOT From 96073e031c55bd76ccfce5d3472386d03c09089d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 00:56:50 +0000 Subject: [PATCH 02/21] Bump net.logstash.logback:logstash-logback-encoder from 7.4 to 8.0 Bumps [net.logstash.logback:logstash-logback-encoder](https://github.com/logfellow/logstash-logback-encoder) from 7.4 to 8.0. - [Release notes](https://github.com/logfellow/logstash-logback-encoder/releases) - [Commits](https://github.com/logfellow/logstash-logback-encoder/compare/logstash-logback-encoder-7.4...logstash-logback-encoder-8.0) --- updated-dependencies: - dependency-name: net.logstash.logback:logstash-logback-encoder dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3dec5e178..5d16ea885 100644 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ 0.15.4 2.6.0 9.3.0 - 7.4 + 8.0 2.6.0 From 486b9f85ce2a199f20685ad132b0978cd98dc840 Mon Sep 17 00:00:00 2001 From: Lubos Racansky Date: Fri, 2 Aug 2024 08:38:39 +0200 Subject: [PATCH 03/21] Fix #866: Javadoc error - Update maven-javadoc-plugin configuration to use delomboked sources. - Fix MavenReportException: Error while generating Javadoc. --- pom.xml | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3dec5e178..411670976 100644 --- a/pom.xml +++ b/pom.xml @@ -83,8 +83,6 @@ 2.6.0 2.2.22 - - 3.5.0 @@ -161,10 +159,38 @@ + + + org.projectlombok + lombok-maven-plugin + 1.18.20.0 + + + org.projectlombok + lombok + ${lombok.version} + + + + ${project.build.directory}/delombok + ${project.basedir}/src/main/java + false + + + + generate-sources + + delombok + + + + + org.apache.maven.plugins maven-javadoc-plugin + ${project.build.directory}/delombok false From 98703d8ff424c1e43704ebb513336ee70f9e0cc4 Mon Sep 17 00:00:00 2001 From: Lubos Racansky Date: Thu, 22 Aug 2024 11:20:02 +0200 Subject: [PATCH 04/21] Fix #868: Add missing SQL migration script for 1.7.0 --- docs/PowerAuth-Push-Server-1.7.0.md | 8 ++++++++ docs/sql/mssql/migration_1.6.0_1.7.0.sql | 19 +++++++++++++++++++ docs/sql/oracle/migration_1.6.0_1.7.0.sql | 13 +++++++++++++ docs/sql/postgresql/migration_1.6.0_1.7.0.sql | 13 +++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 docs/sql/mssql/migration_1.6.0_1.7.0.sql create mode 100644 docs/sql/oracle/migration_1.6.0_1.7.0.sql create mode 100644 docs/sql/postgresql/migration_1.6.0_1.7.0.sql diff --git a/docs/PowerAuth-Push-Server-1.7.0.md b/docs/PowerAuth-Push-Server-1.7.0.md index 34b70620e..4f3a2c0e3 100644 --- a/docs/PowerAuth-Push-Server-1.7.0.md +++ b/docs/PowerAuth-Push-Server-1.7.0.md @@ -5,6 +5,14 @@ This guide contains instructions for migration from PowerAuth Push Server versio ## Database Changes +For convenience, you can use liquibase for your database migration. + +If you prefer to make manual DB schema changes, please use the following SQL scripts: + +- [PostgreSQL script](./sql/postgresql/migration_1.6.0_1.7.0.sql) +- [Oracle script](./sql/oracle/migration_1.6.0_1.7.0.sql) +- [MSSQL script](./sql/mssql/migration_1.6.0_1.7.0.sql) + ### Huawei Mobile Services diff --git a/docs/sql/mssql/migration_1.6.0_1.7.0.sql b/docs/sql/mssql/migration_1.6.0_1.7.0.sql new file mode 100644 index 000000000..daf12533e --- /dev/null +++ b/docs/sql/mssql/migration_1.6.0_1.7.0.sql @@ -0,0 +1,19 @@ +-- Changeset powerauth-push-server/1.7.x/20240119-push_app_credentials-hms.xml::1::Lubos Racansky +-- Add hms_project_id, hms_client_id, and hms_client_secret columns to push_app_credentials +ALTER TABLE push_app_credentials ADD hms_project_id varchar(255); +GO + +ALTER TABLE push_app_credentials ADD hms_client_id varchar(255); +GO + +ALTER TABLE push_app_credentials ADD hms_client_secret varchar(255); +GO + +IF EXISTS( SELECT extended_properties.value FROM sys.extended_properties WHERE major_id = OBJECT_ID('dbo.push_app_credentials') AND name = N'MS_DESCRIPTION' AND minor_id = ( SELECT column_id FROM sys.columns WHERE name = 'hms_project_id' AND object_id = OBJECT_ID('dbo.push_app_credentials')) ) BEGIN EXEC sys.sp_updateextendedproperty @name = N'MS_Description' , @value = N'Project ID defined in Huawei AppGallery Connect.' , @level0type = N'SCHEMA' , @level0name = N'dbo' , @level1type = N'TABLE' , @level1name = N'push_app_credentials' , @level2type = N'COLUMN' , @level2name = N'hms_project_id' END ELSE BEGIN EXEC sys.sp_addextendedproperty @name = N'MS_Description' , @value = N'Project ID defined in Huawei AppGallery Connect.' , @level0type = N'SCHEMA' , @level0name = N'dbo' , @level1type = N'TABLE' , @level1name = N'push_app_credentials' , @level2type = N'COLUMN' , @level2name = N'hms_project_id' END; +GO + +IF EXISTS( SELECT extended_properties.value FROM sys.extended_properties WHERE major_id = OBJECT_ID('dbo.push_app_credentials') AND name = N'MS_DESCRIPTION' AND minor_id = ( SELECT column_id FROM sys.columns WHERE name = 'hms_client_id' AND object_id = OBJECT_ID('dbo.push_app_credentials')) ) BEGIN EXEC sys.sp_updateextendedproperty @name = N'MS_Description' , @value = N'Huawei OAuth 2.0 Client ID.' , @level0type = N'SCHEMA' , @level0name = N'dbo' , @level1type = N'TABLE' , @level1name = N'push_app_credentials' , @level2type = N'COLUMN' , @level2name = N'hms_client_id' END ELSE BEGIN EXEC sys.sp_addextendedproperty @name = N'MS_Description' , @value = N'Huawei OAuth 2.0 Client ID.' , @level0type = N'SCHEMA' , @level0name = N'dbo' , @level1type = N'TABLE' , @level1name = N'push_app_credentials' , @level2type = N'COLUMN' , @level2name = N'hms_client_id' END; +GO + +IF EXISTS( SELECT extended_properties.value FROM sys.extended_properties WHERE major_id = OBJECT_ID('dbo.push_app_credentials') AND name = N'MS_DESCRIPTION' AND minor_id = ( SELECT column_id FROM sys.columns WHERE name = 'hms_client_secret' AND object_id = OBJECT_ID('dbo.push_app_credentials')) ) BEGIN EXEC sys.sp_updateextendedproperty @name = N'MS_Description' , @value = N'Huawei OAuth 2.0 Client Secret.' , @level0type = N'SCHEMA' , @level0name = N'dbo' , @level1type = N'TABLE' , @level1name = N'push_app_credentials' , @level2type = N'COLUMN' , @level2name = N'hms_client_secret' END ELSE BEGIN EXEC sys.sp_addextendedproperty @name = N'MS_Description' , @value = N'Huawei OAuth 2.0 Client Secret.' , @level0type = N'SCHEMA' , @level0name = N'dbo' , @level1type = N'TABLE' , @level1name = N'push_app_credentials' , @level2type = N'COLUMN' , @level2name = N'hms_client_secret' END; +GO diff --git a/docs/sql/oracle/migration_1.6.0_1.7.0.sql b/docs/sql/oracle/migration_1.6.0_1.7.0.sql new file mode 100644 index 000000000..2067e7a45 --- /dev/null +++ b/docs/sql/oracle/migration_1.6.0_1.7.0.sql @@ -0,0 +1,13 @@ +-- Changeset powerauth-push-server/1.7.x/20240119-push_app_credentials-hms.xml::1::Lubos Racansky +-- Add hms_project_id, hms_client_id, and hms_client_secret columns to push_app_credentials +ALTER TABLE push_app_credentials ADD hms_project_id VARCHAR2(255); + +ALTER TABLE push_app_credentials ADD hms_client_id VARCHAR2(255); + +ALTER TABLE push_app_credentials ADD hms_client_secret VARCHAR2(255); + +COMMENT ON COLUMN push_app_credentials.hms_project_id IS 'Project ID defined in Huawei AppGallery Connect.'; + +COMMENT ON COLUMN push_app_credentials.hms_client_id IS 'Huawei OAuth 2.0 Client ID.'; + +COMMENT ON COLUMN push_app_credentials.hms_client_secret IS 'Huawei OAuth 2.0 Client Secret.'; diff --git a/docs/sql/postgresql/migration_1.6.0_1.7.0.sql b/docs/sql/postgresql/migration_1.6.0_1.7.0.sql new file mode 100644 index 000000000..6bcfc0d11 --- /dev/null +++ b/docs/sql/postgresql/migration_1.6.0_1.7.0.sql @@ -0,0 +1,13 @@ +-- Changeset powerauth-push-server/1.7.x/20240119-push_app_credentials-hms.xml::1::Lubos Racansky +-- Add hms_project_id, hms_client_id, and hms_client_secret columns to push_app_credentials +ALTER TABLE push_app_credentials ADD hms_project_id VARCHAR(255); + +ALTER TABLE push_app_credentials ADD hms_client_id VARCHAR(255); + +ALTER TABLE push_app_credentials ADD hms_client_secret VARCHAR(255); + +COMMENT ON COLUMN push_app_credentials.hms_project_id IS 'Project ID defined in Huawei AppGallery Connect.'; + +COMMENT ON COLUMN push_app_credentials.hms_client_id IS 'Huawei OAuth 2.0 Client ID.'; + +COMMENT ON COLUMN push_app_credentials.hms_client_secret IS 'Huawei OAuth 2.0 Client Secret.'; From 079a88b8deccba4c41c24b77507182d1d19c24df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 00:21:16 +0000 Subject: [PATCH 05/21] Bump org.springframework.boot:spring-boot-starter-parent Bumps [org.springframework.boot:spring-boot-starter-parent](https://github.com/spring-projects/spring-boot) from 3.3.2 to 3.3.3. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.2...v3.3.3) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-starter-parent dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5d16ea885..240ec38f6 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 3.3.2 + 3.3.3 From 44f7514b7b708baa94f34a914b8d30ba30bf9f8a Mon Sep 17 00:00:00 2001 From: Lubos Racansky Date: Tue, 3 Sep 2024 07:58:15 +0200 Subject: [PATCH 06/21] Fix #875: Unable to compile after adding support for temporary keys --- .../java/io/getlime/push/client/PowerAuthTestClientRest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerauth-push-server/src/test/java/io/getlime/push/client/PowerAuthTestClientRest.java b/powerauth-push-server/src/test/java/io/getlime/push/client/PowerAuthTestClientRest.java index ea36a11c9..6d3b7483b 100644 --- a/powerauth-push-server/src/test/java/io/getlime/push/client/PowerAuthTestClientRest.java +++ b/powerauth-push-server/src/test/java/io/getlime/push/client/PowerAuthTestClientRest.java @@ -137,7 +137,7 @@ public String createActivation(String userId) throws Exception { byte[] applicationSecretBytes = applicationSecret.getBytes(StandardCharsets.UTF_8); final String protocolVersion = "3.1"; - final EncryptorParameters encryptorParameters = new EncryptorParameters(protocolVersion, applicationKey, null); + final EncryptorParameters encryptorParameters = new EncryptorParameters(protocolVersion, applicationKey, null, null); final ClientEncryptorSecrets encryptorSecrets = new ClientEncryptorSecrets(masterPK, applicationSecretBytes); final ClientEncryptor clientEncryptor = encryptorFactory.getClientEncryptor(EncryptorId.ACTIVATION_LAYER_2, encryptorParameters, encryptorSecrets); final ByteArrayOutputStream baosL2 = new ByteArrayOutputStream(); From 6c2d0758712eec28876de7836793610b1e6053ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 06:24:49 +0000 Subject: [PATCH 07/21] Bump io.swagger.core.v3:swagger-annotations-jakarta Bumps io.swagger.core.v3:swagger-annotations-jakarta from 2.2.22 to 2.2.23. --- updated-dependencies: - dependency-name: io.swagger.core.v3:swagger-annotations-jakarta dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1c150e610..39b69df3b 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ 2.6.0 - 2.2.22 + 2.2.23 From c1b7b7194d0292afa8786c1a0e707a1b60230a05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 06:24:58 +0000 Subject: [PATCH 08/21] Bump com.google.api-client:google-api-client from 2.6.0 to 2.7.0 Bumps [com.google.api-client:google-api-client](https://github.com/googleapis/google-api-java-client) from 2.6.0 to 2.7.0. - [Release notes](https://github.com/googleapis/google-api-java-client/releases) - [Changelog](https://github.com/googleapis/google-api-java-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-api-java-client/compare/v2.6.0...v2.7.0) --- updated-dependencies: - dependency-name: com.google.api-client:google-api-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1c150e610..9ba343e7c 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ 0.15.4 - 2.6.0 + 2.7.0 9.3.0 8.0 From d0a6bf2bb9fac99318d910cc8c99ed9ac66c6b14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 00:11:20 +0000 Subject: [PATCH 09/21] Bump org.springframework.boot:spring-boot-starter-parent Bumps [org.springframework.boot:spring-boot-starter-parent](https://github.com/spring-projects/spring-boot) from 3.3.3 to 3.3.4. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.3...v3.3.4) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-starter-parent dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 752d91941..058a1b540 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 3.3.3 + 3.3.4 From 8d4c9aa1396e54e5515429993b4dde5d6cba6ba5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 00:20:27 +0000 Subject: [PATCH 10/21] Bump io.swagger.core.v3:swagger-annotations-jakarta Bumps io.swagger.core.v3:swagger-annotations-jakarta from 2.2.23 to 2.2.24. --- updated-dependencies: - dependency-name: io.swagger.core.v3:swagger-annotations-jakarta dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 058a1b540..9d71f22d0 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ 2.6.0 - 2.2.23 + 2.2.24 From 485aaa2ff5c1f58de898ce2275cbebdb6f4419f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Ra=C4=8Dansk=C3=BD?= Date: Tue, 1 Oct 2024 14:40:25 +0200 Subject: [PATCH 11/21] Fix #841: Add warning when push notification cannot be sent (#880) * Fix #841: Add warning when push notification cannot be sent due to an invalid userId or userId+activationId combination --- .../service/PushMessageSenderService.java | 14 ++++- .../controller/MockPowerAuthController.java | 2 + .../getlime/push/tests/PushServerTests.java | 62 +++++++++++-------- 3 files changed, 49 insertions(+), 29 deletions(-) diff --git a/powerauth-push-server/src/main/java/io/getlime/push/service/PushMessageSenderService.java b/powerauth-push-server/src/main/java/io/getlime/push/service/PushMessageSenderService.java index bb3cca164..77b8e21bc 100644 --- a/powerauth-push-server/src/main/java/io/getlime/push/service/PushMessageSenderService.java +++ b/powerauth-push-server/src/main/java/io/getlime/push/service/PushMessageSenderService.java @@ -261,16 +261,24 @@ private AppCredentialsEntity getAppCredentials(String appId) throws PushServerEx } // Return list of devices related to given user or activation ID (if present). List of devices is related to particular application as well. - private List getPushDevices(Long id, String userId, String activationId) throws PushServerException { + private List getPushDevices(Long appCredentialsId, String userId, String activationId) throws PushServerException { if (userId == null || userId.isEmpty()) { logger.error("No userId was specified"); throw new PushServerException("No userId was specified"); } + + final List devices; if (activationId != null) { // in case the message should go to the specific device - return pushDeviceRepository.findByUserIdAndAppCredentialsIdAndActivationId(userId, id, activationId); + devices = pushDeviceRepository.findByUserIdAndAppCredentialsIdAndActivationId(userId, appCredentialsId, activationId); } else { - return pushDeviceRepository.findByUserIdAndAppCredentialsId(userId, id); + devices = pushDeviceRepository.findByUserIdAndAppCredentialsId(userId, appCredentialsId); + } + + if (devices.isEmpty()) { + logger.warn("No device found for userId={}, appCredentialsId={}, activationId={}", userId, appCredentialsId, activationId); } + + return devices; } // Prepare and cache APNS, FCM, and HMS clients for provided app diff --git a/powerauth-push-server/src/test/java/io/getlime/push/controller/MockPowerAuthController.java b/powerauth-push-server/src/test/java/io/getlime/push/controller/MockPowerAuthController.java index d38794061..fce0e5f0f 100644 --- a/powerauth-push-server/src/test/java/io/getlime/push/controller/MockPowerAuthController.java +++ b/powerauth-push-server/src/test/java/io/getlime/push/controller/MockPowerAuthController.java @@ -94,6 +94,8 @@ public ObjectResponse getApplicationDetail() { public ObjectResponse getActivationStatus(@RequestBody ObjectRequest request) { final GetActivationStatusResponse response = new GetActivationStatusResponse(); response.setActivationId(request.getRequestObject().getActivationId()); + response.setUserId(PushServerTestClientFactory.TEST_USER_ID); + if (blockedActivations.contains(request.getRequestObject().getActivationId())) { response.setActivationStatus(ActivationStatus.BLOCKED); } else { diff --git a/powerauth-push-server/src/test/java/io/getlime/push/tests/PushServerTests.java b/powerauth-push-server/src/test/java/io/getlime/push/tests/PushServerTests.java index 87f23636d..c303b1e29 100644 --- a/powerauth-push-server/src/test/java/io/getlime/push/tests/PushServerTests.java +++ b/powerauth-push-server/src/test/java/io/getlime/push/tests/PushServerTests.java @@ -164,13 +164,15 @@ void updateDeviceStatusTest() throws Exception { } @Test - @SuppressWarnings("unchecked") //known parameters of HashMap void sendPushMessageTest() throws Exception { - boolean result = pushServerClient.createDevice(powerAuthTestClient.getApplicationId(), MOCK_PUSH_TOKEN, MobilePlatform.ANDROID, powerAuthTestClient.getActivationId()); + final boolean result = pushServerClient.createDevice(powerAuthTestClient.getApplicationId(), MOCK_PUSH_TOKEN, MobilePlatform.ANDROID, powerAuthTestClient.getActivationId()); assertTrue(result); - PushMessage pushMessage = new PushMessage(); - PushMessageAttributes attributes = new PushMessageAttributes(); - PushMessageBody pushMessageBody = new PushMessageBody(); + + final PushMessageAttributes attributes = new PushMessageAttributes(); + attributes.setSilent(false); + attributes.setPersonal(true); + + final PushMessageBody pushMessageBody = new PushMessageBody(); pushMessageBody.setTitle("Balance update"); pushMessageBody.setBody("Your balance is now $745.00"); pushMessageBody.setBadge(3); @@ -178,30 +180,28 @@ void sendPushMessageTest() throws Exception { pushMessageBody.setCategory("balance-update"); pushMessageBody.setCollapseKey("balance-update"); pushMessageBody.setValidUntil(Instant.now()); - pushMessageBody.setExtras((Map) new HashMap().put("_comment", "Any custom data.")); - attributes.setSilent(false); - attributes.setPersonal(true); - pushMessage.setUserId("Test_User"); + pushMessageBody.setExtras(Map.of("_comment", "Any custom data.")); + + final PushMessage pushMessage = new PushMessage(); + pushMessage.setUserId(PushServerTestClientFactory.TEST_USER_ID); pushMessage.setActivationId(powerAuthTestClient.getActivationId()); pushMessage.setAttributes(attributes); pushMessage.setBody(pushMessageBody); - pushMessage.setAttributes(attributes); - ObjectResponse actual = pushServerClient.sendPushMessage(powerAuthTestClient.getApplicationId(), Mode.SYNCHRONOUS, pushMessage); + + final ObjectResponse actual = pushServerClient.sendPushMessage(powerAuthTestClient.getApplicationId(), Mode.SYNCHRONOUS, pushMessage); assertEquals("OK", actual.getStatus()); - List devices = pushDeviceRepository.findByAppCredentialsAppIdAndPushToken(powerAuthTestClient.getApplicationId(), MOCK_PUSH_TOKEN); + + final List devices = pushDeviceRepository.findByAppCredentialsAppIdAndPushToken(powerAuthTestClient.getApplicationId(), MOCK_PUSH_TOKEN); pushDeviceRepository.deleteAll(devices); } @Test - @SuppressWarnings("unchecked") //known parameters of HashMap void sendPushMessageBatchTest() throws Exception { boolean result = pushServerClient.createDevice(powerAuthTestClient.getApplicationId(), MOCK_PUSH_TOKEN, MobilePlatform.ANDROID, powerAuthTestClient.getActivationId()); assertTrue(result); - List batch = new ArrayList<>(); - PushMessage pushMessage = new PushMessage(); - PushMessageAttributes attributes = new PushMessageAttributes(); - PushMessageBody pushMessageBody = new PushMessageBody(); + + final PushMessageBody pushMessageBody = new PushMessageBody(); pushMessageBody.setTitle("Balance update"); pushMessageBody.setBody("Your balance is now $745.00"); pushMessageBody.setBadge(3); @@ -209,18 +209,24 @@ void sendPushMessageBatchTest() throws Exception { pushMessageBody.setCategory("balance-update"); pushMessageBody.setCollapseKey("balance-update"); pushMessageBody.setValidUntil(Instant.now()); - pushMessageBody.setExtras((Map) new HashMap().put("_comment", "Any custom data.")); + pushMessageBody.setExtras(Map.of("_comment", "Any custom data.")); + + final PushMessageAttributes attributes = new PushMessageAttributes(); attributes.setSilent(false); attributes.setPersonal(true); - pushMessage.setUserId("Test_User"); + + final PushMessage pushMessage = new PushMessage(); + pushMessage.setUserId(PushServerTestClientFactory.TEST_USER_ID); pushMessage.setActivationId(powerAuthTestClient.getActivationId()); pushMessage.setAttributes(attributes); pushMessage.setBody(pushMessageBody); - pushMessage.setAttributes(attributes); - batch.add(pushMessage); - ObjectResponse actual = pushServerClient.sendPushMessageBatch(powerAuthTestClient.getApplicationId(), Mode.SYNCHRONOUS, batch); + + final List batch = List.of(pushMessage); + + final ObjectResponse actual = pushServerClient.sendPushMessageBatch(powerAuthTestClient.getApplicationId(), Mode.SYNCHRONOUS, batch); assertEquals("OK", actual.getStatus()); - List devices = pushDeviceRepository.findByAppCredentialsAppIdAndPushToken(powerAuthTestClient.getApplicationId(), MOCK_PUSH_TOKEN); + + final List devices = pushDeviceRepository.findByAppCredentialsAppIdAndPushToken(powerAuthTestClient.getApplicationId(), MOCK_PUSH_TOKEN); pushDeviceRepository.deleteAll(devices); } @@ -289,14 +295,18 @@ void deleteUsersFromCampaignTest() throws Exception { @Test void sendTestingCampaignTest() throws Exception { - boolean actual = pushServerClient.sendTestCampaign(1L, "Test_User"); + boolean result = pushServerClient.createDevice(powerAuthTestClient.getApplicationId(), MOCK_PUSH_TOKEN, MobilePlatform.ANDROID, powerAuthTestClient.getActivationId()); + assertTrue(result); + + final Long campaignId = createCampaign().getResponseObject().getId(); + boolean actual = pushServerClient.sendTestCampaign(campaignId, PushServerTestClientFactory.TEST_USER_ID); assertTrue(actual); } @Test void sendCampaignTest() throws Exception { - createCampaign(); - boolean result = pushServerClient.sendCampaign(1L); + final Long campaignId = createCampaign().getResponseObject().getId(); + boolean result = pushServerClient.sendCampaign(campaignId); assertTrue(result); } From f92072c15a1bfbe349c92ca056aee9642cfb621b Mon Sep 17 00:00:00 2001 From: Lubos Racansky Date: Wed, 2 Oct 2024 10:46:06 +0200 Subject: [PATCH 12/21] Add migration instructions from 1.8.x to 1.9.x --- docs/Migration-Instructions.md | 1 + docs/PowerAuth-Push-Server-1.9.0.md | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 docs/PowerAuth-Push-Server-1.9.0.md diff --git a/docs/Migration-Instructions.md b/docs/Migration-Instructions.md index c44f1837a..383114291 100644 --- a/docs/Migration-Instructions.md +++ b/docs/Migration-Instructions.md @@ -2,6 +2,7 @@ This page contains PowerAuth Push Server migration instructions. +- [PowerAuth Push Server 1.9.0](./PowerAuth-Push-Server-1.9.0.md) - [PowerAuth Push Server 1.8.0](./PowerAuth-Push-Server-1.8.0.md) - [PowerAuth Push Server 1.7.0](./PowerAuth-Push-Server-1.7.0.md) - [PowerAuth Push Server 1.6.0](./PowerAuth-Push-Server-1.6.0.md) diff --git a/docs/PowerAuth-Push-Server-1.9.0.md b/docs/PowerAuth-Push-Server-1.9.0.md new file mode 100644 index 000000000..28ee3db86 --- /dev/null +++ b/docs/PowerAuth-Push-Server-1.9.0.md @@ -0,0 +1,5 @@ +# Migration from 1.8.x to 1.9.x + +This guide contains instructions for migration from PowerAuth Push Server version `1.8.x` to version `1.9.x`. + +No migration steps nor database changes are required. From 239e73ba3ff66b035676602853ef88c709dd4187 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 00:27:33 +0000 Subject: [PATCH 13/21] Bump io.swagger.core.v3:swagger-annotations-jakarta Bumps io.swagger.core.v3:swagger-annotations-jakarta from 2.2.24 to 2.2.25. --- updated-dependencies: - dependency-name: io.swagger.core.v3:swagger-annotations-jakarta dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9d71f22d0..d6c28dd8d 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ 2.6.0 - 2.2.24 + 2.2.25 From 4a2c0234ceccb0ebc4b0a683dadbb090d67a12de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 00:27:58 +0000 Subject: [PATCH 14/21] Bump wultra-core.version from 1.11.0-SNAPSHOT to 1.11.0 Bumps `wultra-core.version` from 1.11.0-SNAPSHOT to 1.11.0. Updates `io.getlime.core:rest-model-base` from 1.11.0-SNAPSHOT to 1.11.0 - [Release notes](https://github.com/wultra/lime-java-core/releases) - [Commits](https://github.com/wultra/lime-java-core/commits/1.11.0) Updates `io.getlime.core:rest-client-base` from 1.11.0-SNAPSHOT to 1.11.0 - [Release notes](https://github.com/wultra/lime-java-core/releases) - [Commits](https://github.com/wultra/lime-java-core/commits/1.11.0) --- updated-dependencies: - dependency-name: io.getlime.core:rest-model-base dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: io.getlime.core:rest-client-base dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9d71f22d0..92d3cea36 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ - 1.11.0-SNAPSHOT + 1.11.0 1.9.0-SNAPSHOT 1.9.0-SNAPSHOT 1.9.0-SNAPSHOT From 71b2e815e61e3ba8e7d7cf0696b4c465688bbe66 Mon Sep 17 00:00:00 2001 From: Lubos Racansky Date: Thu, 10 Oct 2024 10:55:25 +0200 Subject: [PATCH 15/21] Fix #890: Add liquibase tag 1.8.0 --- .../1.8.x/20241010-add-tag-1.8.0.xml | 10 ++++++++++ .../1.8.x/db.changelog-version.xml | 1 + 2 files changed, 11 insertions(+) create mode 100644 docs/db/changelog/changesets/powerauth-push-server/1.8.x/20241010-add-tag-1.8.0.xml diff --git a/docs/db/changelog/changesets/powerauth-push-server/1.8.x/20241010-add-tag-1.8.0.xml b/docs/db/changelog/changesets/powerauth-push-server/1.8.x/20241010-add-tag-1.8.0.xml new file mode 100644 index 000000000..3ed9de58a --- /dev/null +++ b/docs/db/changelog/changesets/powerauth-push-server/1.8.x/20241010-add-tag-1.8.0.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/docs/db/changelog/changesets/powerauth-push-server/1.8.x/db.changelog-version.xml b/docs/db/changelog/changesets/powerauth-push-server/1.8.x/db.changelog-version.xml index 934825527..b937c7e68 100644 --- a/docs/db/changelog/changesets/powerauth-push-server/1.8.x/db.changelog-version.xml +++ b/docs/db/changelog/changesets/powerauth-push-server/1.8.x/db.changelog-version.xml @@ -4,5 +4,6 @@ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.9.xsd"> + \ No newline at end of file From 889590a77f97a7d8c35a7803090a169b6e37b385 Mon Sep 17 00:00:00 2001 From: Lubos Racansky Date: Fri, 11 Oct 2024 09:59:39 +0200 Subject: [PATCH 16/21] Fix #887: Set release version to 1.9.0 --- pom.xml | 2 +- powerauth-push-client/pom.xml | 2 +- powerauth-push-model/pom.xml | 2 +- powerauth-push-server/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index c189e54ff..85306b1df 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ io.getlime.security powerauth-push-server-parent - 1.9.0-SNAPSHOT + 1.9.0 pom diff --git a/powerauth-push-client/pom.xml b/powerauth-push-client/pom.xml index 16628c204..330d2da56 100644 --- a/powerauth-push-client/pom.xml +++ b/powerauth-push-client/pom.xml @@ -10,7 +10,7 @@ powerauth-push-server-parent io.getlime.security - 1.9.0-SNAPSHOT + 1.9.0 diff --git a/powerauth-push-model/pom.xml b/powerauth-push-model/pom.xml index 83e2083f0..1ce4dde8e 100644 --- a/powerauth-push-model/pom.xml +++ b/powerauth-push-model/pom.xml @@ -11,7 +11,7 @@ powerauth-push-server-parent io.getlime.security - 1.9.0-SNAPSHOT + 1.9.0 diff --git a/powerauth-push-server/pom.xml b/powerauth-push-server/pom.xml index 145d6cbe8..a7d4aa5a3 100644 --- a/powerauth-push-server/pom.xml +++ b/powerauth-push-server/pom.xml @@ -11,7 +11,7 @@ io.getlime.security powerauth-push-server-parent - 1.9.0-SNAPSHOT + 1.9.0 From a1528d8e806772bafb4b5ca56f9b1e5ad63fce77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 00:38:19 +0000 Subject: [PATCH 17/21] Bump io.getlime.security:powerauth-java-crypto Bumps [io.getlime.security:powerauth-java-crypto](https://github.com/wultra/powerauth-crypto) from 1.9.0-SNAPSHOT to 1.9.0. - [Release notes](https://github.com/wultra/powerauth-crypto/releases) - [Changelog](https://github.com/wultra/powerauth-crypto/blob/develop/docs/Releases.md) - [Commits](https://github.com/wultra/powerauth-crypto/commits/1.9.0) --- updated-dependencies: - dependency-name: io.getlime.security:powerauth-java-crypto dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c189e54ff..e39aee0bb 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ 1.11.0 1.9.0-SNAPSHOT - 1.9.0-SNAPSHOT + 1.9.0 1.9.0-SNAPSHOT From 1d97c3ce8f7f4079ca8ad1ca5ab4c5a115736785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Ra=C4=8Dansk=C3=BD?= Date: Tue, 15 Oct 2024 14:01:34 +0200 Subject: [PATCH 18/21] Fix #884: clearing the cache of the push client works only on one node (#885) * Fix #884: clearing the cache of the push client works only on one node --- docs/Configuration-Properties.md | 19 +- docs/PowerAuth-Push-Server-1.9.0.md | 15 +- docs/Push-Server-Database.md | 28 ++- .../20241011-app-credentials-timestamp.xml | 22 ++ .../1.9.x/db.changelog-version.xml | 8 + .../db.changelog-module.xml | 6 + docs/sql/mssql/migration_1.8.0_1.9.0.sql | 7 + docs/sql/oracle/migration_1.8.0_1.9.0.sql | 5 + docs/sql/postgresql/migration_1.8.0_1.9.0.sql | 5 + powerauth-push-server/pom.xml | 5 + .../configuration/CacheConfiguration.java | 56 +++++ .../StorageMapConfiguration.java | 40 ---- .../rest/AdministrationController.java | 166 ++----------- .../model/AppCredentialsEntity.java | 7 + .../push/service/AdministrationService.java | 225 ++++++++++++++++++ .../AppRelatedPushClientCacheLoader.java | 105 ++++++++ .../service/PushMessageSenderService.java | 69 +----- .../service/batch/UserDeviceItemWriter.java | 15 +- .../storage/AppCredentialStorageMap.java | 57 ----- .../storage/CampaignMessageStorageMap.java | 59 ----- .../service/batch/storage/ItemStorageMap.java | 58 ----- .../main/resources/application-dev.properties | 5 + .../src/main/resources/application.properties | 2 + 23 files changed, 536 insertions(+), 448 deletions(-) create mode 100644 docs/db/changelog/changesets/powerauth-push-server/1.9.x/20241011-app-credentials-timestamp.xml create mode 100644 docs/db/changelog/changesets/powerauth-push-server/1.9.x/db.changelog-version.xml create mode 100644 docs/sql/mssql/migration_1.8.0_1.9.0.sql create mode 100644 docs/sql/oracle/migration_1.8.0_1.9.0.sql create mode 100644 docs/sql/postgresql/migration_1.8.0_1.9.0.sql create mode 100644 powerauth-push-server/src/main/java/io/getlime/push/configuration/CacheConfiguration.java delete mode 100644 powerauth-push-server/src/main/java/io/getlime/push/configuration/StorageMapConfiguration.java create mode 100644 powerauth-push-server/src/main/java/io/getlime/push/service/AdministrationService.java create mode 100644 powerauth-push-server/src/main/java/io/getlime/push/service/AppRelatedPushClientCacheLoader.java delete mode 100644 powerauth-push-server/src/main/java/io/getlime/push/service/batch/storage/AppCredentialStorageMap.java delete mode 100644 powerauth-push-server/src/main/java/io/getlime/push/service/batch/storage/CampaignMessageStorageMap.java delete mode 100644 powerauth-push-server/src/main/java/io/getlime/push/service/batch/storage/ItemStorageMap.java diff --git a/docs/Configuration-Properties.md b/docs/Configuration-Properties.md index e46015565..d9b74c163 100644 --- a/docs/Configuration-Properties.md +++ b/docs/Configuration-Properties.md @@ -25,15 +25,16 @@ The Push Server uses the following public configuration properties: ## PowerAuth Push Service Configuration -| Property | Default | Note | -|---|---|---| -| `powerauth.push.service.applicationName` | `powerauth-push` | Technical name of the instance | -| `powerauth.push.service.applicationDisplayName` | `PowerAuth Push Server` | Display name of the instance | -| `powerauth.push.service.applicationEnvironment` | `_empty_` | Environment identifier | -| `powerauth.push.service.message.storage.enabled` | `false` | Whether persistent storing of sent messages is enabled | -| `powerauth.push.service.registration.multipleActivations.enabled` | `false` | Whether push registration supports "associated activations" | -| `powerauth.push.service.registration.retry.backoff` | `100` | Duration in milliseconds before a retry attempt during device registration in case of an insert error | -| `owerauth.push.service.registration.retry.maxAttempts` | `2` | Max number of retry attempts during device registration in case of an insert error | +| Property | Default | Note | +|-------------------------------------------------------------------|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `powerauth.push.service.applicationName` | `powerauth-push` | Technical name of the instance | +| `powerauth.push.service.applicationDisplayName` | `PowerAuth Push Server` | Display name of the instance | +| `powerauth.push.service.applicationEnvironment` | `_empty_` | Environment identifier | +| `powerauth.push.service.message.storage.enabled` | `false` | Whether persistent storing of sent messages is enabled | +| `powerauth.push.service.registration.multipleActivations.enabled` | `false` | Whether push registration supports "associated activations" | +| `powerauth.push.service.registration.retry.backoff` | `100` | Duration in milliseconds before a retry attempt during device registration in case of an insert error | +| `powerauth.push.service.registration.retry.maxAttempts` | `2` | Max number of retry attempts during device registration in case of an insert error | +| `powerauth.push.service.clients.cache.refreshAfterWrite` | `5m` | APNS, FCM and HMS client configuration is cached. It is evicted if updated via administration on a single node. This is a smart fallback for the clustered environment. | ## PowerAuth Push Campaign Setup diff --git a/docs/PowerAuth-Push-Server-1.9.0.md b/docs/PowerAuth-Push-Server-1.9.0.md index 28ee3db86..44d81c2c0 100644 --- a/docs/PowerAuth-Push-Server-1.9.0.md +++ b/docs/PowerAuth-Push-Server-1.9.0.md @@ -2,4 +2,17 @@ This guide contains instructions for migration from PowerAuth Push Server version `1.8.x` to version `1.9.x`. -No migration steps nor database changes are required. +## Database Changes + +For convenience, you can use liquibase for your database migration. + +If you prefer to make manual DB schema changes, please use the following SQL scripts: + +- [PostgreSQL script](./sql/postgresql/migration_1.8.0_1.9.0.sql) +- [Oracle script](./sql/oracle/migration_1.8.0_1.9.0.sql) +- [MSSQL script](./sql/mssql/migration_1.8.0_1.9.0.sql) + + +### App Credentials Timestamp + +To improve caching, the columns `timestamp_created`, and `timestamp_last_updated` have been added into the table `push_app_credentials`. diff --git a/docs/Push-Server-Database.md b/docs/Push-Server-Database.md index d688a6d8f..5d7e14941 100644 --- a/docs/Push-Server-Database.md +++ b/docs/Push-Server-Database.md @@ -92,7 +92,9 @@ CREATE TABLE push_app_credentials ( ios_bundle VARCHAR(255), ios_environment VARCHAR(32), android_private_key BYTEA, - android_project_id VARCHAR(255) + android_project_id VARCHAR(255), + timestamp_created TIMESTAMP(6) DEFAULT NOW() NOT NULL, + timestamp_last_updated TIMESTAMP(6) ); CREATE UNIQUE INDEX push_app_cred_app ON push_app_credentials (app_id); @@ -100,17 +102,19 @@ CREATE UNIQUE INDEX push_app_cred_app ON push_app_credentials (app_id); #### Columns -| Name | Type | Info | Note | -|-----------------------|--------------|-----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `id` | INTEGER | primary key, index, autoincrement | Unique credential record ID. | -| `app_id` | VARCHAR(255) | index | Associated application ID. | -| `ios_key_id` | VARCHAR(255) | - | Key ID used for identifying a private key in APNs service. | -| `ios_private_key` | BYTEA | - | Binary representation of P8 file with private key used for Apple's APNs service. | -| `ios_team_id` | VARCHAR(255) | - | Team ID used for sending push notifications. | -| `ios_bundle` | VARCHAR(255) | - | Application bundle ID, used as a APNs "topic". | -| `ios_environment` | VARCHAR(32) | - | Per-application APNs environment setting. `NULL` or unknown value inherits from global server configuration, values `development` or `production` override the settings. | -| `android_private_key` | BYTEA | - | Firebase service account private key used when obtaining access tokens for FCM HTTP v1 API. | -| `android_project_id` | VARCHAR(255) | - | Firebase project ID, used when sending push messages using FCM. | +| Name | Type | Info | Note | +|--------------------------|--------------|--------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `id` | INTEGER | primary key, index, autoincrement | Unique credential record ID. | +| `app_id` | VARCHAR(255) | index | Associated application ID. | +| `ios_key_id` | VARCHAR(255) | - | Key ID used for identifying a private key in APNs service. | +| `ios_private_key` | BYTEA | - | Binary representation of P8 file with private key used for Apple's APNs service. | +| `ios_team_id` | VARCHAR(255) | - | Team ID used for sending push notifications. | +| `ios_bundle` | VARCHAR(255) | - | Application bundle ID, used as a APNs "topic". | +| `ios_environment` | VARCHAR(32) | - | Per-application APNs environment setting. `NULL` or unknown value inherits from global server configuration, values `development` or `production` override the settings. | +| `android_private_key` | BYTEA | - | Firebase service account private key used when obtaining access tokens for FCM HTTP v1 API. | +| `android_project_id` | VARCHAR(255) | - | Firebase project ID, used when sending push messages using FCM. | +| `timestamp_created` | TIMESTAMP | `NOT NULL DEFAULT CURRENT_TIMESTAMP` | Timestamp when the record was created. | +| `timestamp_last_updated` | TIMESTAMP | | Timestamp when the record was last updated. | #### Keys diff --git a/docs/db/changelog/changesets/powerauth-push-server/1.9.x/20241011-app-credentials-timestamp.xml b/docs/db/changelog/changesets/powerauth-push-server/1.9.x/20241011-app-credentials-timestamp.xml new file mode 100644 index 000000000..74f67317c --- /dev/null +++ b/docs/db/changelog/changesets/powerauth-push-server/1.9.x/20241011-app-credentials-timestamp.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + Add columns timestamp_last_updated and timestamp_created to push_app_credentials table + + + + + + + + + \ No newline at end of file diff --git a/docs/db/changelog/changesets/powerauth-push-server/1.9.x/db.changelog-version.xml b/docs/db/changelog/changesets/powerauth-push-server/1.9.x/db.changelog-version.xml new file mode 100644 index 000000000..9d39dcb65 --- /dev/null +++ b/docs/db/changelog/changesets/powerauth-push-server/1.9.x/db.changelog-version.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/docs/db/changelog/changesets/powerauth-push-server/db.changelog-module.xml b/docs/db/changelog/changesets/powerauth-push-server/db.changelog-module.xml index 9b81cae22..6b91e95c0 100644 --- a/docs/db/changelog/changesets/powerauth-push-server/db.changelog-module.xml +++ b/docs/db/changelog/changesets/powerauth-push-server/db.changelog-module.xml @@ -3,6 +3,11 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.9.xsd"> + + + + + @@ -11,5 +16,6 @@ + diff --git a/docs/sql/mssql/migration_1.8.0_1.9.0.sql b/docs/sql/mssql/migration_1.8.0_1.9.0.sql new file mode 100644 index 000000000..aff4076bd --- /dev/null +++ b/docs/sql/mssql/migration_1.8.0_1.9.0.sql @@ -0,0 +1,7 @@ +-- Changeset powerauth-push-server/1.9.x/20241011-app-credentials-timestamp.xml::1::Lubos Racansky +-- Add columns timestamp_last_updated and timestamp_created to push_app_credentials table +ALTER TABLE push_app_credentials ADD timestamp_created datetime2 CONSTRAINT DF_push_app_credentials_timestamp_created DEFAULT GETDATE() NOT NULL; +GO + +ALTER TABLE push_app_credentials ADD timestamp_last_updated datetime2; +GO diff --git a/docs/sql/oracle/migration_1.8.0_1.9.0.sql b/docs/sql/oracle/migration_1.8.0_1.9.0.sql new file mode 100644 index 000000000..afe02b682 --- /dev/null +++ b/docs/sql/oracle/migration_1.8.0_1.9.0.sql @@ -0,0 +1,5 @@ +-- Changeset powerauth-push-server/1.9.x/20241011-app-credentials-timestamp.xml::1::Lubos Racansky +-- Add columns timestamp_last_updated and timestamp_created to push_app_credentials table +ALTER TABLE push_app_credentials ADD timestamp_created TIMESTAMP DEFAULT sysdate NOT NULL; + +ALTER TABLE push_app_credentials ADD timestamp_last_updated TIMESTAMP; diff --git a/docs/sql/postgresql/migration_1.8.0_1.9.0.sql b/docs/sql/postgresql/migration_1.8.0_1.9.0.sql new file mode 100644 index 000000000..f6081e2de --- /dev/null +++ b/docs/sql/postgresql/migration_1.8.0_1.9.0.sql @@ -0,0 +1,5 @@ +-- Changeset powerauth-push-server/1.9.x/20241011-app-credentials-timestamp.xml::1::Lubos Racansky +-- Add columns timestamp_last_updated and timestamp_created to push_app_credentials table +ALTER TABLE push_app_credentials ADD timestamp_created TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL; + +ALTER TABLE push_app_credentials ADD timestamp_last_updated TIMESTAMP WITHOUT TIME ZONE; diff --git a/powerauth-push-server/pom.xml b/powerauth-push-server/pom.xml index 145d6cbe8..bc33b3980 100644 --- a/powerauth-push-server/pom.xml +++ b/powerauth-push-server/pom.xml @@ -158,6 +158,11 @@ jackson-datatype-jsr310 + + com.github.ben-manes.caffeine + caffeine + + io.netty diff --git a/powerauth-push-server/src/main/java/io/getlime/push/configuration/CacheConfiguration.java b/powerauth-push-server/src/main/java/io/getlime/push/configuration/CacheConfiguration.java new file mode 100644 index 000000000..532f3a9b5 --- /dev/null +++ b/powerauth-push-server/src/main/java/io/getlime/push/configuration/CacheConfiguration.java @@ -0,0 +1,56 @@ +/* + * Copyright 2024 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.push.configuration; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import io.getlime.push.service.AppRelatedPushClient; +import io.getlime.push.service.AppRelatedPushClientCacheLoader; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.Duration; + + +/** + * Cache configuration. + * + * @author Lubos Racansky, lubos.racansky@wultra.com + */ +@Configuration +@Slf4j +public class CacheConfiguration { + + /** + * Configure cache for {@link AppRelatedPushClient}. + * + * @return cache for AppRelatedPushClient + */ + @Bean + public LoadingCache appRelatedPushClientCache( + @Value("${powerauth.push.service.clients.cache.refreshAfterWrite}") final Duration refreshAfterWrite, + final AppRelatedPushClientCacheLoader cacheLoader) { + + logger.info("Initializing AppRelatedPushClient cache with refreshAfterWrite={}", refreshAfterWrite); + return Caffeine.newBuilder() + .refreshAfterWrite(refreshAfterWrite) + .build(cacheLoader); + } + +} diff --git a/powerauth-push-server/src/main/java/io/getlime/push/configuration/StorageMapConfiguration.java b/powerauth-push-server/src/main/java/io/getlime/push/configuration/StorageMapConfiguration.java deleted file mode 100644 index 662e7585d..000000000 --- a/powerauth-push-server/src/main/java/io/getlime/push/configuration/StorageMapConfiguration.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2016 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.push.configuration; - -import io.getlime.push.service.batch.storage.AppCredentialStorageMap; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - - -/** - * Configuration class for in memory maps - * - * @author Martin Tupy, martin.tupy.work@gmail.com - */ -@Configuration -public class StorageMapConfiguration { - - /** - * Bean definition for app credentials map - * @return Bean with app credential storage map. - */ - @Bean - public AppCredentialStorageMap appCredentialStorageMap() { - return new AppCredentialStorageMap(); - } -} diff --git a/powerauth-push-server/src/main/java/io/getlime/push/controller/rest/AdministrationController.java b/powerauth-push-server/src/main/java/io/getlime/push/controller/rest/AdministrationController.java index 8b0825d9b..5183e325b 100644 --- a/powerauth-push-server/src/main/java/io/getlime/push/controller/rest/AdministrationController.java +++ b/powerauth-push-server/src/main/java/io/getlime/push/controller/rest/AdministrationController.java @@ -15,8 +15,6 @@ */ package io.getlime.push.controller.rest; -import com.wultra.security.powerauth.client.PowerAuthClient; -import com.wultra.security.powerauth.client.model.entity.Application; import com.wultra.security.powerauth.client.model.error.PowerAuthClientException; import io.getlime.core.rest.model.base.request.ObjectRequest; import io.getlime.core.rest.model.base.response.ObjectResponse; @@ -29,48 +27,27 @@ import io.getlime.push.model.response.GetApplicationDetailResponse; import io.getlime.push.model.response.GetApplicationListResponse; import io.getlime.push.model.validator.*; -import io.getlime.push.repository.AppCredentialsRepository; import io.getlime.push.repository.model.AppCredentialsEntity; -import io.getlime.push.service.batch.storage.AppCredentialStorageMap; -import io.getlime.push.service.http.HttpCustomizationService; +import io.getlime.push.service.AdministrationService; import jakarta.validation.Valid; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; -import java.util.*; +import java.util.List; /** * Controller for administering the push server. * * @author Roman Strobl, roman.strobl@wultra.com */ +@Slf4j +@AllArgsConstructor @RestController @RequestMapping(value = "admin/app") public class AdministrationController { - private static final Logger logger = LoggerFactory.getLogger(AdministrationController.class); - - private final PowerAuthClient powerAuthClient; - private final AppCredentialsRepository appCredentialsRepository; - private final AppCredentialStorageMap appCredentialStorageMap; - private final HttpCustomizationService httpCustomizationService; - - /** - * Constructor with injected fields. - * @param powerAuthClient PowerAuth service client. - * @param appCredentialsRepository Application credentials repository. - * @param appCredentialStorageMap Application credentials storage map. - * @param httpCustomizationService HTTP customization service. - */ - @Autowired - public AdministrationController(PowerAuthClient powerAuthClient, AppCredentialsRepository appCredentialsRepository, AppCredentialStorageMap appCredentialStorageMap, HttpCustomizationService httpCustomizationService) { - this.powerAuthClient = powerAuthClient; - this.appCredentialsRepository = appCredentialsRepository; - this.appCredentialStorageMap = appCredentialStorageMap; - this.httpCustomizationService = httpCustomizationService; - } + private final AdministrationService administrationService; /** * List applications configured in Push Server. @@ -79,18 +56,9 @@ public AdministrationController(PowerAuthClient powerAuthClient, AppCredentialsR @GetMapping(value = "list") public ObjectResponse listApplications() { logger.debug("Received listApplications request"); + final List applications = administrationService.findAllApplications(); final GetApplicationListResponse response = new GetApplicationListResponse(); - final Iterable appCredentials = appCredentialsRepository.findAll(); - final List appList = new ArrayList<>(); - for (AppCredentialsEntity appCredentialsEntity : appCredentials) { - final PushServerApplication app = new PushServerApplication(); - app.setAppId(appCredentialsEntity.getAppId()); - app.setIos(appCredentialsEntity.getIosPrivateKey() != null); - app.setAndroid(appCredentialsEntity.getAndroidPrivateKey() != null); - app.setHuawei(isHuawei(appCredentialsEntity)); - appList.add(app); - } - response.setApplicationList(appList); + response.setApplicationList(applications); logger.debug("The listApplications request succeeded"); return new ObjectResponse<>(response); } @@ -104,27 +72,9 @@ public ObjectResponse listApplications() { public ObjectResponse listUnconfiguredApplications() throws PushServerException { try { logger.debug("Received listUnconfiguredApplications request"); + final List applications = administrationService.findUnconfiguredApplications(); final GetApplicationListResponse response = new GetApplicationListResponse(); - - // Get all applications in PA Server - final List applicationList = getApplicationList().getApplications(); - - // Get all applications that are already set up - final Iterable appCredentials = appCredentialsRepository.findAll(); - - // Compute intersection by app ID - final Set identifiers = new HashSet<>(); - for (AppCredentialsEntity appCred: appCredentials) { - identifiers.add(appCred.getAppId()); - } - for (Application app : applicationList) { - if (!identifiers.contains(app.getApplicationId())) { - final PushServerApplication applicationToAdd = new PushServerApplication(); - applicationToAdd.setAppId(app.getApplicationId()); - // add apps in intersection - response.getApplicationList().add(applicationToAdd); - } - } + response.setApplicationList(applications); logger.debug("The listUnconfiguredApplications request succeeded"); return new ObjectResponse<>(response); } catch (PowerAuthClientException ex) { @@ -148,7 +98,7 @@ public ObjectResponse getApplicationDetail(@Reques throw new PushServerException(errorMessage); } final GetApplicationDetailResponse response = new GetApplicationDetailResponse(); - final AppCredentialsEntity appCredentialsEntity = findAppCredentialsEntityById(requestObject.getAppId()); + final AppCredentialsEntity appCredentialsEntity = administrationService.findAppCredentials(requestObject.getAppId()); final PushServerApplication app = new PushServerApplication(); app.setAppId(appCredentialsEntity.getAppId()); app.setIos(appCredentialsEntity.getIosPrivateKey() != null); @@ -188,18 +138,8 @@ public ObjectResponse createApplication(@RequestBody throw new PushServerException("Request object must not be empty"); } logger.info("Received createApplication request, application ID: {}", requestObject.getAppId()); - String errorMessage = CreateApplicationRequestValidator.validate(requestObject); - final Optional appCredentialsEntityOptional = appCredentialsRepository.findFirstByAppId(requestObject.getAppId()); - if (appCredentialsEntityOptional.isPresent()) { - errorMessage = "Application already exists"; - } - if (errorMessage != null) { - throw new PushServerException(errorMessage); - } - final AppCredentialsEntity appCredentialsEntity = new AppCredentialsEntity(); - appCredentialsEntity.setAppId(requestObject.getAppId()); - final AppCredentialsEntity newAppCredentialsEntity = appCredentialsRepository.save(appCredentialsEntity); - final CreateApplicationResponse response = new CreateApplicationResponse(newAppCredentialsEntity.getAppId()); + final AppCredentialsEntity appCredentials = administrationService.createAppCredentials(requestObject); + final CreateApplicationResponse response = new CreateApplicationResponse(appCredentials.getAppId()); logger.info("The createApplication request succeeded, application ID: {}", requestObject.getAppId()); return new ObjectResponse<>(response); } @@ -221,25 +161,10 @@ public Response updateIos(@RequestBody ObjectRequest request) if (errorMessage != null) { throw new PushServerException(errorMessage); } - final AppCredentialsEntity appCredentialsEntity = findAppCredentialsEntityById(requestObject.getAppId()); - final byte[] privateKeyBytes = Base64.getDecoder().decode(requestObject.getPrivateKeyBase64()); - appCredentialsEntity.setIosPrivateKey(privateKeyBytes); - appCredentialsEntity.setIosTeamId(requestObject.getTeamId()); - appCredentialsEntity.setIosKeyId(requestObject.getKeyId()); - appCredentialsEntity.setIosBundle(requestObject.getBundle()); - appCredentialsEntity.setIosEnvironment(convert(requestObject.getEnvironment())); - appCredentialsRepository.save(appCredentialsEntity); - appCredentialStorageMap.cleanByKey(appCredentialsEntity.getAppId()); - logger.info("The updateIos request succeeded, application credentials entity ID: {}", appCredentialsEntity.getId()); + administrationService.updateIosAppCredentials(requestObject); return new Response(); } - private static String convert(final ApnsEnvironment environment) { - return Optional.ofNullable(environment) - .map(ApnsEnvironment::getKey) - .orElse(null); - } - /** * Remove iOS configuration. * @param request Remove iOS configuration request. @@ -257,15 +182,7 @@ public Response removeIos(@RequestBody ObjectRequest request) if (errorMessage != null) { throw new PushServerException(errorMessage); } - final AppCredentialsEntity appCredentialsEntity = findAppCredentialsEntityById(requestObject.getAppId()); - appCredentialsEntity.setIosPrivateKey(null); - appCredentialsEntity.setIosTeamId(null); - appCredentialsEntity.setIosKeyId(null); - appCredentialsEntity.setIosBundle(null); - appCredentialsEntity.setIosEnvironment(null); - appCredentialsRepository.save(appCredentialsEntity); - appCredentialStorageMap.cleanByKey(appCredentialsEntity.getAppId()); - logger.info("The removeIos request succeeded, application credentials entity ID: {}", appCredentialsEntity.getId()); + administrationService.removeIosAppCredentials(requestObject.getAppId()); return new Response(); } @@ -286,13 +203,7 @@ public Response updateAndroid(@RequestBody ObjectRequest r if (errorMessage != null) { throw new PushServerException(errorMessage); } - final AppCredentialsEntity appCredentialsEntity = findAppCredentialsEntityById(requestObject.getAppId()); - final byte[] privateKeyBytes = Base64.getDecoder().decode(requestObject.getPrivateKeyBase64()); - appCredentialsEntity.setAndroidPrivateKey(privateKeyBytes); - appCredentialsEntity.setAndroidProjectId(requestObject.getProjectId()); - appCredentialsRepository.save(appCredentialsEntity); - appCredentialStorageMap.cleanByKey(appCredentialsEntity.getAppId()); - logger.info("The updateAndroid request succeeded, application credentials entity ID: {}", appCredentialsEntity.getId()); + administrationService.updateAndroidAppCredentials(requestObject); return new Response(); } @@ -313,12 +224,7 @@ public Response removeAndroid(@RequestBody ObjectRequest r if (errorMessage != null) { throw new PushServerException(errorMessage); } - final AppCredentialsEntity appCredentialsEntity = findAppCredentialsEntityById(requestObject.getAppId()); - appCredentialsEntity.setAndroidPrivateKey(null); - appCredentialsEntity.setAndroidProjectId(null); - appCredentialsRepository.save(appCredentialsEntity); - appCredentialStorageMap.cleanByKey(appCredentialsEntity.getAppId()); - logger.info("The removeAndroid request succeeded, application credentials entity ID: {}", appCredentialsEntity.getId()); + administrationService.removeAndroidAppCredentials(requestObject.getAppId()); return new Response(); } @@ -333,14 +239,7 @@ public Response removeAndroid(@RequestBody ObjectRequest r public Response updateHuawei(@Valid @RequestBody ObjectRequest request) throws PushServerException { final UpdateHuaweiRequest requestObject = request.getRequestObject(); logger.info("Received update Huawei request, application ID: {}", requestObject.getAppId()); - - final AppCredentialsEntity appCredentialsEntity = findAppCredentialsEntityById(requestObject.getAppId()); - appCredentialsEntity.setHmsProjectId(requestObject.getProjectId()); - appCredentialsEntity.setHmsClientId(requestObject.getClientId()); - appCredentialsEntity.setHmsClientSecret(requestObject.getClientSecret()); - appCredentialsRepository.save(appCredentialsEntity); - appCredentialStorageMap.cleanByKey(appCredentialsEntity.getAppId()); - logger.info("The update Huawei request succeeded, application credentials entity ID: {}", appCredentialsEntity.getId()); + administrationService.updateHuaweiAppCredentials(requestObject); return new Response(); } @@ -355,32 +254,7 @@ public Response updateHuawei(@Valid @RequestBody ObjectRequest request) throws PushServerException { final RemoveHuaweiRequest requestObject = request.getRequestObject(); logger.info("Received remove Huawei request, application ID: {}", requestObject.getAppId()); - - final AppCredentialsEntity appCredentialsEntity = findAppCredentialsEntityById(requestObject.getAppId()); - appCredentialsEntity.setHmsProjectId(null); - appCredentialsEntity.setHmsClientSecret(null); - appCredentialsEntity.setHmsClientId(null); - appCredentialsRepository.save(appCredentialsEntity); - appCredentialStorageMap.cleanByKey(appCredentialsEntity.getAppId()); - logger.info("The remove Huawei request succeeded, application credentials entity ID: {}", appCredentialsEntity.getId()); + administrationService.removeHuaweiAppCredentials(requestObject.getAppId()); return new Response(); } - - /** - * Find app credentials entity by ID. - * @param powerAuthAppId App credentials ID. - * @return App credentials entity. - * @throws PushServerException Thrown when application credentials entity does not exists. - */ - private AppCredentialsEntity findAppCredentialsEntityById(String powerAuthAppId) throws PushServerException { - return appCredentialsRepository.findFirstByAppId(powerAuthAppId).orElseThrow(() -> - new PushServerException("Application credentials with entered ID does not exist")); - } - - private com.wultra.security.powerauth.client.model.response.GetApplicationListResponse getApplicationList() throws PowerAuthClientException { - return powerAuthClient.getApplicationList( - httpCustomizationService.getQueryParams(), - httpCustomizationService.getHttpHeaders() - ); - } } diff --git a/powerauth-push-server/src/main/java/io/getlime/push/repository/model/AppCredentialsEntity.java b/powerauth-push-server/src/main/java/io/getlime/push/repository/model/AppCredentialsEntity.java index c51cda195..cc0a21b44 100644 --- a/powerauth-push-server/src/main/java/io/getlime/push/repository/model/AppCredentialsEntity.java +++ b/powerauth-push-server/src/main/java/io/getlime/push/repository/model/AppCredentialsEntity.java @@ -22,6 +22,7 @@ import java.io.Serial; import java.io.Serializable; +import java.time.LocalDateTime; /** * Class representing application tokens used to authenticate against APNs, FCM, or HMS services. @@ -112,4 +113,10 @@ public class AppCredentialsEntity implements Serializable { @Column(name = "hms_client_secret") private String hmsClientSecret; + @Column(name = "timestamp_created", nullable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") + private LocalDateTime timestampCreated = LocalDateTime.now(); + + @Column(name = "timestamp_last_updated") + private LocalDateTime timestampLastUpdated; + } diff --git a/powerauth-push-server/src/main/java/io/getlime/push/service/AdministrationService.java b/powerauth-push-server/src/main/java/io/getlime/push/service/AdministrationService.java new file mode 100644 index 000000000..404938429 --- /dev/null +++ b/powerauth-push-server/src/main/java/io/getlime/push/service/AdministrationService.java @@ -0,0 +1,225 @@ +/* + * Copyright 2024 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.push.service; + +import com.github.benmanes.caffeine.cache.LoadingCache; +import com.wultra.security.powerauth.client.PowerAuthClient; +import com.wultra.security.powerauth.client.model.entity.Application; +import com.wultra.security.powerauth.client.model.error.PowerAuthClientException; +import io.getlime.push.errorhandling.exceptions.PushServerException; +import io.getlime.push.model.entity.PushServerApplication; +import io.getlime.push.model.enumeration.ApnsEnvironment; +import io.getlime.push.model.request.CreateApplicationRequest; +import io.getlime.push.model.request.UpdateAndroidRequest; +import io.getlime.push.model.request.UpdateHuaweiRequest; +import io.getlime.push.model.request.UpdateIosRequest; +import io.getlime.push.model.validator.CreateApplicationRequestValidator; +import io.getlime.push.repository.AppCredentialsRepository; +import io.getlime.push.repository.model.AppCredentialsEntity; +import io.getlime.push.service.http.HttpCustomizationService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.StreamSupport; + +/** + * Administration service. + * + * @author Lubos Racansky, lubos.racansky@wultra.com + */ +@Service +@Transactional +@AllArgsConstructor +@Slf4j +public class AdministrationService { + + private final PowerAuthClient powerAuthClient; + private final AppCredentialsRepository appCredentialsRepository; + private final LoadingCache appRelatedPushClientCache; + private final HttpCustomizationService httpCustomizationService; + + @Transactional(readOnly = true) + public List findAllApplications() { + return StreamSupport.stream(appCredentialsRepository.findAll().spliterator(),false) + .map(appCredentialsEntity -> { + final PushServerApplication app = new PushServerApplication(); + app.setAppId(appCredentialsEntity.getAppId()); + app.setIos(appCredentialsEntity.getIosPrivateKey() != null); + app.setAndroid(appCredentialsEntity.getAndroidPrivateKey() != null); + app.setHuawei(isHuawei(appCredentialsEntity)); + return app; + }) + .toList(); + } + + @Transactional(readOnly = true) + public List findUnconfiguredApplications() throws PowerAuthClientException { + // Get all applications in PA Server + final List applicationList = getApplicationList().getApplications(); + + // Get all applications that are already set up + final Iterable appCredentials = appCredentialsRepository.findAll(); + + // Compute intersection by app ID + final Set identifiers = new HashSet<>(); + for (AppCredentialsEntity appCred: appCredentials) { + identifiers.add(appCred.getAppId()); + } + + final List result = new ArrayList<>(); + for (Application app : applicationList) { + if (!identifiers.contains(app.getApplicationId())) { + final PushServerApplication applicationToAdd = new PushServerApplication(); + applicationToAdd.setAppId(app.getApplicationId()); + result.add(applicationToAdd); + } + } + + return result; + } + + @Transactional(readOnly = true) + public AppCredentialsEntity findAppCredentials(final String appId) throws PushServerException { + return findAppCredentialsByAppId(appId); + } + + public AppCredentialsEntity createAppCredentials(final CreateApplicationRequest request) throws PushServerException { + String errorMessage = CreateApplicationRequestValidator.validate(request); + final Optional appCredentialsEntityOptional = appCredentialsRepository.findFirstByAppId(request.getAppId()); + if (appCredentialsEntityOptional.isPresent()) { + errorMessage = "Application already exists"; + } + if (errorMessage != null) { + throw new PushServerException(errorMessage); + } + final AppCredentialsEntity appCredentialsEntity = new AppCredentialsEntity(); + appCredentialsEntity.setAppId(request.getAppId()); + return appCredentialsRepository.save(appCredentialsEntity); + } + + public void updateIosAppCredentials(final UpdateIosRequest request) throws PushServerException { + final AppCredentialsEntity appCredentialsEntity = findAppCredentialsByAppId(request.getAppId()); + final byte[] privateKeyBytes = Base64.getDecoder().decode(request.getPrivateKeyBase64()); + appCredentialsEntity.setIosPrivateKey(privateKeyBytes); + appCredentialsEntity.setIosTeamId(request.getTeamId()); + appCredentialsEntity.setIosKeyId(request.getKeyId()); + appCredentialsEntity.setIosBundle(request.getBundle()); + appCredentialsEntity.setIosEnvironment(convert(request.getEnvironment())); + appCredentialsEntity.setTimestampLastUpdated(LocalDateTime.now()); + appCredentialsRepository.save(appCredentialsEntity); + refreshCacheAfterCommit(appCredentialsEntity.getAppId()); + logger.info("The updateIos request succeeded, application credentials entity ID: {}", appCredentialsEntity.getId()); + } + + public void removeIosAppCredentials(final String appId) throws PushServerException { + final AppCredentialsEntity appCredentialsEntity = findAppCredentialsByAppId(appId); + appCredentialsEntity.setIosPrivateKey(null); + appCredentialsEntity.setIosTeamId(null); + appCredentialsEntity.setIosKeyId(null); + appCredentialsEntity.setIosBundle(null); + appCredentialsEntity.setIosEnvironment(null); + appCredentialsEntity.setTimestampLastUpdated(LocalDateTime.now()); + appCredentialsRepository.save(appCredentialsEntity); + refreshCacheAfterCommit(appCredentialsEntity.getAppId()); + logger.info("The removeIos request succeeded, application credentials entity ID: {}", appCredentialsEntity.getId()); + } + + public void updateAndroidAppCredentials(final UpdateAndroidRequest request) throws PushServerException { + final AppCredentialsEntity appCredentialsEntity = findAppCredentialsByAppId(request.getAppId()); + final byte[] privateKeyBytes = Base64.getDecoder().decode(request.getPrivateKeyBase64()); + appCredentialsEntity.setAndroidPrivateKey(privateKeyBytes); + appCredentialsEntity.setAndroidProjectId(request.getProjectId()); + appCredentialsEntity.setTimestampLastUpdated(LocalDateTime.now()); + appCredentialsRepository.save(appCredentialsEntity); + refreshCacheAfterCommit(appCredentialsEntity.getAppId()); + logger.info("The updateAndroid request succeeded, application credentials entity ID: {}", appCredentialsEntity.getId()); + } + + public void removeAndroidAppCredentials(final String appId) throws PushServerException { + final AppCredentialsEntity appCredentialsEntity = findAppCredentialsByAppId(appId); + appCredentialsEntity.setAndroidPrivateKey(null); + appCredentialsEntity.setAndroidProjectId(null); + appCredentialsEntity.setTimestampLastUpdated(LocalDateTime.now()); + appCredentialsRepository.save(appCredentialsEntity); + refreshCacheAfterCommit(appCredentialsEntity.getAppId()); + logger.info("The removeAndroid request succeeded, application credentials entity ID: {}", appCredentialsEntity.getId()); + } + + public void updateHuaweiAppCredentials(final UpdateHuaweiRequest request) throws PushServerException { + final AppCredentialsEntity appCredentialsEntity = findAppCredentialsByAppId(request.getAppId()); + appCredentialsEntity.setHmsProjectId(request.getProjectId()); + appCredentialsEntity.setHmsClientId(request.getClientId()); + appCredentialsEntity.setHmsClientSecret(request.getClientSecret()); + appCredentialsEntity.setTimestampLastUpdated(LocalDateTime.now()); + appCredentialsRepository.save(appCredentialsEntity); + refreshCacheAfterCommit(appCredentialsEntity.getAppId()); + logger.info("The update Huawei request succeeded, application credentials entity ID: {}", appCredentialsEntity.getId()); + } + + public void removeHuaweiAppCredentials(final String appId) throws PushServerException { + final AppCredentialsEntity appCredentialsEntity = findAppCredentialsByAppId(appId); + appCredentialsEntity.setHmsProjectId(null); + appCredentialsEntity.setHmsClientSecret(null); + appCredentialsEntity.setHmsClientId(null); + appCredentialsEntity.setTimestampLastUpdated(LocalDateTime.now()); + appCredentialsRepository.save(appCredentialsEntity); + refreshCacheAfterCommit(appId); + logger.info("The remove Huawei request succeeded, application credentials entity ID: {}", appCredentialsEntity.getId()); + } + + private void refreshCacheAfterCommit(final String appId) { + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + @Override + public void afterCommit() { + appRelatedPushClientCache.refresh(appId); + } + }); + } + + /** + * Find app credentials entity by ID. + * @param powerAuthAppId App credentials ID. + * @return App credentials entity. + * @throws PushServerException Thrown when application credentials entity does not exists. + */ + private AppCredentialsEntity findAppCredentialsByAppId(String powerAuthAppId) throws PushServerException { + return appCredentialsRepository.findFirstByAppId(powerAuthAppId).orElseThrow(() -> + new PushServerException("Application credentials with entered ID: %s does not exist".formatted(powerAuthAppId))); + } + + private com.wultra.security.powerauth.client.model.response.GetApplicationListResponse getApplicationList() throws PowerAuthClientException { + return powerAuthClient.getApplicationList( + httpCustomizationService.getQueryParams(), + httpCustomizationService.getHttpHeaders() + ); + } + + private static String convert(final ApnsEnvironment environment) { + return Optional.ofNullable(environment) + .map(ApnsEnvironment::getKey) + .orElse(null); + } + + private static boolean isHuawei(final AppCredentialsEntity appCredentialsEntity) { + return appCredentialsEntity.getHmsClientSecret() != null && appCredentialsEntity.getHmsClientId() != null; + } +} diff --git a/powerauth-push-server/src/main/java/io/getlime/push/service/AppRelatedPushClientCacheLoader.java b/powerauth-push-server/src/main/java/io/getlime/push/service/AppRelatedPushClientCacheLoader.java new file mode 100644 index 000000000..37a66440c --- /dev/null +++ b/powerauth-push-server/src/main/java/io/getlime/push/service/AppRelatedPushClientCacheLoader.java @@ -0,0 +1,105 @@ +/* + * Copyright 2024 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.push.service; + +import com.eatthepath.pushy.apns.ApnsClient; +import com.github.benmanes.caffeine.cache.CacheLoader; +import io.getlime.push.errorhandling.exceptions.PushServerException; +import io.getlime.push.repository.AppCredentialsRepository; +import io.getlime.push.repository.model.AppCredentialsEntity; +import io.getlime.push.service.fcm.FcmClient; +import io.getlime.push.service.hms.HmsClient; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.Objects; + +/** + * Specialization of {@link CacheLoader} for {@link AppRelatedPushClient}. + * + * @author Lubos Racansky, lubos.racansky@wultra.com + */ +@AllArgsConstructor +@Slf4j +@Component +public class AppRelatedPushClientCacheLoader implements CacheLoader { + + private final AppCredentialsRepository appCredentialsRepository; + + private final PushSendingWorker pushSendingWorker; + + /** + * Smartly reload {@link AppRelatedPushClient}. + * Fetch {@link AppCredentialsEntity#getTimestampLastUpdated()} and reload only if the value differs from the value store in the cache. + *

+ * {@inheritDoc} + */ + @Override + public AppRelatedPushClient reload(final String appId, final AppRelatedPushClient oldAppRelatedPushClient) throws Exception { + final AppCredentialsEntity credentials = appCredentialsRepository.findFirstByAppId(appId).orElse(null); + if (credentials == null) { + logger.warn("AppCredentials does not exist anymore for app: {}", appId); + return null; + } + + final LocalDateTime lastUpdatedInDb = credentials.getTimestampLastUpdated(); + final LocalDateTime lastUpdatedInCache = oldAppRelatedPushClient.getAppCredentials().getTimestampLastUpdated(); + if (Objects.equals(lastUpdatedInCache, lastUpdatedInDb)) { + logger.debug("LastUpdated is same for app: {}", appId); + return oldAppRelatedPushClient; + } + + logger.debug("LastUpdated differs for app: {}", appId); + return createPushClient(credentials); + } + + @Override + public AppRelatedPushClient load(final String appId) throws Exception { + final AppCredentialsEntity credentials = appCredentialsRepository.findFirstByAppId(appId).orElse(null); + if (credentials == null) { + logger.warn("AppCredentials does not exist for app: {}", appId); + return null; + } + + return createPushClient(credentials); + } + + private AppRelatedPushClient createPushClient(final AppCredentialsEntity credentials) throws PushServerException { + logger.info("Creating APNS, FCM, and HMS clients for app: {}", credentials.getAppId()); + + final AppRelatedPushClient pushClient = new AppRelatedPushClient(); + pushClient.setAppCredentials(credentials); + + if (credentials.getIosPrivateKey() != null) { + final ApnsClient apnsClient = pushSendingWorker.prepareApnsClient(credentials); + pushClient.setApnsClient(apnsClient); + } + + if (credentials.getAndroidPrivateKey() != null) { + final FcmClient fcmClient = pushSendingWorker.prepareFcmClient(credentials.getAndroidProjectId(), credentials.getAndroidPrivateKey()); + pushClient.setFcmClient(fcmClient); + } + + if (credentials.getHmsClientId() != null) { + final HmsClient hmsClient = pushSendingWorker.prepareHmsClient(credentials); + pushClient.setHmsClient(hmsClient); + } + + return pushClient; + } +} diff --git a/powerauth-push-server/src/main/java/io/getlime/push/service/PushMessageSenderService.java b/powerauth-push-server/src/main/java/io/getlime/push/service/PushMessageSenderService.java index 77b8e21bc..7bda96d9e 100644 --- a/powerauth-push-server/src/main/java/io/getlime/push/service/PushMessageSenderService.java +++ b/powerauth-push-server/src/main/java/io/getlime/push/service/PushMessageSenderService.java @@ -16,7 +16,7 @@ package io.getlime.push.service; -import com.eatthepath.pushy.apns.ApnsClient; +import com.github.benmanes.caffeine.cache.LoadingCache; import io.getlime.push.configuration.PushServiceConfiguration; import io.getlime.push.errorhandling.exceptions.PushServerException; import io.getlime.push.model.entity.*; @@ -30,12 +30,8 @@ import io.getlime.push.repository.model.Platform; import io.getlime.push.repository.model.PushDeviceRegistrationEntity; import io.getlime.push.repository.model.PushMessageEntity; -import io.getlime.push.service.batch.storage.AppCredentialStorageMap; -import io.getlime.push.service.fcm.FcmClient; -import io.getlime.push.service.hms.HmsClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.List; @@ -46,42 +42,18 @@ * * @author Petr Dvorak, petr@wultra.com */ +@Slf4j +@AllArgsConstructor @Service public class PushMessageSenderService { - private static final Logger logger = LoggerFactory.getLogger(PushMessageSenderService.class); - private final PushSendingWorker pushSendingWorker; private final AppCredentialsRepository appCredentialsRepository; private final PushDeviceRepository pushDeviceRepository; private final PushMessageDAO pushMessageDAO; - private final AppCredentialStorageMap appRelatedPushClientMap; + private final LoadingCache appRelatedPushClientCache; private final PushServiceConfiguration configuration; - /** - * Constructor with autowired dependencies. - * @param appCredentialsRepository App credentials repository. - * @param pushDeviceRepository Push service repository. - * @param pushMessageDAO Push message DAO. - * @param pushSendingWorker Push sending worker. - * @param appRelatedPushClientMap Map with cached push clients in a map. - * @param configuration Push service configuration. - */ - @Autowired - public PushMessageSenderService(AppCredentialsRepository appCredentialsRepository, - PushDeviceRepository pushDeviceRepository, - PushMessageDAO pushMessageDAO, - PushSendingWorker pushSendingWorker, - AppCredentialStorageMap appRelatedPushClientMap, - PushServiceConfiguration configuration) { - this.appCredentialsRepository = appCredentialsRepository; - this.pushDeviceRepository = pushDeviceRepository; - this.pushMessageDAO = pushMessageDAO; - this.pushSendingWorker = pushSendingWorker; - this.appRelatedPushClientMap = appRelatedPushClientMap; - this.configuration = configuration; - } - /** * Send push notifications to given application. * @@ -92,7 +64,6 @@ public PushMessageSenderService(AppCredentialsRepository appCredentialsRepositor * @throws PushServerException In case push message sending fails. */ public BasePushMessageSendResult sendPushMessage(final String appId, final Mode mode, List pushMessageList) throws PushServerException { - // Prepare clients final AppRelatedPushClient pushClient = prepareClients(appId); // Prepare synchronization primitive for parallel push message sending @@ -225,7 +196,6 @@ public void sendCampaignMessage(String appId, Platform platform, String token, P * the error can be found in exception message. */ public void sendCampaignMessage(final String appId, Platform platform, final String token, PushMessageBody pushMessageBody, PushMessageAttributes attributes, Priority priority, String userId, Long deviceId, String activationId) throws PushServerException { - final AppRelatedPushClient pushClient = prepareClients(appId); final PushMessageEntity pushMessageObject = pushMessageDAO.storePushMessageObject(pushMessageBody, attributes, userId, activationId, deviceId); @@ -281,31 +251,12 @@ private List getPushDevices(Long appCredentialsId, return devices; } - // Prepare and cache APNS, FCM, and HMS clients for provided app private AppRelatedPushClient prepareClients(String appId) throws PushServerException { - synchronized (this) { - AppRelatedPushClient pushClient = appRelatedPushClientMap.get(appId); - if (pushClient == null) { - final AppCredentialsEntity credentials = getAppCredentials(appId); - pushClient = new AppRelatedPushClient(); - if (credentials.getIosPrivateKey() != null) { - final ApnsClient apnsClient = pushSendingWorker.prepareApnsClient(credentials); - pushClient.setApnsClient(apnsClient); - } - if (credentials.getAndroidPrivateKey() != null) { - final FcmClient fcmClient = pushSendingWorker.prepareFcmClient(credentials.getAndroidProjectId(), credentials.getAndroidPrivateKey()); - pushClient.setFcmClient(fcmClient); - } - if (credentials.getHmsClientId() != null) { - final HmsClient hmsClient = pushSendingWorker.prepareHmsClient(credentials); - pushClient.setHmsClient(hmsClient); - } - pushClient.setAppCredentials(credentials); - appRelatedPushClientMap.put(appId, pushClient); - logger.info("Creating APNS, FCM, and HMS clients for app {}", appId); - } - return pushClient; + final AppRelatedPushClient pushClient = appRelatedPushClientCache.get(appId); + if (pushClient == null) { + throw new PushServerException("AppCredentials not found: " + appId); } + return pushClient; } diff --git a/powerauth-push-server/src/main/java/io/getlime/push/service/batch/UserDeviceItemWriter.java b/powerauth-push-server/src/main/java/io/getlime/push/service/batch/UserDeviceItemWriter.java index f890cc063..b747f6f0a 100644 --- a/powerauth-push-server/src/main/java/io/getlime/push/service/batch/UserDeviceItemWriter.java +++ b/powerauth-push-server/src/main/java/io/getlime/push/service/batch/UserDeviceItemWriter.java @@ -24,13 +24,15 @@ import io.getlime.push.repository.model.aggregate.UserDevice; import io.getlime.push.repository.serialization.JsonSerialization; import io.getlime.push.service.PushMessageSenderService; -import io.getlime.push.service.batch.storage.CampaignMessageStorageMap; import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.item.Chunk; import org.springframework.batch.item.ItemWriter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + /** * Item writer that send notification to directed device and save message to database. * @@ -45,7 +47,7 @@ public class UserDeviceItemWriter implements ItemWriter { private final JsonSerialization jsonSerialization; // Non-autowired fields - private final CampaignMessageStorageMap campaignStorageMap = new CampaignMessageStorageMap(); + private final ConcurrentMap campaignMessageStorage = new ConcurrentHashMap<>(); /** * Constructor with autowired dependencies. @@ -76,13 +78,12 @@ public void write(Chunk list) throws Exception { final Long deviceId = device.getDeviceId(); final String activationId = device.getActivationId(); - // Load and cache campaign information - PushCampaignEntity campaign = campaignStorageMap.get(campaignId); + final PushCampaignEntity campaign = campaignMessageStorage.computeIfAbsent(campaignId, k -> + pushCampaignRepository.findById(k).orElse(null)); if (campaign == null) { - campaign = pushCampaignRepository.findById(campaignId).orElseThrow(() -> - new PushServerException("Campaign with entered ID does not exist")); - campaignStorageMap.put(campaignId, campaign); + throw new PushServerException("Campaign with entered ID: %s does not exist".formatted(campaign)); } + final PushMessageBody messageBody = jsonSerialization.deserializePushMessageBody(campaign.getMessage()); // Send the push message using push sender service diff --git a/powerauth-push-server/src/main/java/io/getlime/push/service/batch/storage/AppCredentialStorageMap.java b/powerauth-push-server/src/main/java/io/getlime/push/service/batch/storage/AppCredentialStorageMap.java deleted file mode 100644 index a6f3c6e2c..000000000 --- a/powerauth-push-server/src/main/java/io/getlime/push/service/batch/storage/AppCredentialStorageMap.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2016 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.push.service.batch.storage; - -import io.getlime.push.service.AppRelatedPushClient; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * Simple in-memory storage cache for app credentials and push service clients. - * Uses {@link ConcurrentHashMap} as an underlying storage. - * - * @author Petr Dvorak, petr@wultra.com - */ -public class AppCredentialStorageMap implements ItemStorageMap { - - private final ConcurrentMap map = new ConcurrentHashMap<>(); - - @Override - public AppRelatedPushClient get(String key) { - return map.get(key); - } - - @Override - public void put(String key, AppRelatedPushClient value) { - map.put(key, value); - } - - @Override - public boolean contains(String key) { - return map.containsKey(key); - } - - @Override - public void cleanAll() { - map.clear(); - } - - @Override - public void cleanByKey(String key) { - map.remove(key); - } -} diff --git a/powerauth-push-server/src/main/java/io/getlime/push/service/batch/storage/CampaignMessageStorageMap.java b/powerauth-push-server/src/main/java/io/getlime/push/service/batch/storage/CampaignMessageStorageMap.java deleted file mode 100644 index a7cadacd3..000000000 --- a/powerauth-push-server/src/main/java/io/getlime/push/service/batch/storage/CampaignMessageStorageMap.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2016 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.push.service.batch.storage; - -import io.getlime.push.repository.model.PushCampaignEntity; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - - -/** - * Simple to use class for storing campaigns. Simple in-memory storage cache for app credentials. Uses {@link ConcurrentHashMap} - * as an underlying storage. - * - * @author Petr Dvorak, petr@wultra.com - */ -public class CampaignMessageStorageMap implements ItemStorageMap { - - private final ConcurrentMap mapStorage = new ConcurrentHashMap<>(); - - @Override - public PushCampaignEntity get(Long key) { - return mapStorage.get(key); - } - - @Override - public void put(Long key, PushCampaignEntity value) { - mapStorage.put(key, value); - } - - @Override - public boolean contains(Long key) { - return mapStorage.containsKey(key); - } - - @Override - public void cleanAll() { - mapStorage.clear(); - } - - @Override - public void cleanByKey(Long key) { - mapStorage.remove(key); - } -} diff --git a/powerauth-push-server/src/main/java/io/getlime/push/service/batch/storage/ItemStorageMap.java b/powerauth-push-server/src/main/java/io/getlime/push/service/batch/storage/ItemStorageMap.java deleted file mode 100644 index da971f166..000000000 --- a/powerauth-push-server/src/main/java/io/getlime/push/service/batch/storage/ItemStorageMap.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2016 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.push.service.batch.storage; - -/** - * Interface for generic item storage - * @param Key class for item storage. - * @param Value class for item storage. - */ -public interface ItemStorageMap { - - /** - * Get the value from the map. - * @param key Key. - * @return Value from the map for corresponding key. - */ - V get(K key); - - /** - * Put a value for provided key. - * @param key Key. - * @param value Value. - */ - void put(K key, V value); - - /** - * Check if storage contains value for given key. - * @param key Key. - * @return True if there is a value for given key, false otherwise. - */ - boolean contains(K key); - - /** - * Clean all values from the storage. - */ - void cleanAll(); - - /** - * Clean value for provided key. - * @param key Key. - */ - void cleanByKey(K key); - -} diff --git a/powerauth-push-server/src/main/resources/application-dev.properties b/powerauth-push-server/src/main/resources/application-dev.properties index d627f39d5..c3be0fe1d 100644 --- a/powerauth-push-server/src/main/resources/application-dev.properties +++ b/powerauth-push-server/src/main/resources/application-dev.properties @@ -3,3 +3,8 @@ spring.liquibase.enabled=true spring.liquibase.change-log=classpath:db/changelog/db.changelog-module.xml powerauth.push.service.registration.multipleActivations.enabled=true + +powerauth.push.service.clients.cache.refreshAfterWrite=1m + +logging.level.com.wultra=DEBUG +logging.level.io.getlime=DEBUG diff --git a/powerauth-push-server/src/main/resources/application.properties b/powerauth-push-server/src/main/resources/application.properties index c18cd6176..e2ddb54a4 100644 --- a/powerauth-push-server/src/main/resources/application.properties +++ b/powerauth-push-server/src/main/resources/application.properties @@ -53,6 +53,8 @@ powerauth.push.service.registration.multipleActivations.enabled=false powerauth.push.service.registration.retry.backoff=100 powerauth.push.service.registration.retry.maxAttempts=2 +powerauth.push.service.clients.cache.refreshAfterWrite=5m + # APNs Configuration powerauth.push.service.apns.useDevelopment=true powerauth.push.service.apns.proxy.enabled=false From 06690555e7dba8d727a342919a1a7690b5161d0c Mon Sep 17 00:00:00 2001 From: Lubos Racansky Date: Tue, 15 Oct 2024 12:32:19 +0200 Subject: [PATCH 19/21] Fix #899: Remove SQL schema creation from markdown --- docs/Push-Server-Database.md | 145 +---------------------------------- 1 file changed, 1 insertion(+), 144 deletions(-) diff --git a/docs/Push-Server-Database.md b/docs/Push-Server-Database.md index 5d7e14941..7a3b8d828 100644 --- a/docs/Push-Server-Database.md +++ b/docs/Push-Server-Database.md @@ -8,6 +8,7 @@ You can download DDL scripts for supported databases: - [PostgreSQL](./sql/postgresql/create_push_server_schema.sql) - [Oracle](./sql/oracle/create_push_server_schema.sql) +- [MS SQL](./sql/mssql/create_push_server_schema.sql) ## Tables @@ -26,26 +27,6 @@ You can download DDL scripts for supported databases: Stores push tokens specific for a given device. -#### Schema - -```sql -CREATE TABLE push_device_registration ( - id INTEGER NOT NULL CONSTRAINT push_device_registration_pkey PRIMARY KEY, - activation_id VARCHAR(37), - user_id VARCHAR(255), - app_id INTEGER NOT NULL, - platform VARCHAR(255) NOT NULL, - push_token VARCHAR(255) NOT NULL, - timestamp_last_registered TIMESTAMP(6) NOT NULL, - is_active BOOLEAN -); - -CREATE INDEX push_device_app_token ON push_device_registration (app_id, push_token); -CREATE INDEX push_device_user_app ON push_device_registration (user_id, app_id); -CREATE UNIQUE INDEX push_device_activation ON push_device_registration (activation_id); -CREATE UNIQUE INDEX push_device_activation_token ON push_device_registration (activation_id, push_token); -``` - #### Columns | Name | Type | Info | Note | @@ -80,26 +61,6 @@ CREATE UNIQUE INDEX push_device_activation_token ON push_device_registration (ac Stores per-app credentials used for communication with APNs / FCM. -#### Schema - -```sql -CREATE TABLE push_app_credentials ( - id INTEGER NOT NULL CONSTRAINT push_app_credentials_pkey PRIMARY KEY, - app_id VARCHAR(255) NOT NULL, - ios_key_id VARCHAR(255), - ios_private_key BYTEA, - ios_team_id VARCHAR(255), - ios_bundle VARCHAR(255), - ios_environment VARCHAR(32), - android_private_key BYTEA, - android_project_id VARCHAR(255), - timestamp_created TIMESTAMP(6) DEFAULT NOW() NOT NULL, - timestamp_last_updated TIMESTAMP(6) -); - -CREATE UNIQUE INDEX push_app_cred_app ON push_app_credentials (app_id); -``` - #### Columns | Name | Type | Info | Note | @@ -134,24 +95,6 @@ CREATE UNIQUE INDEX push_app_cred_app ON push_app_credentials (app_id); Stores individual messages that were sent by the push server and their sent status. -#### Schema - -```sql -CREATE TABLE push_message ( - id INTEGER NOT NULL CONSTRAINT push_message_pkey PRIMARY KEY, - device_registration_id INTEGER NOT NULL, - user_id VARCHAR(255) NOT NULL, - activation_id VARCHAR(37), - is_silent BOOLEAN DEFAULT false NOT NULL, - is_personal BOOLEAN DEFAULT false NOT NULL, - message_body TEXT NOT NULL, - timestamp_created TIMESTAMP(6) NOT NULL, - status INTEGER NOT NULL -); - -CREATE INDEX push_message_status ON push_message (status); -``` - #### Columns | Name | Type | Info | Note | @@ -184,22 +127,6 @@ CREATE INDEX push_message_status ON push_message (status); Stores particular campaigns together with notification messages. -#### Schema - -```sql -CREATE TABLE push_campaign ( - id INTEGER NOT NULL CONSTRAINT push_campaign_pkey PRIMARY KEY, - app_id INTEGER NOT NULL, - message TEXT NOT NULL, - is_sent BOOLEAN DEFAULT false NOT NULL, - timestamp_created TIMESTAMP(6) NOT NULL, - timestamp_sent TIMESTAMP(6), - timestamp_completed TIMESTAMP(6) -); - -CREATE INDEX push_campaign_sent ON push_campaign (is_sent); -``` - #### Columns | Name | Type | Info | Note | @@ -230,20 +157,6 @@ CREATE INDEX push_campaign_sent ON push_campaign (is_sent); Stores users who are going to get notification from specific campaign. -#### Schema - -```sql -CREATE TABLE push_campaign_user ( - id INTEGER NOT NULL CONSTRAINT push_campaign_user_pkey PRIMARY KEY, - campaign_id INTEGER NOT NULL, - user_id INTEGER NOT NULL, - timestamp_created TIMESTAMP(6) NOT NULL -); - -CREATE INDEX push_campaign_user_campaign ON push_campaign_user (campaign_id, user_id); -CREATE INDEX push_campaign_user_detail ON push_campaign_user (user_id); -``` - #### Columns | Name | Type | Info | Note | @@ -272,27 +185,6 @@ CREATE INDEX push_campaign_user_detail ON push_campaign_user (user_id); Stores the messages to be delivered to particular users. -#### Schema - -```sql -CREATE TABLE push_inbox ( - id INTEGER NOT NULL CONSTRAINT push_inbox_pk PRIMARY KEY, - inbox_id VARCHAR(37) NOT NULL, - user_id VARCHAR(255) NOT NULL, - type VARCHAR(32) NOT NULL; - subject TEXT NOT NULL, - summary TEXT NOT NULL; - body TEXT NOT NULL, - read BOOLEAN DEFAULT false NOT NULL, - timestamp_created TIMESTAMP NOT NULL, - timestamp_read TIMESTAMP -); - -CREATE INDEX push_inbox_id ON push_inbox (inbox_id); -CREATE INDEX push_inbox_user ON push_inbox (user_id); -CREATE INDEX push_inbox_user_read ON push_inbox (user_id, read); -``` - #### Columns | Name | Type | Info | Note | @@ -328,16 +220,6 @@ CREATE INDEX push_inbox_user_read ON push_inbox (user_id, read); Stores the messages to application mapping. -#### Schema - -```sql -CREATE TABLE push_inbox_app ( - app_credentials_id INTEGER NOT NULL, - inbox_id INTEGER NOT NULL, - CONSTRAINT push_inbox_app_pk PRIMARY KEY (inbox_id, app_credentials_id) -); -``` - #### Columns | Name | Type | Info | Note | @@ -367,11 +249,6 @@ CREATE TABLE push_inbox_app ( Sequence for application credentials registered in the system. -#### Schema - -```sql -CREATE SEQUENCE push_credentials_seq; -``` @@ -379,11 +256,6 @@ CREATE SEQUENCE push_credentials_seq; Sequence for device registrations in the system. -#### Schema - -```sql -CREATE SEQUENCE push_device_registration_seq; -``` @@ -391,11 +263,6 @@ CREATE SEQUENCE push_device_registration_seq; Sequence for push messages sent by the system. -#### Schema - -```sql -CREATE SEQUENCE push_message_seq; -``` @@ -403,11 +270,6 @@ CREATE SEQUENCE push_message_seq; Sequence for push campaigns that are created in the system. -#### Schema - -```sql -CREATE SEQUENCE push_campaign_seq; -``` @@ -415,9 +277,4 @@ CREATE SEQUENCE push_campaign_seq; Sequence for user assignments to campaigns. -#### Schema - -```sql -CREATE SEQUENCE push_campaign_user_seq; -``` From 06987f5b9dff82f07f5812e81f94a5ad5ea1ab44 Mon Sep 17 00:00:00 2001 From: Lubos Racansky Date: Fri, 11 Oct 2024 09:55:06 +0200 Subject: [PATCH 20/21] Fix #889: Update Wultra dependencies --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e39aee0bb..a349f2c33 100644 --- a/pom.xml +++ b/pom.xml @@ -70,9 +70,9 @@ 1.11.0 - 1.9.0-SNAPSHOT + 1.9.0 1.9.0 - 1.9.0-SNAPSHOT + 1.9.0 0.15.4 From c4d953e233b04596ca38813ee59477de7eb353e7 Mon Sep 17 00:00:00 2001 From: Lubos Racansky Date: Fri, 11 Oct 2024 10:12:53 +0200 Subject: [PATCH 21/21] Fix #892: Add liquibase tag 1.9.0 --- .../1.9.x/20241011-add-tag-1.9.0.xml | 10 ++++++++++ .../1.9.x/db.changelog-version.xml | 1 + 2 files changed, 11 insertions(+) create mode 100644 docs/db/changelog/changesets/powerauth-push-server/1.9.x/20241011-add-tag-1.9.0.xml diff --git a/docs/db/changelog/changesets/powerauth-push-server/1.9.x/20241011-add-tag-1.9.0.xml b/docs/db/changelog/changesets/powerauth-push-server/1.9.x/20241011-add-tag-1.9.0.xml new file mode 100644 index 000000000..86b1d0625 --- /dev/null +++ b/docs/db/changelog/changesets/powerauth-push-server/1.9.x/20241011-add-tag-1.9.0.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/docs/db/changelog/changesets/powerauth-push-server/1.9.x/db.changelog-version.xml b/docs/db/changelog/changesets/powerauth-push-server/1.9.x/db.changelog-version.xml index 9d39dcb65..45aede793 100644 --- a/docs/db/changelog/changesets/powerauth-push-server/1.9.x/db.changelog-version.xml +++ b/docs/db/changelog/changesets/powerauth-push-server/1.9.x/db.changelog-version.xml @@ -4,5 +4,6 @@ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.9.xsd"> + \ No newline at end of file