diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 36ca9c565f6..03729d0555f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -48,7 +48,7 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: @@ -77,7 +77,7 @@ jobs: # uses a compiled language - run: | - mvn -U -T 1C -PskipTests,all + mvn -U -T 1C -Dapache.snapshots=true -PskipTests,all - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 diff --git a/.github/workflows/crosschecks.yml b/.github/workflows/crosschecks.yml index 2dff009b19c..7ba6a2f4432 100644 --- a/.github/workflows/crosschecks.yml +++ b/.github/workflows/crosschecks.yml @@ -31,7 +31,7 @@ jobs: fail-fast: false matrix: language: ['java'] - java: [ '21', '26-ea' ] + java: [ '25', '26-ea' ] os: [ubuntu-latest, windows-latest, macos-latest] steps: @@ -47,7 +47,7 @@ jobs: with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -D'apache.snapshots=true' -P 'skipTests,all' - name: Validate if: runner.os == 'Linux' run: mvn -T 1C checkstyle:check tools:verify-legal-files modernizer:modernizer apache-rat:check diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml index a32657a194f..3cc7999b1a8 100644 --- a/.github/workflows/dockerhub.yml +++ b/.github/workflows/dockerhub.yml @@ -58,12 +58,12 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all,docker' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all,docker' - name: 'Push to DockerHub' run: mvn -f docker/pom.xml docker:push -Ddocker.username=${{secrets.DOCKERHUB_USER}} -Ddocker.password=${{secrets.DOCKERHUB_TOKEN}} diff --git a/.github/workflows/fit_Elasticsearch.yml b/.github/workflows/fit_Elasticsearch.yml index 5c555b79141..601c7def800 100644 --- a/.github/workflows/fit_Elasticsearch.yml +++ b/.github/workflows/fit_Elasticsearch.yml @@ -34,12 +34,12 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'Elasticsearch / PostgreSQL' run: mvn -f fit/core-reference/pom.xml -P elasticsearch-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true diff --git a/.github/workflows/fit_OpenSearch.yml b/.github/workflows/fit_OpenSearch.yml index 7d69bc388d2..dd2f6dd8db0 100644 --- a/.github/workflows/fit_OpenSearch.yml +++ b/.github/workflows/fit_OpenSearch.yml @@ -34,12 +34,12 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'zulu' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'OpenSearch / PostgreSQL' run: mvn -f fit/core-reference/pom.xml -P opensearch-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true diff --git a/.github/workflows/fit_Payara.yml b/.github/workflows/fit_Payara.yml index ae6de3868d4..8e3c150e1f3 100644 --- a/.github/workflows/fit_Payara.yml +++ b/.github/workflows/fit_Payara.yml @@ -34,12 +34,12 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'Payara / PostgreSQL' run: mvn -f fit/core-reference/pom.xml -P payara-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true diff --git a/.github/workflows/fit_Tomcat_LiveSync.yml b/.github/workflows/fit_Tomcat_LiveSync.yml index 443292972bf..39e5837d663 100644 --- a/.github/workflows/fit_Tomcat_LiveSync.yml +++ b/.github/workflows/fit_Tomcat_LiveSync.yml @@ -34,13 +34,13 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'Tomcat / PostgreSQL / Debezium' run: mvn -f fit/core-reference/pom.xml -P debezium-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true - name: 'Tomcat / PostgreSQL / SyncRepl' diff --git a/.github/workflows/fit_Tomcat_PostgreSQL.yml b/.github/workflows/fit_Tomcat_PostgreSQL.yml index 1058101e800..d0ee4ff9ddc 100644 --- a/.github/workflows/fit_Tomcat_PostgreSQL.yml +++ b/.github/workflows/fit_Tomcat_PostgreSQL.yml @@ -34,12 +34,12 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'Tomcat / PostgreSQL' run: mvn -f fit/core-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true diff --git a/.github/workflows/fit_WA_OIDCC4UI.yml b/.github/workflows/fit_WA_OIDCC4UI.yml index f77de653d52..f0b8529bfef 100644 --- a/.github/workflows/fit_WA_OIDCC4UI.yml +++ b/.github/workflows/fit_WA_OIDCC4UI.yml @@ -34,12 +34,12 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'WA / OIDCC4UI' run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=OIDCC4UIITCase diff --git a/.github/workflows/fit_WA_OpenFGA.yml b/.github/workflows/fit_WA_OpenFGA.yml index f578f41a64d..b3f79a0b6dc 100644 --- a/.github/workflows/fit_WA_OpenFGA.yml +++ b/.github/workflows/fit_WA_OpenFGA.yml @@ -34,13 +34,13 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'Unit Tests: OpenFGA' run: mvn -f ext/openfga/client-openfga/pom.xml -P openfga -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true - name: 'WA / OpenFGA' diff --git a/.github/workflows/fit_WA_SAML2SP4UI.yml b/.github/workflows/fit_WA_SAML2SP4UI.yml index 9f7dfda7780..ee1573e4198 100644 --- a/.github/workflows/fit_WA_SAML2SP4UI.yml +++ b/.github/workflows/fit_WA_SAML2SP4UI.yml @@ -34,12 +34,12 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'WA / SAML2SP4UI' run: mkdir -p fit/core-reference/target/test-classes && cp fit/core-reference/src/test/resources/saml.keystore.jks fit/core-reference/target/test-classes && mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=SAML2SP4UIITCase diff --git a/.github/workflows/fit_WA_SRA_CASClient.yml b/.github/workflows/fit_WA_SRA_CASClient.yml index 783a4441b19..f6f1e96a65d 100644 --- a/.github/workflows/fit_WA_SRA_CASClient.yml +++ b/.github/workflows/fit_WA_SRA_CASClient.yml @@ -34,12 +34,12 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'WA / SRA / CAS Client' run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=org.apache.syncope.fit.sra.CASSRAITCase diff --git a/.github/workflows/fit_WA_SRA_OAuth2.yml b/.github/workflows/fit_WA_SRA_OAuth2.yml index a9d9463b0c5..9a1465cf6bf 100644 --- a/.github/workflows/fit_WA_SRA_OAuth2.yml +++ b/.github/workflows/fit_WA_SRA_OAuth2.yml @@ -34,12 +34,12 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'WA / SRA / OAuth 2.0' run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=org.apache.syncope.fit.sra.OAUTH2SRAITCase diff --git a/.github/workflows/fit_WA_SRA_OIDC.yml b/.github/workflows/fit_WA_SRA_OIDC.yml index 047c981b988..df6bbfc5d0d 100644 --- a/.github/workflows/fit_WA_SRA_OIDC.yml +++ b/.github/workflows/fit_WA_SRA_OIDC.yml @@ -34,12 +34,12 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'WA / SRA / OpenID Connect 1.0' run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=org.apache.syncope.fit.sra.OIDCSRAITCase diff --git a/.github/workflows/fit_WA_SRA_SAML2.yml b/.github/workflows/fit_WA_SRA_SAML2.yml index 1bf02f5f248..cf67afdaac3 100644 --- a/.github/workflows/fit_WA_SRA_SAML2.yml +++ b/.github/workflows/fit_WA_SRA_SAML2.yml @@ -34,12 +34,12 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'WA / SRA / SAML 2.0' run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=org.apache.syncope.fit.sra.SAML2SRAITCase diff --git a/.github/workflows/fit_Wildfly.yml b/.github/workflows/fit_Wildfly.yml index 2936d90a82a..77409f1c396 100644 --- a/.github/workflows/fit_Wildfly.yml +++ b/.github/workflows/fit_Wildfly.yml @@ -34,12 +34,12 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'Wildfly / PostgreSQL' run: mvn -f fit/core-reference/pom.xml -P wildfly-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true diff --git a/.github/workflows/fit_Zookeeper.yml b/.github/workflows/fit_Zookeeper.yml index 9d733a59155..23c82d2857d 100644 --- a/.github/workflows/fit_Zookeeper.yml +++ b/.github/workflows/fit_Zookeeper.yml @@ -34,12 +34,12 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'Zookeeper / PostgreSQL' run: mvn -f fit/core-reference/pom.xml -P zookeeper-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true diff --git a/.github/workflows/mariadb.yml b/.github/workflows/mariadb.yml index 3dd0fb94d65..862f256eb6f 100644 --- a/.github/workflows/mariadb.yml +++ b/.github/workflows/mariadb.yml @@ -34,13 +34,13 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'Unit Tests: MariaDB' run: mvn -f core/persistence-jpa/pom.xml -P mariadb -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true - name: 'Integration Tests: MariaDB' diff --git a/.github/workflows/mysql.yml b/.github/workflows/mysql.yml index 71c38d9cf77..4a3420a21fa 100644 --- a/.github/workflows/mysql.yml +++ b/.github/workflows/mysql.yml @@ -34,13 +34,13 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'Unit Tests: MySQL' run: mvn -f core/persistence-jpa/pom.xml -P mysql -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true - name: 'Integration Tests: MySQL' diff --git a/.github/workflows/neo4j.yml b/.github/workflows/neo4j.yml index d9f05139d42..1208def78d8 100644 --- a/.github/workflows/neo4j.yml +++ b/.github/workflows/neo4j.yml @@ -34,13 +34,13 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'Unit Tests: Neo4j' run: mvn -f core/persistence-neo4j/pom.xml -P neo4j -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true - name: 'Integration Tests: Neo4j' diff --git a/.github/workflows/oracle.yml b/.github/workflows/oracle.yml index 0ae40924314..0560d9a34a7 100644 --- a/.github/workflows/oracle.yml +++ b/.github/workflows/oracle.yml @@ -36,13 +36,13 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Setup Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - name: Build - run: mvn -U -T 1C -P 'skipTests,all' + run: mvn -U -T 1C -Dapache.snapshots=true -P 'skipTests,all' - name: 'Unit Tests: Oracle' run: mvn -f core/persistence-jpa/pom.xml -P oracle -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true - name: 'Integration Tests: Oracle' diff --git a/archetype/pom.xml b/archetype/pom.xml index a0dc755602a..40a21253bd7 100644 --- a/archetype/pom.xml +++ b/archetype/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Archetype diff --git a/archetype/src/main/resources/archetype-resources/fit/pom.xml b/archetype/src/main/resources/archetype-resources/fit/pom.xml index 0031a1951eb..bc258c66517 100644 --- a/archetype/src/main/resources/archetype-resources/fit/pom.xml +++ b/archetype/src/main/resources/archetype-resources/fit/pom.xml @@ -448,7 +448,6 @@ under the License. syncope 20 5 - sjvm http://syncope:8080/syncope/rest/ http://syncope:8080/syncope/rest/keymaster ${anonymousUser} diff --git a/client/am/console/pom.xml b/client/am/console/pom.xml index 6f14d2a79d1..af4609cad89 100644 --- a/client/am/console/pom.xml +++ b/client/am/console/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.client syncope-client-am - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Client AM Console diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java index f6a1003ccdf..ac5618431b3 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.client.console.pages; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; import de.agilecoders.wicket.core.markup.html.bootstrap.tabs.AjaxBootstrapTabbedPanel; import jakarta.ws.rs.core.MediaType; @@ -56,6 +54,7 @@ import org.apache.syncope.client.ui.commons.annotations.AMPage; import org.apache.syncope.common.keymaster.client.api.ServiceOps; import org.apache.syncope.common.keymaster.client.api.model.NetworkService; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.types.AMEntitlement; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxLink; @@ -67,13 +66,15 @@ import org.apache.wicket.model.ResourceModel; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.spring.injection.annot.SpringBean; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; @AMPage(label = "WA", icon = "fas fa-id-card", listEntitlement = "", priority = 200) public class WA extends BasePage { private static final long serialVersionUID = 9200112197134882164L; - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); @SpringBean protected WAConfigRestClient waConfigRestClient; @@ -155,7 +156,7 @@ public void onClick(final AjaxRequestTarget target) { if (properties.has("cas.server.prefix")) { JsonNode prefix = properties.get("cas.server.prefix"); if (prefix.has("value")) { - waPrefix = Strings.CS.removeEnd(prefix.get("value").asText(), "/"); + waPrefix = Strings.CS.removeEnd(prefix.get("value").asString(), "/"); } } } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/OIDC.java b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/OIDC.java index b71c26bf7a5..5e0bbb6a72c 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/OIDC.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/OIDC.java @@ -18,9 +18,7 @@ */ package org.apache.syncope.client.console.panels; -import com.fasterxml.jackson.databind.json.JsonMapper; import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; -import java.io.IOException; import java.util.Optional; import org.apache.commons.lang3.mutable.Mutable; import org.apache.syncope.client.console.SyncopeConsoleSession; @@ -44,6 +42,8 @@ import org.apache.wicket.spring.injection.annot.SpringBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.json.JsonMapper; public class OIDC extends Panel { @@ -101,7 +101,7 @@ public void onClick(final AjaxRequestTarget target) { try { pretty = MAPPER.writerWithDefaultPrettyPrinter(). writeValueAsString(MAPPER.readTree(oidcjwksto.get().getJson())); - } catch (IOException e) { + } catch (JacksonException e) { LOG.error("Could not pretty-print", e); pretty = Optional.ofNullable(oidcjwksto.get()).map(OIDCJWKSTO::getJson).orElse(null); } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AMSessionDeserializer.java b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AMSessionDeserializer.java index 7444b2d82e4..59bba0a39f6 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AMSessionDeserializer.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AMSessionDeserializer.java @@ -18,26 +18,28 @@ */ package org.apache.syncope.client.console.rest; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import java.io.IOException; import java.io.StringWriter; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import org.apache.syncope.common.lib.AMSession; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.core.exc.JacksonIOException; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.deser.std.StdDeserializer; +import tools.jackson.databind.json.JsonMapper; public class AMSessionDeserializer extends StdDeserializer { - private static final long serialVersionUID = 24527200564172L; + protected static final Logger LOG = LoggerFactory.getLogger(AMSessionDeserializer.class); - private static final Logger LOG = LoggerFactory.getLogger(AMSessionDeserializer.class); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); public AMSessionDeserializer() { this(null); @@ -48,15 +50,13 @@ public AMSessionDeserializer(final Class vc) { } @Override - public AMSession deserialize(final JsonParser jp, final DeserializationContext ctxt) - throws IOException { - - JsonNode node = jp.getCodec().readTree(jp); + public AMSession deserialize(final JsonParser jp, final DeserializationContext ctxt) throws JacksonException { + JsonNode node = jp.readValueAsTree(); AMSession waSession = new AMSession(); if (node.has("authentication_date_formatted")) { - String authenticationDate = node.get("authentication_date_formatted").textValue(); + String authenticationDate = node.get("authentication_date_formatted").stringValue(); try { waSession.setAuthenticationDate( OffsetDateTime.parse(authenticationDate, DateTimeFormatter.ISO_OFFSET_DATE_TIME)); @@ -66,18 +66,19 @@ public AMSession deserialize(final JsonParser jp, final DeserializationContext c } if (node.has("authenticated_principal")) { - waSession.setPrincipal(node.get("authenticated_principal").textValue()); + waSession.setPrincipal(node.get("authenticated_principal").stringValue()); } if (node.has("ticket_granting_ticket")) { - waSession.setKey(node.get("ticket_granting_ticket").textValue()); + waSession.setKey(node.get("ticket_granting_ticket").stringValue()); } - StringWriter writer = new StringWriter(); - JsonGenerator jgen = jp.getCodec().getFactory().createGenerator(writer); - jgen.setPrettyPrinter(new DefaultPrettyPrinter()); - jp.getCodec().writeTree(jgen, node); - waSession.setJson(writer.toString()); + try (StringWriter writer = new StringWriter()) { + MAPPER.writerWithDefaultPrettyPrinter().writeValue(writer, node); + waSession.setJson(writer.toString()); + } catch (IOException e) { + throw JacksonIOException.construct(e); + } return waSession; } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AMSessionRestClient.java b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AMSessionRestClient.java index 47e66bd29fe..e4051ac9496 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AMSessionRestClient.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AMSessionRestClient.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.client.console.rest; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.util.List; @@ -29,9 +27,11 @@ import org.apache.syncope.common.keymaster.client.api.model.NetworkService; import org.apache.syncope.common.lib.AMSession; import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.types.ClientExceptionType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.jakarta.rs.json.JacksonJsonProvider; public abstract class AMSessionRestClient implements RestClient { @@ -39,8 +39,7 @@ public abstract class AMSessionRestClient implements RestClient { protected static final Logger LOG = LoggerFactory.getLogger(AMSessionRestClient.class); - protected static final List JAX_RS_PROVIDERS = - List.of(new JacksonJsonProvider(JsonMapper.builder().findAndAddModules().build())); + protected static final List JAX_RS_PROVIDERS = List.of(new JacksonJsonProvider(new SyncopeJsonMapper())); protected final List instances; diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/SRAStatisticsRestClient.java b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/SRAStatisticsRestClient.java index 6bf529856ba..829def79f0d 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/SRAStatisticsRestClient.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/SRAStatisticsRestClient.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.client.console.rest; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.util.List; @@ -29,6 +27,8 @@ import org.apache.syncope.common.keymaster.client.api.model.NetworkService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.jakarta.rs.json.JacksonJsonProvider; public final class SRAStatisticsRestClient { diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WASessionRestClient.java b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WASessionRestClient.java index 782c729168b..6fe51783628 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WASessionRestClient.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WASessionRestClient.java @@ -18,10 +18,6 @@ */ package org.apache.syncope.client.console.rest; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.io.InputStream; @@ -30,13 +26,20 @@ import org.apache.syncope.client.console.SyncopeWebApplication; import org.apache.syncope.common.keymaster.client.api.model.NetworkService; import org.apache.syncope.common.lib.AMSession; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; public final class WASessionRestClient extends AMSessionRestClient { private static final long serialVersionUID = 22118820292494L; protected static final JsonMapper MAPPER = JsonMapper.builder(). - addModule(new SimpleModule().addDeserializer(AMSession.class, new AMSessionDeserializer())).build(); + addModule(new SimpleModule().addDeserializer(AMSession.class, new AMSessionDeserializer())). + enable(MapperFeature.USE_GETTERS_AS_SETTERS). + build(); public WASessionRestClient(final List instances) { super(instances); diff --git a/client/am/enduser/pom.xml b/client/am/enduser/pom.xml index 3d9efa6b042..3628b92cec5 100644 --- a/client/am/enduser/pom.xml +++ b/client/am/enduser/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.client syncope-client-am - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Client AM Enduser diff --git a/client/am/lib/pom.xml b/client/am/lib/pom.xml index 4b71908028d..242d08c2716 100644 --- a/client/am/lib/pom.xml +++ b/client/am/lib/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.client syncope-client-am - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Client AM Lib diff --git a/client/am/pom.xml b/client/am/pom.xml index b7386df99fc..930d4e082d4 100644 --- a/client/am/pom.xml +++ b/client/am/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-client - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Client AM diff --git a/client/idm/console/pom.xml b/client/idm/console/pom.xml index 7ef9fbc2224..add44d1de73 100644 --- a/client/idm/console/pom.xml +++ b/client/idm/console/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.client syncope-client-idm - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Client IdM Console diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.java index 9b4915eebea..0e609842bda 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.client.console.policies; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; @@ -42,6 +41,7 @@ import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel; import org.apache.syncope.client.ui.commons.pages.BaseWebPage; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.policy.AbstractCorrelationRuleConf; import org.apache.syncope.common.lib.policy.DefaultInboundCorrelationRuleConf; import org.apache.syncope.common.lib.policy.DefaultPushCorrelationRuleConf; @@ -64,12 +64,13 @@ import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.spring.injection.annot.SpringBean; +import tools.jackson.databind.json.JsonMapper; public class ProvisioningPolicyModalPanel extends AbstractModalPanel { private static final long serialVersionUID = 2988891313881271124L; - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); @SpringBean protected ImplementationRestClient implementationRestClient; diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java index 1a775d6994d..5802d0fd3cf 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.client.console.topology; -import com.fasterxml.jackson.databind.json.JsonMapper; import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; import java.io.Serializable; import java.text.MessageFormat; @@ -44,6 +43,7 @@ import org.apache.syncope.client.ui.commons.pages.BaseWebPage; import org.apache.syncope.client.ui.commons.wizards.AjaxWizard; import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.to.ConnInstanceTO; import org.apache.syncope.common.lib.to.ResourceTO; import org.apache.syncope.common.lib.types.IdMEntitlement; @@ -63,12 +63,13 @@ import org.apache.wicket.model.Model; import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.spring.injection.annot.SpringBean; +import tools.jackson.databind.json.JsonMapper; public class TopologyTogglePanel extends TogglePanel { private static final long serialVersionUID = -2025535531121434056L; - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); @SpringBean protected ConnectorRestClient connectorRestClient; diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyWebSocketBehavior.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyWebSocketBehavior.java index 1c81d023b6d..09d346bcdc9 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyWebSocketBehavior.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyWebSocketBehavior.java @@ -18,9 +18,6 @@ */ package org.apache.syncope.client.console.topology; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; -import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -37,6 +34,7 @@ import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.common.keymaster.client.api.ServiceOps; import org.apache.syncope.common.keymaster.client.api.model.NetworkService; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.wicket.protocol.ws.api.WebSocketBehavior; import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler; import org.apache.wicket.protocol.ws.api.message.TextMessage; @@ -44,6 +42,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.task.SimpleAsyncTaskExecutor; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; public class TopologyWebSocketBehavior extends WebSocketBehavior { @@ -51,7 +52,7 @@ public class TopologyWebSocketBehavior extends WebSocketBehavior { protected static final Logger LOG = LoggerFactory.getLogger(TopologyWebSocketBehavior.class); - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); protected static final String CONNECTOR_TEST_TIMEOUT_PARAMETER = "connector.test.timeout"; @@ -140,9 +141,9 @@ protected void timeoutHandlingConnectionChecker( protected void onMessage(final WebSocketRequestHandler handler, final TextMessage message) { try { JsonNode obj = MAPPER.readTree(message.getText()); - switch (Topology.SupportedOperation.valueOf(obj.get("kind").asText())) { + switch (Topology.SupportedOperation.valueOf(obj.get("kind").asString())) { case CHECK_CONNECTOR: - String ckey = obj.get("target").asText(); + String ckey = obj.get("target").asString(); if (connectors.containsKey(ckey)) { handler.push(connectors.get(ckey)); @@ -166,7 +167,7 @@ protected void onMessage(final WebSocketRequestHandler handler, final TextMessag break; case CHECK_RESOURCE: - String rkey = obj.get("target").asText(); + String rkey = obj.get("target").asString(); if (resources.containsKey(rkey)) { handler.push(resources.get(rkey)); @@ -191,14 +192,14 @@ protected void onMessage(final WebSocketRequestHandler handler, final TextMessag case ADD_ENDPOINT: handler.appendJavaScript(String.format("addEndpoint('%s', '%s', '%s');", - obj.get("source").asText(), - obj.get("target").asText(), - obj.get("scope").asText())); + obj.get("source").asString(), + obj.get("target").asString(), + obj.get("scope").asString())); break; default: } - } catch (IOException e) { + } catch (JacksonException e) { LOG.error("Error managing websocket message", e); } } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsWizardBuilder.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsWizardBuilder.java index 453fde65d61..abf300d3da5 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsWizardBuilder.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsWizardBuilder.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.client.console.wizards.any; -import com.fasterxml.jackson.databind.json.JsonMapper; import jakarta.ws.rs.HttpMethod; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; @@ -36,6 +35,7 @@ import org.apache.syncope.client.lib.batch.BatchRequest; import org.apache.syncope.client.ui.commons.Constants; import org.apache.syncope.client.ui.commons.wizards.AjaxWizard; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.request.LinkedAccountUR; import org.apache.syncope.common.lib.request.UserUR; import org.apache.syncope.common.lib.to.LinkedAccountTO; @@ -50,12 +50,13 @@ import org.apache.wicket.event.IEventSink; import org.apache.wicket.extensions.wizard.WizardModel; import org.apache.wicket.model.IModel; +import tools.jackson.databind.json.JsonMapper; public class MergeLinkedAccountsWizardBuilder extends BaseAjaxWizardBuilder implements IEventSink { private static final long serialVersionUID = -9142332740863374891L; - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); protected final UserDirectoryPanel parentPanel; diff --git a/client/idm/lib/pom.xml b/client/idm/lib/pom.xml index 40584b153cf..e188de05003 100644 --- a/client/idm/lib/pom.xml +++ b/client/idm/lib/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.client syncope-client-idm - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Client IdM Lib diff --git a/client/idm/pom.xml b/client/idm/pom.xml index ad9b78f41b5..82ce90f8d07 100644 --- a/client/idm/pom.xml +++ b/client/idm/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-client - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Client IdM diff --git a/client/idrepo/common-ui/pom.xml b/client/idrepo/common-ui/pom.xml index feeb5b21bbc..854864cb4bd 100644 --- a/client/idrepo/common-ui/pom.xml +++ b/client/idrepo/common-ui/pom.xml @@ -22,7 +22,7 @@ under the License. org.apache.syncope.client syncope-client-idrepo - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Client IdRepo Common UI @@ -38,7 +38,7 @@ under the License. org.springframework.boot - spring-boot-actuator + spring-boot-health org.springframework.boot diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/MIMETypesLoader.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/MIMETypesLoader.java index a41082b611a..ee34c0de2af 100644 --- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/MIMETypesLoader.java +++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/MIMETypesLoader.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.client.ui.commons; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; @@ -29,6 +27,8 @@ import org.apache.wicket.util.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; public class MIMETypesLoader implements Serializable { @@ -50,7 +50,7 @@ public void load() { JsonNode type = node.path("name"); JsonNode ext = node.path("extension"); if (!type.isMissingNode()) { - mimeTypesMap.put(type.asText(), ext.isMissingNode() ? "" : ext.asText()); + mimeTypesMap.put(type.asString(), ext.isMissingNode() ? "" : ext.asString()); } } diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/actuate/SyncopeCoreHealthIndicator.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/actuate/SyncopeCoreHealthIndicator.java index b340bcb4201..7624d612bfa 100644 --- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/actuate/SyncopeCoreHealthIndicator.java +++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/actuate/SyncopeCoreHealthIndicator.java @@ -25,9 +25,9 @@ import org.apache.syncope.common.rest.api.service.UserSelfService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.actuate.health.Health; -import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.boot.actuate.health.Status; +import org.springframework.boot.health.contributor.Health; +import org.springframework.boot.health.contributor.HealthIndicator; +import org.springframework.boot.health.contributor.Status; public class SyncopeCoreHealthIndicator implements HealthIndicator { diff --git a/client/idrepo/console/pom.xml b/client/idrepo/console/pom.xml index 380927752f2..d800c9117ef 100644 --- a/client/idrepo/console/pom.xml +++ b/client/idrepo/console/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.client syncope-client-idrepo - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Client IdRepo Console @@ -48,10 +48,9 @@ under the License. com.giffing.wicket.spring.boot.starter wicket-spring-boot-starter - - org.springframework.boot - spring-boot-starter-actuator + org.apache.wicket + wicket-auth-roles @@ -70,12 +69,16 @@ under the License. org.springframework.boot - spring-boot-starter-log4j2 + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-health - org.apache.wicket - wicket-auth-roles + org.springframework.boot + spring-boot-starter-log4j2 @@ -143,6 +146,10 @@ under the License. com.lmax disruptor + + org.slf4j + jcl-over-slf4j + org.apache.wicket diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/PreferenceManager.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/PreferenceManager.java index 3624b6e34e5..683c1914c20 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/PreferenceManager.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/PreferenceManager.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.client.console; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.io.IOException; import java.io.Serializable; import java.io.StringWriter; @@ -30,10 +28,13 @@ import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.wicket.util.cookies.CookieDefaults; import org.apache.wicket.util.cookies.CookieUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.json.JsonMapper; public final class PreferenceManager implements Serializable { @@ -45,7 +46,7 @@ public final class PreferenceManager implements Serializable { private static final int ONE_YEAR_TIME = 60 * 60 * 24 * 365; - private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + private static final JsonMapper MAPPER = new SyncopeJsonMapper(); private static final TypeReference> MAP_TYPE_REF = new TypeReference<>() { }; diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SecurityConfig.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SecurityConfig.java index 083010cf9d5..6b4040a1e87 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SecurityConfig.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SecurityConfig.java @@ -19,8 +19,8 @@ package org.apache.syncope.client.console; import org.apache.syncope.common.lib.types.IdRepoEntitlement; -import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.security.autoconfigure.actuate.web.servlet.EndpointRequest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.Customizer; diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java index b44503fbf96..650d6c79569 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java @@ -18,19 +18,6 @@ */ package org.apache.syncope.client.console.audit; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.StreamReadFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -62,6 +49,17 @@ import org.apache.wicket.model.Model; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.StreamReadFeature; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.ObjectNode; +import tools.jackson.databind.ser.std.StdSerializer; public abstract class AuditHistoryDetails extends Panel implements ModalPanel { @@ -83,8 +81,6 @@ public ObjectNode objectNode() { protected static class SortedSetJsonSerializer extends StdSerializer> { - private static final long serialVersionUID = 3849059774309L; - SortedSetJsonSerializer(final Class> clazz) { super(clazz); } @@ -93,7 +89,8 @@ protected static class SortedSetJsonSerializer extends StdSerializer> { public void serialize( final Set set, final JsonGenerator gen, - final SerializerProvider sp) throws IOException { + final SerializationContext sp) + throws JacksonException { if (set == null) { gen.writeNull(); @@ -117,7 +114,7 @@ public void serialize( } for (Object item : sorted) { - gen.writeObject(item); + gen.writePOJO(item); } } @@ -130,10 +127,12 @@ protected static Class cast(final Class aClass) { return (Class) aClass; } - protected static final ObjectMapper MAPPER = JsonMapper.builder(). - nodeFactory(new SortingNodeFactory()).build(). - registerModule(new SimpleModule().addSerializer(new SortedSetJsonSerializer(cast(Set.class)))). - registerModule(new JavaTimeModule()); + protected static final JsonMapper MAPPER = JsonMapper.builder(). + nodeFactory(new SortingNodeFactory()). + findAndAddModules(). + enable(MapperFeature.USE_GETTERS_AS_SETTERS). + addModule(new SimpleModule().addSerializer(new SortedSetJsonSerializer(cast(Set.class)))). + build(); protected EntityTO currentEntity; @@ -143,8 +142,6 @@ protected static Class cast(final Class aClass) { protected String op; - protected Class reference; - protected final List auditEntries = new ArrayList<>(); protected AuditEventTO latestAuditEventTO; @@ -175,7 +172,6 @@ public AuditHistoryDetails( this.type = type; this.category = category; this.op = op; - this.reference = (Class) currentEntity.getClass(); this.restClient = restClient; setOutputMarkupId(true); @@ -222,7 +218,7 @@ protected void onEvent(final AjaxRequestTarget target) { ? after : buildAfterAuditEventTO(beforeEvent); AuditHistoryDetails.this.addOrReplace( - new JsonDiffPanel(toJSON(beforeEvent, reference), toJSON(afterEvent, reference))); + new JsonDiffPanel(toJSON(beforeEvent), toJSON(afterEvent))); // change after audit entries in order to match only the ones newer than the current after one afterVersionsPanel.setChoices(auditEntries.stream(). filter(ae -> ae.getWhen().isAfter(beforeEvent.getWhen()) @@ -245,10 +241,10 @@ protected void onEvent(final AjaxRequestTarget target) { AuditHistoryDetails.this.addOrReplace(new JsonDiffPanel( toJSON(beforeVersionsPanel.getModelObject() == null ? latestAuditEventTO - : beforeVersionsPanel.getModelObject(), reference), + : beforeVersionsPanel.getModelObject()), toJSON(afterVersionsPanel.getModelObject() == null ? after - : buildAfterAuditEventTO(afterVersionsPanel.getModelObject()), reference))); + : buildAfterAuditEventTO(afterVersionsPanel.getModelObject())))); target.add(AuditHistoryDetails.this); } }); @@ -271,7 +267,7 @@ public void onClick(final AjaxRequestTarget target) { : MAPPER.readTree(before.getOutput()).get("entity").toPrettyString() : before.getBefore(); restore(json, target); - } catch (JsonProcessingException e) { + } catch (JacksonException e) { throw new WicketRuntimeException(e); } } @@ -301,7 +297,7 @@ protected void initDiff() { latestAuditEventTO = auditEntries.isEmpty() ? null : auditEntries.getFirst(); after = latestAuditEventTO == null ? null : buildAfterAuditEventTO(latestAuditEventTO); // add default diff panel - addOrReplace(new JsonDiffPanel(toJSON(latestAuditEventTO, reference), toJSON(after, reference))); + addOrReplace(new JsonDiffPanel(toJSON(latestAuditEventTO), toJSON(after))); beforeVersionsPanel.setChoices(auditEntries); afterVersionsPanel.setChoices(auditEntries.stream(). @@ -324,7 +320,7 @@ protected AuditEventTO buildAfterAuditEventTO(final AuditEventTO input) { return output; } - protected Model toJSON(final AuditEventTO auditEvent, final Class reference) { + protected Model toJSON(final AuditEventTO auditEvent) { if (auditEvent == null) { return Model.of(); } @@ -347,7 +343,7 @@ protected Model toJSON(final AuditEventTO auditEvent, final Class ref T entity = MAPPER.reader(). with(StreamReadFeature.STRICT_DUPLICATE_DETECTION). - readValue(content, reference); + readValue(content); if (entity instanceof UserTO userTO) { userTO.setPassword(null); userTO.setSecurityAnswer(null); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/chartjs/ChartJSPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/chartjs/ChartJSPanel.java index 57810848029..dbc946aae00 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/chartjs/ChartJSPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/chartjs/ChartJSPanel.java @@ -18,14 +18,12 @@ */ package org.apache.syncope.client.console.chartjs; -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.json.JsonMapper; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.databind.json.JsonMapper; public class ChartJSPanel extends Panel { @@ -33,8 +31,7 @@ public class ChartJSPanel extends Panel { private static final Logger LOG = LoggerFactory.getLogger(ChartJSPanel.class); - private static final JsonMapper MAPPER = JsonMapper.builder(). - findAndAddModules().serializationInclusion(Include.NON_NULL).build(); + private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); private final IModel> model; @@ -66,12 +63,12 @@ public String generateChart(final String markupId) { String dataString = null; String optionString = null; try { - Object data = (model.getObject() instanceof final SimpleChart simpleChart) + Object data = (model.getObject() instanceof final SimpleChart simpleChart) ? simpleChart.getData() : ((DataSetChart) model.getObject()).getData(); dataString = MAPPER.writeValueAsString(data); optionString = MAPPER.writeValueAsString(model.getObject().getOptions()); - } catch (JsonProcessingException e) { + } catch (Exception e) { LOG.error("Unexpected error during JSON serialization", e); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/layout/AnyLayoutUtils.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/layout/AnyLayoutUtils.java index 6b39e42a052..147eacbe054 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/layout/AnyLayoutUtils.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/layout/AnyLayoutUtils.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.client.console.layout; -import com.fasterxml.jackson.databind.json.JsonMapper; -import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.function.Function; @@ -34,15 +32,18 @@ import org.apache.syncope.client.console.rest.UserRestClient; import org.apache.syncope.client.ui.commons.layout.AbstractAnyFormLayout; import org.apache.syncope.client.ui.commons.wizards.any.AnyForm; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.to.AnyTO; import org.apache.syncope.common.lib.to.GroupTO; import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.wicket.PageReference; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.json.JsonMapper; public final class AnyLayoutUtils { - private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + private static final JsonMapper MAPPER = new SyncopeJsonMapper(); private static void setUserIfEmpty(final AnyLayout anyLayout) { if (anyLayout.getUser() == null) { @@ -94,7 +95,7 @@ public static AnyLayout fetch(final RoleRestClient roleRestClient, final List any try { result = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(anyLayout); - } catch (IOException e) { + } catch (JacksonException e) { throw new IllegalArgumentException("While generating default console layout for " + SyncopeConsoleSession.get().getSelfTO().getUsername(), e); } } else { try { result = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(MAPPER.readTree(content)); - } catch (IOException e) { + } catch (JacksonException e) { result = content; } } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DirectoryPanel.java index 95fdfa2e5bd..6ca58a9b1f5 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DirectoryPanel.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.client.console.panels; -import com.fasterxml.jackson.databind.json.JsonMapper; import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; import java.io.Serializable; import java.util.Collection; @@ -36,6 +35,7 @@ import org.apache.syncope.client.ui.commons.Constants; import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior; import org.apache.syncope.client.ui.commons.rest.RestClient; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.wicket.PageReference; import org.apache.wicket.ajax.AjaxRequestTarget; @@ -50,6 +50,7 @@ import org.apache.wicket.model.PropertyModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.databind.json.JsonMapper; public abstract class DirectoryPanel< T extends Serializable, W extends Serializable, DP extends DirectoryDataProvider, E extends RestClient> @@ -59,7 +60,7 @@ public abstract class DirectoryPanel< protected static final Logger LOG = LoggerFactory.getLogger(DirectoryPanel.class); - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); protected E restClient; diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainDirectoryPanel.java index 24fc3578425..b6a41f4b6c3 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainDirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainDirectoryPanel.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.client.console.panels; -import com.fasterxml.jackson.databind.JsonNode; import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; import java.util.ArrayList; import java.util.Collection; @@ -51,6 +50,7 @@ import org.apache.wicket.model.ResourceModel; import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.spring.injection.annot.SpringBean; +import tools.jackson.databind.JsonNode; public class DomainDirectoryPanel extends DirectoryPanel { @@ -87,7 +87,7 @@ public DomainDirectoryPanel(final String id, final PageReference pageRef) { try { JsonNode info = SyncopeConsoleSession.get().getAnonymousClient().info(); if (info.has("persistence") && info.get("persistence").has("vendor") - && "OpenJPA".equals(info.get("persistence").get("vendor").asText())) { + && "Hibernate.org".equals(info.get("persistence").get("vendor").asString())) { domainClass = JPADomain.class; } else { diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainWizardBuilder.java index ff03fdfb763..e86b9072882 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainWizardBuilder.java @@ -58,13 +58,11 @@ public class DomainWizardBuilder extends BaseAjaxWizardBuilder { "org.h2.Driver"); private static final List DATABASE_PLATFORMS = List.of( - "org.apache.openjpa.jdbc.sql.PostgresDictionary", - "org.apache.openjpa.jdbc.sql.MySQLDictionary" - + "(blobTypeName=LONGBLOB,dateFractionDigits=3,useSetStringForClobs=true)", - "org.apache.openjpa.jdbc.sql.MariaDBDictionary" - + "(blobTypeName=LONGBLOB,dateFractionDigits=3)", - "org.apache.openjpa.jdbc.sql.OracleDictionary", - "org.apache.openjpa.jdbc.sql.H2Dictionary"); + "org.apache.syncope.core.persistence.jpa.hibernate.SyncopePostgreSQLDialect", + "org.hibernate.dialect.MySQLDialect", + "org.hibernate.dialect.MariaDBDialect", + "org.hibernate.dialect.OracleDialect", + "org.hibernate.dialect.H2Dialect"); private final DomainOps domainOps; diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationModalPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationModalPanel.java index 81db01e117d..f95571bbb0a 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationModalPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationModalPanel.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.client.console.panels; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.io.IOException; import java.util.List; import org.apache.commons.lang3.StringUtils; @@ -32,6 +30,7 @@ import org.apache.syncope.client.ui.commons.Constants; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.to.ImplementationTO; import org.apache.syncope.common.lib.types.ImplementationEngine; import org.apache.wicket.PageReference; @@ -45,12 +44,15 @@ import org.apache.wicket.model.PropertyModel; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.util.io.IOUtils; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; public class ImplementationModalPanel extends AbstractModalPanel { private static final long serialVersionUID = 5283548960927517342L; - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); @SpringBean protected ImplementationRestClient implementationRestClient; @@ -100,9 +102,9 @@ public ImplementationModalPanel( try { JsonNode node = MAPPER.readTree(implementation.getBody()); if (node.has("_class")) { - jsonClass.setModelObject(node.get("_class").asText()); + jsonClass.setModelObject(node.get("_class").asString()); } - } catch (IOException e) { + } catch (JacksonException e) { LOG.error("Could not parse as JSON payload: {}", implementation.getBody(), e); } } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ParametersModalPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ParametersModalPanel.java index 4d7caad4d37..f2abc8dbb81 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ParametersModalPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ParametersModalPanel.java @@ -18,12 +18,10 @@ */ package org.apache.syncope.client.console.panels; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.json.JsonMapper; import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; import jakarta.ws.rs.core.MediaType; import java.io.IOException; -import java.io.StringReader; +import java.io.Reader; import java.text.ParseException; import java.util.Base64; import java.util.Set; @@ -34,12 +32,15 @@ import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal; import org.apache.syncope.client.ui.commons.wizards.AjaxWizard; import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.to.PlainSchemaTO; import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.wicket.PageReference; import org.bouncycastle.util.io.pem.PemReader; import org.xml.sax.InputSource; import org.xml.sax.SAXException; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.json.JsonMapper; public class ParametersModalPanel extends AbstractModalPanel { @@ -47,7 +48,7 @@ public class ParametersModalPanel extends AbstractModalPanel { protected static final Set BASE64_EXCEPTIONS = Set.of("username"); - protected static final JsonMapper JSON_MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper JSON_MAPPER = new SyncopeJsonMapper(); protected static boolean isDate(final String value) { try { @@ -71,7 +72,7 @@ protected static boolean isJSON(final String value) { try { JSON_MAPPER.readTree(value); return true; - } catch (JsonProcessingException jpe) { + } catch (JacksonException e) { return false; } } @@ -81,7 +82,7 @@ protected static boolean isXML(final String value) { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE); factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - factory.newSAXParser().getXMLReader().parse(new InputSource(new StringReader(value))); + factory.newSAXParser().getXMLReader().parse(new InputSource(Reader.of(value))); return true; } catch (IOException | ParserConfigurationException | SAXException e) { return false; @@ -89,7 +90,7 @@ protected static boolean isXML(final String value) { } protected static boolean isPEM(final String value) { - try (PemReader reader = new PemReader(new StringReader(value))) { + try (PemReader reader = new PemReader(Reader.of(value))) { return reader.readPemObject() != null; } catch (IOException e) { return false; diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleWizardBuilder.java index 12f515064e2..e43bb2cb93e 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleWizardBuilder.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.client.console.policies; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -29,6 +28,7 @@ import org.apache.syncope.client.console.wizards.BaseAjaxWizardBuilder; import org.apache.syncope.client.ui.commons.Constants; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.policy.ComposablePolicy; import org.apache.syncope.common.lib.policy.PolicyTO; import org.apache.syncope.common.lib.policy.RuleConf; @@ -44,12 +44,13 @@ import org.apache.wicket.extensions.wizard.WizardStep; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.PropertyModel; +import tools.jackson.databind.json.JsonMapper; public class PolicyRuleWizardBuilder extends BaseAjaxWizardBuilder { private static final long serialVersionUID = 5945391813567245081L; - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); protected final String policy; diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportWizardBuilder.java index 053aedaf9dd..9bec3a8a029 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportWizardBuilder.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.client.console.reports; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.io.Serializable; import java.util.List; import java.util.Optional; @@ -35,6 +34,7 @@ import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.report.ReportConf; import org.apache.syncope.common.lib.to.ImplementationTO; import org.apache.syncope.common.lib.to.ReportTO; @@ -51,12 +51,13 @@ import org.apache.wicket.model.PropertyModel; import org.springframework.beans.BeanWrapper; import org.springframework.beans.PropertyAccessorFactory; +import tools.jackson.databind.json.JsonMapper; public class ReportWizardBuilder extends BaseAjaxWizardBuilder { private static final long serialVersionUID = 5945391813567245081L; - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); protected final ImplementationRestClient implementationRestClient; diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/LoggerConfRestClient.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/LoggerConfRestClient.java index 13ea6b41bee..529265f2841 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/LoggerConfRestClient.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/LoggerConfRestClient.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.client.console.rest; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.io.InputStream; @@ -38,6 +36,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.logging.LogLevel; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; public class LoggerConfRestClient implements RestClient, LoggerConfOp { @@ -84,7 +84,7 @@ public List list() { LoggerConf loggerConf = new LoggerConf(); loggerConf.setKey(entry.getKey()); if (entry.getValue().has("effectiveLevel")) { - loggerConf.setLevel(LogLevel.valueOf(entry.getValue().get("effectiveLevel").asText())); + loggerConf.setLevel(LogLevel.valueOf(entry.getValue().get("effectiveLevel").asString())); } else { loggerConf.setLevel(LogLevel.OFF); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/CommandComposeDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/CommandComposeDirectoryPanel.java index ef7eadc11df..1f8b6ffe9bc 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/CommandComposeDirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/CommandComposeDirectoryPanel.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.client.console.tasks; -import com.fasterxml.jackson.core.JsonProcessingException; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; @@ -57,6 +56,7 @@ import org.apache.wicket.model.IModel; import org.apache.wicket.model.ResourceModel; import org.apache.wicket.spring.injection.annot.SpringBean; +import tools.jackson.core.JacksonException; public class CommandComposeDirectoryPanel extends DirectoryPanel< CommandWrapper, CommandWrapper, DirectoryDataProvider, CommandRestClient> @@ -176,7 +176,7 @@ public void onClick(final AjaxRequestTarget target, final CommandWrapper ignore) idx = i; } - } catch (JsonProcessingException e) { + } catch (JacksonException e) { LOG.error("While comparing command arguments", e); } } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/PropagationDataView.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/PropagationDataView.java index 09198c2de61..7927bb19bbe 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/PropagationDataView.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/PropagationDataView.java @@ -18,17 +18,18 @@ */ package org.apache.syncope.client.console.tasks; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; -import java.io.IOException; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.client.console.panels.MultilevelPanel; import org.apache.syncope.client.console.wicket.markup.html.form.JsonEditorPanel; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.to.PropagationTaskTO; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.model.PropertyModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; /** * Task attributes details. @@ -39,7 +40,7 @@ public class PropagationDataView extends MultilevelPanel.SecondLevel { private static final Logger LOG = LoggerFactory.getLogger(AnyPropagationTasks.class); - private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + private static final JsonMapper MAPPER = new SyncopeJsonMapper(); public PropagationDataView(final PropagationTaskTO taskTO) { super(); @@ -64,8 +65,8 @@ private static String getJSONInfo(final PropagationTaskTO taskTO) { try { JsonNode list = MAPPER.readTree(taskTO.getPropagationData()); json = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(list); - } catch (IOException ex) { - LOG.error("Error converting objects to JSON", ex); + } catch (JacksonException e) { + LOG.error("Error converting objects to JSON", e); } return json; diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java index 83995f27265..08d70b9b085 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java @@ -21,7 +21,6 @@ import java.io.Serializable; import java.util.List; import java.util.Optional; -import org.apache.commons.lang3.StringUtils; import org.apache.cxf.common.util.CollectionUtils; import org.apache.syncope.client.console.layout.GroupForm; import org.apache.syncope.client.console.layout.GroupFormLayoutInfo; @@ -105,13 +104,7 @@ protected Serializable onApplyInternal(final AnyWrapper modelObject) { // SYNCOPE-1170 boolean othersNotEqualsOrBlanks = - !updated.getADynMembershipConds().equals(original.getADynMembershipConds()) - || (StringUtils.isNotBlank(original.getUDynMembershipCond()) - && StringUtils.isBlank(updated.getUDynMembershipCond())) - || (StringUtils.isBlank(original.getUDynMembershipCond()) - && StringUtils.isNotBlank(updated.getUDynMembershipCond())) - || StringUtils.isAllBlank(original.getUDynMembershipCond(), updated.getUDynMembershipCond()) - || !updated.getUDynMembershipCond().equals(original.getUDynMembershipCond()) + !updated.getDynMembershipConds().equals(original.getDynMembershipConds()) || !CollectionUtils.diff(updated.getTypeExtensions(), original.getTypeExtensions()).isEmpty(); GroupUR groupUR = AnyOperations.diff(updated, original, false); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWrapper.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWrapper.java index 649083ec5cb..a1c4e299f1a 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWrapper.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWrapper.java @@ -21,12 +21,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; import org.apache.commons.collections4.CollectionUtils; import org.apache.syncope.client.console.panels.search.SearchClause; import org.apache.syncope.client.console.panels.search.SearchUtils; import org.apache.syncope.client.lib.SyncopeClient; import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper; import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; public class GroupWrapper extends AnyWrapper { @@ -54,10 +57,10 @@ public GroupTO getPreviousGroupTO() { } public final List getUDynClauses() { - if (this.uDynClauses == null) { - this.uDynClauses = SearchUtils.getSearchClauses(this.anyTO.getUDynMembershipCond()); + if (uDynClauses == null) { + uDynClauses = SearchUtils.getSearchClauses(anyTO.getDynMembershipConds().get(AnyTypeKind.USER.name())); } - return this.uDynClauses; + return uDynClauses; } public void setUDynClauses(final List uDynClauses) { @@ -65,10 +68,12 @@ public void setUDynClauses(final List uDynClauses) { } public final Map> getADynClauses() { - if (this.aDynClauses == null) { - this.aDynClauses = SearchUtils.getSearchClauses(this.anyTO.getADynMembershipConds()); + if (aDynClauses == null) { + aDynClauses = SearchUtils.getSearchClauses(anyTO.getDynMembershipConds().entrySet().stream(). + filter(e -> !e.getKey().equals(AnyTypeKind.USER.name())). + collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); } - return this.aDynClauses; + return aDynClauses; } public void setADynClauses(final Map> aDynClauses) { @@ -76,22 +81,20 @@ public void setADynClauses(final Map> aDynClauses) { } public String getUDynMembershipCond() { - return CollectionUtils.isEmpty(this.uDynClauses) + return CollectionUtils.isEmpty(uDynClauses) ? null - : SearchUtils.buildFIQL(this.uDynClauses, SyncopeClient.getUserSearchConditionBuilder()); + : SearchUtils.buildFIQL(uDynClauses, SyncopeClient.getUserSearchConditionBuilder()); } public Map getADynMembershipConds() { Map res = new HashMap<>(); - if (this.aDynClauses != null && !this.aDynClauses.isEmpty()) { - this.aDynClauses.entrySet().stream(). - filter(entry -> CollectionUtils.isNotEmpty(entry.getValue())). - forEach(entry -> { - String fiql = SearchUtils.buildFIQL(entry.getValue(), - SyncopeClient.getAnyObjectSearchConditionBuilder(entry.getKey())); - if (fiql != null) { - res.put(entry.getKey(), fiql); - } + if (aDynClauses != null) { + aDynClauses.entrySet().stream(). + filter(e -> CollectionUtils.isNotEmpty(e.getValue())). + forEach(e -> { + String fiql = SearchUtils.buildFIQL(e.getValue(), + SyncopeClient.getAnyObjectSearchConditionBuilder(e.getKey())); + Optional.ofNullable(fiql).ifPresent(f -> res.put(e.getKey(), f)); }); } @@ -99,9 +102,10 @@ public Map getADynMembershipConds() { } public GroupTO fillDynamicConditions() { - this.anyTO.setUDynMembershipCond(this.getUDynMembershipCond()); - this.anyTO.getADynMembershipConds().clear(); - this.anyTO.getADynMembershipConds().putAll(this.getADynMembershipConds()); - return this.anyTO; + anyTO.getDynMembershipConds().clear(); + Optional.ofNullable(getUDynMembershipCond()). + ifPresent(cond -> anyTO.getDynMembershipConds().put(AnyTypeKind.USER.name(), cond)); + anyTO.getDynMembershipConds().putAll(getADynMembershipConds()); + return anyTO; } } diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/layout/AnyLayoutTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/layout/AnyLayoutTest.java index dbf75e3b64a..a1b65d2efa9 100644 --- a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/layout/AnyLayoutTest.java +++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/layout/AnyLayoutTest.java @@ -21,19 +21,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.util.Arrays; import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.junit.jupiter.api.Test; +import tools.jackson.databind.json.JsonMapper; public class AnyLayoutTest { - private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + private static final JsonMapper MAPPER = new SyncopeJsonMapper(); @Test - public void issueSYNCOPE1554() throws JsonProcessingException { + public void issueSYNCOPE1554() { AnyLayout defaultObj = new AnyLayout(); defaultObj.setUser(new UserFormLayoutInfo()); defaultObj.setGroup(new GroupFormLayoutInfo()); diff --git a/client/idrepo/enduser/pom.xml b/client/idrepo/enduser/pom.xml index 914c3f36ea1..5dce18edf16 100644 --- a/client/idrepo/enduser/pom.xml +++ b/client/idrepo/enduser/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.client syncope-client-idrepo - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Client IdRepo Enduser @@ -48,17 +48,24 @@ under the License. com.giffing.wicket.spring.boot.starter wicket-spring-boot-starter + org.springframework.boot - spring-boot-starter-log4j2 + spring-boot-starter-security + org.springframework.boot spring-boot-starter-actuator org.springframework.boot - spring-boot-starter-security + spring-boot-health + + + + org.springframework.boot + spring-boot-starter-log4j2 @@ -101,6 +108,10 @@ under the License. com.lmax disruptor + + org.slf4j + jcl-over-slf4j + org.apache.wicket diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/PreferenceManager.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/PreferenceManager.java index 36282f2e78b..8f5b5b0ea9d 100644 --- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/PreferenceManager.java +++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/PreferenceManager.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.client.enduser; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.io.IOException; import java.io.Serializable; import java.io.StringWriter; @@ -31,10 +29,13 @@ import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.wicket.util.cookies.CookieDefaults; import org.apache.wicket.util.cookies.CookieUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.json.JsonMapper; public final class PreferenceManager implements Serializable { @@ -46,7 +47,7 @@ public final class PreferenceManager implements Serializable { private static final int ONE_YEAR_TIME = 60 * 60 * 24 * 365; - private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + private static final JsonMapper MAPPER = new SyncopeJsonMapper(); private static final TypeReference> MAP_TYPE_REF = new TypeReference<>() { }; diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SecurityConfig.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SecurityConfig.java index 97e114c7aee..d18b6522d8f 100644 --- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SecurityConfig.java +++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SecurityConfig.java @@ -19,8 +19,8 @@ package org.apache.syncope.client.enduser; import org.apache.syncope.common.lib.types.IdRepoEntitlement; -import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.security.autoconfigure.actuate.web.servlet.EndpointRequest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.Customizer; diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java index 9318e464961..6df62605a81 100644 --- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java +++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.client.enduser; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.json.JsonMapper; import com.giffing.wicket.spring.boot.starter.app.WicketBootSecuredWebApplication; import de.agilecoders.wicket.core.Bootstrap; import de.agilecoders.wicket.core.settings.BootstrapSettings; @@ -48,6 +46,7 @@ import org.apache.syncope.client.ui.commons.themes.AdminLTE; import org.apache.syncope.common.keymaster.client.api.ServiceOps; import org.apache.syncope.common.keymaster.client.api.model.NetworkService; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.wicket.Page; import org.apache.wicket.Session; import org.apache.wicket.WicketRuntimeException; @@ -70,12 +69,14 @@ import org.slf4j.LoggerFactory; import org.springframework.aop.support.AopUtils; import org.springframework.core.io.ResourceLoader; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.json.JsonMapper; public class SyncopeWebApplication extends WicketBootSecuredWebApplication implements BaseWebApplication { protected static final Logger LOG = LoggerFactory.getLogger(SyncopeWebApplication.class); - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); public static SyncopeWebApplication get() { return (SyncopeWebApplication) WebApplication.get(); diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfRegistration.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfRegistration.java index 8474a212f12..eadbd7c1938 100644 --- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfRegistration.java +++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfRegistration.java @@ -18,16 +18,16 @@ */ package org.apache.syncope.client.enduser.pages; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.json.JsonMapper; import org.apache.syncope.client.enduser.SyncopeEnduserSession; import org.apache.syncope.client.enduser.SyncopeWebApplication; import org.apache.syncope.client.enduser.layout.UserFormLayoutInfo; import org.apache.syncope.client.enduser.panels.UserSelfFormPanel; import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.to.UserTO; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.request.mapper.parameter.PageParameters; +import tools.jackson.databind.json.JsonMapper; public class SelfRegistration extends BaseNoSidebarPage { @@ -37,7 +37,7 @@ public class SelfRegistration extends BaseNoSidebarPage { public static final String NEW_USER_PARAM = "newUser"; - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); public SelfRegistration(final PageParameters parameters) { super(parameters, SELF_REGISTRATION); @@ -71,7 +71,7 @@ private static UserTO buildNewUserTO(final PageParameters parameters) { if (!parameters.get(NEW_USER_PARAM).isNull()) { try { userTO = MAPPER.readValue(parameters.get(NEW_USER_PARAM).toString(), UserTO.class); - } catch (JsonProcessingException e) { + } catch (Exception e) { LOG.error("While reading user data from social registration", e); } } diff --git a/client/idrepo/lib/pom.xml b/client/idrepo/lib/pom.xml index 3a576464dde..03d4c855cf6 100644 --- a/client/idrepo/lib/pom.xml +++ b/client/idrepo/lib/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.client syncope-client-idrepo - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Client IdRepo Lib @@ -52,7 +52,7 @@ under the License. - com.fasterxml.jackson.jakarta.rs + tools.jackson.jakarta.rs jackson-jakarta-rs-json-provider diff --git a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/RestClientExceptionMapper.java b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/RestClientExceptionMapper.java index 8eb11728ad7..738fe2e7489 100644 --- a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/RestClientExceptionMapper.java +++ b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/RestClientExceptionMapper.java @@ -94,13 +94,11 @@ public Exception fromResponse(final Response response) { } private static SyncopeClientCompositeException checkSyncopeClientCompositeException(final Response response) { - SyncopeClientCompositeException compException = SyncopeClientException.buildComposite(); - // Attempts to read ErrorTO or List as entity... List errors = null; try { ErrorTO error = response.readEntity(ErrorTO.class); - if (error != null) { + if (error != null && error.getType() != null) { errors = List.of(error); } } catch (Exception e) { @@ -116,6 +114,7 @@ private static SyncopeClientCompositeException checkSyncopeClientCompositeExcept } // ...if not possible, attempts to parse response headers + SyncopeClientCompositeException compException = SyncopeClientException.buildComposite(); if (errors == null) { List exTypesInHeaders = response.getStringHeaders().get(RESTHeaders.ERROR_CODE); if (exTypesInHeaders == null) { diff --git a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeAnonymousClient.java b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeAnonymousClient.java index c703687722d..5382f13af3e 100644 --- a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeAnonymousClient.java +++ b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeAnonymousClient.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.client.lib; -import com.fasterxml.jackson.databind.JsonNode; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import java.io.IOException; @@ -35,6 +34,7 @@ import org.apache.syncope.common.lib.info.PlatformInfo; import org.apache.syncope.common.lib.info.SystemInfo; import org.apache.syncope.common.rest.api.RESTHeaders; +import tools.jackson.databind.JsonNode; public class SyncopeAnonymousClient extends SyncopeClient { @@ -74,9 +74,9 @@ public Pair gitAndBuildInfo() { JsonNode info = info(); return Pair.of( info.has("git") && info.get("git").has("commit") && info.get("git").get("commit").has("id") - ? info.get("git").get("commit").get("id").asText() + ? info.get("git").get("commit").get("id").asString() : StringUtils.EMPTY, - info.get("build").get("version").asText()); + info.get("build").get("version").asString()); } catch (IOException e) { throw new RuntimeException("While getting build and git Info", e); } diff --git a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java index 2b88c481f48..1d4b5300598 100644 --- a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java +++ b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java @@ -18,13 +18,10 @@ */ package org.apache.syncope.client.lib; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.json.JsonMapper; import jakarta.ws.rs.core.EntityTag; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; -import java.io.IOException; import java.io.Serializable; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; @@ -59,6 +56,9 @@ import org.apache.syncope.common.rest.api.service.UserSelfService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.json.JsonMapper; /** * Entry point for client access to all REST services exposed by Syncope core; obtain instances via @@ -214,7 +214,8 @@ public static EntityTag getLatestEntityTag(final T service) { protected static final String HEADER_SPLIT_PROPERTY = "org.apache.cxf.http.header.split"; - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules(). + enable(MapperFeature.USE_GETTERS_AS_SETTERS).build(); protected final JAXRSClientFactoryBean restClientFactory; @@ -442,7 +443,7 @@ public Self self() { MAPPER.readValue( response.getHeaderString(RESTHeaders.DELEGATIONS), new TypeReference<>() { })); - } catch (IOException e) { + } catch (Exception e) { throw new IllegalStateException(e); } } diff --git a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java index a1b45d92d9e..21ce8214f3d 100644 --- a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java +++ b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.client.lib; -import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import java.util.HashMap; import java.util.List; import java.util.Optional; @@ -31,6 +30,7 @@ import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.rest.api.DateParamConverterProvider; import org.apache.syncope.common.rest.api.RESTHeaders; +import tools.jackson.jakarta.rs.json.JacksonJsonProvider; /** * Factory bean for creating instances of {@link SyncopeClient}. diff --git a/client/idrepo/pom.xml b/client/idrepo/pom.xml index fb6ecc8daca..68ec9dcdf0e 100644 --- a/client/idrepo/pom.xml +++ b/client/idrepo/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-client - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Client IdRepo diff --git a/client/pom.xml b/client/pom.xml index 97ed61e3517..23a878963ff 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Client diff --git a/common/am/lib/pom.xml b/common/am/lib/pom.xml index b446b45d44c..9efe89b6ac0 100644 --- a/common/am/lib/pom.xml +++ b/common/am/lib/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.common syncope-common-am - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common AM Lib diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java index b6df7e58494..715447ecf02 100644 --- a/common/am/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java @@ -20,7 +20,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.io.IOException; import java.io.StringWriter; import java.net.URI; @@ -28,6 +27,7 @@ import org.apache.syncope.common.lib.policy.AccessPolicyTO; import org.apache.syncope.common.lib.policy.DefaultAccessPolicyConf; import org.junit.jupiter.api.Test; +import tools.jackson.databind.json.JsonMapper; public class SerializationTest { diff --git a/common/am/pom.xml b/common/am/pom.xml index 80074191a01..95ec6604e4d 100644 --- a/common/am/pom.xml +++ b/common/am/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-common - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common AM diff --git a/common/am/rest-api/pom.xml b/common/am/rest-api/pom.xml index 9d09c294911..398102e3ba9 100644 --- a/common/am/rest-api/pom.xml +++ b/common/am/rest-api/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.common syncope-common-am - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common AM REST API diff --git a/common/idm/lib/pom.xml b/common/idm/lib/pom.xml index 040a51929ec..4953618bc2d 100644 --- a/common/idm/lib/pom.xml +++ b/common/idm/lib/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.common syncope-common-idm - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common IdM Lib diff --git a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java index d1519adc964..a937cf02ca6 100644 --- a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java +++ b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java @@ -121,7 +121,7 @@ public void setVersion(final String version) { } public List getConf() { - return this.conf; + return conf; } @JsonIgnore diff --git a/common/idm/pom.xml b/common/idm/pom.xml index c740e834128..8946a5e6d21 100644 --- a/common/idm/pom.xml +++ b/common/idm/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-common - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common IdM diff --git a/common/idm/rest-api/pom.xml b/common/idm/rest-api/pom.xml index 92645988109..93f62018083 100644 --- a/common/idm/rest-api/pom.xml +++ b/common/idm/rest-api/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.common syncope-common-idm - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common IdM REST API diff --git a/common/idrepo/lib/pom.xml b/common/idrepo/lib/pom.xml index a0d6b4ddaf2..43e13f23905 100644 --- a/common/idrepo/lib/pom.xml +++ b/common/idrepo/lib/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.common syncope-common-idrepo - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common IdRepo Lib @@ -53,12 +53,8 @@ under the License. - com.fasterxml.jackson.core - jackson-annotations - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 + tools.jackson.core + jackson-databind diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/AnyOperations.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/AnyOperations.java index 1815e44c603..b6ff6d7c8b7 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/AnyOperations.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/AnyOperations.java @@ -342,8 +342,7 @@ public static GroupUR diff(final GroupTO updated, final GroupTO original, final replacePatchItem(updated.getGroupOwner(), original.getGroupOwner(), new StringReplacePatchItem())); // 3. dynamic membership - result.setUDynMembershipCond(updated.getUDynMembershipCond()); - result.getADynMembershipConds().putAll(updated.getADynMembershipConds()); + result.getDynMembershipConds().putAll(updated.getDynMembershipConds()); // 4. type extensions result.getTypeExtensions().addAll(updated.getTypeExtensions()); @@ -478,9 +477,8 @@ public static GroupTO patch(final GroupTO groupTO, final GroupUR groupUR) { result.setGroupOwner(groupUR.getGroupOwner().getValue()); } - result.setUDynMembershipCond(groupUR.getUDynMembershipCond()); - result.getADynMembershipConds().clear(); - result.getADynMembershipConds().putAll(groupUR.getADynMembershipConds()); + result.getDynMembershipConds().clear(); + result.getDynMembershipConds().putAll(groupUR.getDynMembershipConds()); relationships(groupUR.getRelationships(), result); diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/EntityTOUtils.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/EntityTOUtils.java index 7f10da1df0e..da03d4ec4f0 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/EntityTOUtils.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/EntityTOUtils.java @@ -82,8 +82,7 @@ public static void toAnyCR(final A anyTO, fin groupCR.setName(groupTO.getName()); groupCR.setUserOwner(groupTO.getUserOwner()); groupCR.setGroupOwner(groupTO.getGroupOwner()); - groupCR.setUDynMembershipCond(groupTO.getUDynMembershipCond()); - groupCR.getADynMembershipConds().putAll(groupTO.getADynMembershipConds()); + groupCR.getDynMembershipConds().putAll(groupTO.getDynMembershipConds()); groupCR.getTypeExtensions().addAll(groupTO.getTypeExtensions()); } else if (anyCR instanceof final AnyObjectCR anyObjectCR && anyTO instanceof final AnyObjectTO anyObjectTO) { anyObjectCR.setType(anyObjectTO.getType()); @@ -112,8 +111,7 @@ public static void toAnyTO(final C anyCR, fin groupTO.setName(groupCR.getName()); groupTO.setUserOwner(groupCR.getUserOwner()); groupTO.setGroupOwner(groupCR.getGroupOwner()); - groupTO.setUDynMembershipCond(groupCR.getUDynMembershipCond()); - groupTO.getADynMembershipConds().putAll(groupCR.getADynMembershipConds()); + groupTO.getDynMembershipConds().putAll(groupCR.getDynMembershipConds()); groupTO.getTypeExtensions().addAll(groupCR.getTypeExtensions()); } else if (anyTO instanceof final AnyObjectTO anyObjectTO && anyCR instanceof final AnyObjectCR anyObjectCR) { anyObjectTO.setType(anyObjectCR.getType()); diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/jackson/SyncopeJsonMapper.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/jackson/SyncopeJsonMapper.java index ef7d5525ec9..e5840503fae 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/jackson/SyncopeJsonMapper.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/jackson/SyncopeJsonMapper.java @@ -18,15 +18,17 @@ */ package org.apache.syncope.common.lib.jackson; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.json.JsonMapper; +import java.io.DataOutput; import java.io.File; -import java.io.IOException; import java.io.OutputStream; import java.io.Writer; +import java.nio.file.Path; import java.util.Map; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.util.TokenBuffer; /** * Jackson ObjectMapper that unwraps singleton map values and enable default @@ -37,11 +39,7 @@ public class SyncopeJsonMapper extends JsonMapper { private static final long serialVersionUID = -317191546835195103L; public SyncopeJsonMapper() { - super(); - - findAndRegisterModules(); - - configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + super(JsonMapper.builder().findAndAddModules().enable(MapperFeature.USE_GETTERS_AS_SETTERS)); } /** @@ -62,32 +60,47 @@ protected Object unwrapMap(final Object value) { } @Override - public void writeValue(final JsonGenerator jgen, final Object value) throws IOException { + public void writeValue(final DataOutput out, final Object value) throws JacksonException { + super.writeValue(out, unwrapMap(value)); + } + + @Override + public void writeValue(final Path path, final Object value) throws JacksonException { + super.writeValue(path, unwrapMap(value)); + } + + @Override + public TokenBuffer writeValueIntoBuffer(final Object value) throws JacksonException { + return super.writeValueIntoBuffer(unwrapMap(value)); + } + + @Override + public void writeValue(final JsonGenerator jgen, final Object value) throws JacksonException { super.writeValue(jgen, unwrapMap(value)); } @Override - public void writeValue(final File resultFile, final Object value) throws IOException { + public void writeValue(final File resultFile, final Object value) throws JacksonException { super.writeValue(resultFile, unwrapMap(value)); } @Override - public void writeValue(final OutputStream out, final Object value) throws IOException { + public void writeValue(final OutputStream out, final Object value) throws JacksonException { super.writeValue(out, unwrapMap(value)); } @Override - public void writeValue(final Writer writer, final Object value) throws IOException { + public void writeValue(final Writer writer, final Object value) throws JacksonException { super.writeValue(writer, unwrapMap(value)); } @Override - public byte[] writeValueAsBytes(final Object value) throws JsonProcessingException { + public byte[] writeValueAsBytes(final Object value) throws JacksonException { return super.writeValueAsBytes(unwrapMap(value)); } @Override - public String writeValueAsString(final Object value) throws JsonProcessingException { + public String writeValueAsString(final Object value) throws JacksonException { return super.writeValueAsString(unwrapMap(value)); } } diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/request/GroupCR.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/request/GroupCR.java index 767f1a1a9d2..4fcaadb0c0d 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/request/GroupCR.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/request/GroupCR.java @@ -58,13 +58,8 @@ public Builder groupOwner(final String groupOwner) { return this; } - public Builder udynMembershipCond(final String udynMembershipCond) { - getInstance().setUDynMembershipCond(udynMembershipCond); - return this; - } - - public Builder adynMembershipCond(final String type, final String fiql) { - getInstance().getADynMembershipConds().put(type, fiql); + public Builder dynMembershipCond(final String type, final String fiql) { + getInstance().getDynMembershipConds().put(type, fiql); return this; } @@ -90,9 +85,7 @@ public Builder typeExtensions(final Collection typeExtensions) private String groupOwner; - private String udynMembershipCond; - - private final Map adynMembershipConds = new HashMap<>(); + private final Map dynMembershipConds = new HashMap<>(); private final List typeExtensions = new ArrayList<>(); @@ -129,16 +122,8 @@ public void setGroupOwner(final String groupOwner) { this.groupOwner = groupOwner; } - public String getUDynMembershipCond() { - return udynMembershipCond; - } - - public void setUDynMembershipCond(final String uDynMembershipCond) { - this.udynMembershipCond = uDynMembershipCond; - } - - public Map getADynMembershipConds() { - return adynMembershipConds; + public Map getDynMembershipConds() { + return dynMembershipConds; } public List getTypeExtensions() { @@ -152,8 +137,7 @@ public int hashCode() { append(name). append(userOwner). append(groupOwner). - append(udynMembershipCond). - append(adynMembershipConds). + append(dynMembershipConds). append(typeExtensions). build(); } @@ -175,8 +159,7 @@ public boolean equals(final Object obj) { append(name, other.name). append(userOwner, other.userOwner). append(groupOwner, other.groupOwner). - append(udynMembershipCond, other.udynMembershipCond). - append(adynMembershipConds, other.adynMembershipConds). + append(dynMembershipConds, other.dynMembershipConds). append(typeExtensions, other.typeExtensions). build(); } diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/request/GroupUR.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/request/GroupUR.java index 38756c04692..697f500263e 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/request/GroupUR.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/request/GroupUR.java @@ -62,18 +62,13 @@ public Builder groupOwner(final StringReplacePatchItem groupOwner) { return this; } - public Builder udynMembershipCond(final String udynMembershipCond) { - getInstance().setUDynMembershipCond(udynMembershipCond); + public Builder dynMembershipCond(final String type, final String fiql) { + getInstance().getDynMembershipConds().put(type, fiql); return this; } - public Builder adynMembershipCond(final String type, final String fiql) { - getInstance().getADynMembershipConds().put(type, fiql); - return this; - } - - public Builder adynMembershipConds(final Map conds) { - getInstance().getADynMembershipConds().putAll(conds); + public Builder dynMembershipConds(final Map conds) { + getInstance().getDynMembershipConds().putAll(conds); return this; } @@ -99,9 +94,7 @@ public Builder typeExtensions(final Collection typeExtensions) private StringReplacePatchItem groupOwner; - private String udynMembershipCond; - - private final Map adynMembershipConds = new HashMap<>(); + private final Map dynMembershipConds = new HashMap<>(); private final List typeExtensions = new ArrayList<>(); @@ -137,16 +130,8 @@ public void setGroupOwner(final StringReplacePatchItem groupOwner) { this.groupOwner = groupOwner; } - public String getUDynMembershipCond() { - return udynMembershipCond; - } - - public void setUDynMembershipCond(final String udynMembershipCond) { - this.udynMembershipCond = udynMembershipCond; - } - - public Map getADynMembershipConds() { - return adynMembershipConds; + public Map getDynMembershipConds() { + return dynMembershipConds; } @JsonIgnore @@ -162,8 +147,8 @@ public List getTypeExtensions() { @Override public boolean isEmpty() { return super.isEmpty() - && name == null && userOwner == null && groupOwner == null && udynMembershipCond == null - && adynMembershipConds.isEmpty() && typeExtensions.isEmpty(); + && name == null && userOwner == null && groupOwner == null + && dynMembershipConds.isEmpty() && typeExtensions.isEmpty(); } @Override @@ -173,8 +158,7 @@ public int hashCode() { append(name). append(userOwner). append(groupOwner). - append(udynMembershipCond). - append(adynMembershipConds). + append(dynMembershipConds). append(typeExtensions). build(); } @@ -196,8 +180,7 @@ public boolean equals(final Object obj) { append(name, other.name). append(userOwner, other.userOwner). append(groupOwner, other.groupOwner). - append(udynMembershipCond, other.udynMembershipCond). - append(adynMembershipConds, other.adynMembershipConds). + append(dynMembershipConds, other.dynMembershipConds). append(typeExtensions, other.typeExtensions). build(); } diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/GroupTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/GroupTO.java index 80305b712f8..56ec2d255c6 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/GroupTO.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/GroupTO.java @@ -41,8 +41,6 @@ public class GroupTO extends AnyTO implements TypeExtensionHolderTO { private String groupOwner; - private String udynMembershipCond; - private long staticUserMembershipCount; private long dynamicUserMembershipCount; @@ -51,7 +49,7 @@ public class GroupTO extends AnyTO implements TypeExtensionHolderTO { private long dynamicAnyObjectMembershipCount; - private final Map adynMembershipConds = new HashMap<>(); + private final Map dynMembershipConds = new HashMap<>(); private final List typeExtensions = new ArrayList<>(); @@ -97,14 +95,6 @@ public void setGroupOwner(final String groupOwner) { this.groupOwner = groupOwner; } - public String getUDynMembershipCond() { - return udynMembershipCond; - } - - public void setUDynMembershipCond(final String uDynMembershipCond) { - this.udynMembershipCond = uDynMembershipCond; - } - public long getStaticUserMembershipCount() { return staticUserMembershipCount; } @@ -137,8 +127,8 @@ public void setDynamicAnyObjectMembershipCount(final long dynamicAnyObjectMember this.dynamicAnyObjectMembershipCount = dynamicAnyObjectMembershipCount; } - public Map getADynMembershipConds() { - return adynMembershipConds; + public Map getDynMembershipConds() { + return dynMembershipConds; } @JsonIgnore @@ -161,8 +151,7 @@ public int hashCode() { append(name). append(userOwner). append(groupOwner). - append(udynMembershipCond). - append(adynMembershipConds). + append(dynMembershipConds). append(typeExtensions). build(); } @@ -184,8 +173,7 @@ public boolean equals(final Object obj) { append(name, other.name). append(userOwner, other.userOwner). append(groupOwner, other.groupOwner). - append(udynMembershipCond, other.udynMembershipCond). - append(adynMembershipConds, other.adynMembershipConds). + append(dynMembershipConds, other.dynMembershipConds). append(typeExtensions, other.typeExtensions). build(); } diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java index 439cca4e817..9a9b6676efd 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java @@ -53,24 +53,27 @@ public enum EntityViolationType { private Object invalidValue; - public void setMessage(final String message) { + public EntityViolationType message(final String message) { this.message = message; + return this; } public String getMessage() { return message; } - public String getPropertyPath() { - return propertyPath; + public EntityViolationType propertyPath(final String propertyPath) { + this.propertyPath = propertyPath; + return this; } - public void setPropertyPath(final String propertyPath) { - this.propertyPath = propertyPath; + public String getPropertyPath() { + return propertyPath; } - public void setInvalidValue(final Object invalidValue) { + public EntityViolationType invalidValue(final Object invalidValue) { this.invalidValue = invalidValue; + return this; } public Object getInvalidValue() { diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java index bd82ee2b3ff..a8991dc2e35 100644 --- a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java @@ -21,8 +21,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; @@ -43,6 +41,8 @@ import org.apache.syncope.common.lib.to.ReportTO; import org.apache.syncope.common.lib.types.PatchOperation; import org.junit.jupiter.api.Test; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.json.JsonMapper; public class SerializationTest { @@ -78,8 +78,8 @@ public void nonEmptyListAsMember() throws IOException { public void map() throws IOException { GroupUR req = new GroupUR(); req.setKey(UUID.randomUUID().toString()); - req.getADynMembershipConds().put("key1", "value1"); - req.getADynMembershipConds().put("key2", "value2"); + req.getDynMembershipConds().put("key1", "value1"); + req.getDynMembershipConds().put("key2", "value2"); StringWriter writer = new StringWriter(); MAPPER.writeValue(writer, req); @@ -115,7 +115,7 @@ public void pagedResult() throws IOException { group.setName(UUID.randomUUID().toString()); group.setRealm(SyncopeConstants.ROOT_REALM); group.getPlainAttrs().add(new Attr.Builder("style").value("cool").value("great").build()); - group.getADynMembershipConds().put("USER", "username==a*"); + group.getDynMembershipConds().put("USER", "username==a*"); PagedResult original = new PagedResult<>(); original.getResult().add(group); @@ -135,7 +135,7 @@ public void provisioningResult() throws IOException { GroupTO group = new GroupTO(); group.setName(UUID.randomUUID().toString()); group.setRealm(SyncopeConstants.ROOT_REALM); - group.getADynMembershipConds().put("USER", "username==a*"); + group.getDynMembershipConds().put("USER", "username==a*"); ProvisioningResult original = new ProvisioningResult<>(); original.setEntity(group); diff --git a/common/idrepo/pom.xml b/common/idrepo/pom.xml index 6e43eadef7e..715ef15da01 100644 --- a/common/idrepo/pom.xml +++ b/common/idrepo/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-common - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common IdRepo diff --git a/common/idrepo/rest-api/pom.xml b/common/idrepo/rest-api/pom.xml index 4058f764a1c..0aadfb5fcd6 100644 --- a/common/idrepo/rest-api/pom.xml +++ b/common/idrepo/rest-api/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.common syncope-common-idrepo - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common IdRepo REST API diff --git a/common/keymaster/client-api/pom.xml b/common/keymaster/client-api/pom.xml index 153c17c0a44..4b809c6ecaa 100644 --- a/common/keymaster/client-api/pom.xml +++ b/common/keymaster/client-api/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.common syncope-common-keymaster - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common Keymaster Client API diff --git a/common/keymaster/client-zookeeper/pom.xml b/common/keymaster/client-zookeeper/pom.xml index 2214108676a..ae49d603546 100644 --- a/common/keymaster/client-zookeeper/pom.xml +++ b/common/keymaster/client-zookeeper/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.common syncope-common-keymaster - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common Keymaster Client Zookeeper @@ -61,11 +61,6 @@ under the License. org.springframework.boot spring-boot-autoconfigure - - - com.fasterxml.jackson.core - jackson-databind - org.apache.curator diff --git a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperConfParamOps.java b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperConfParamOps.java index aca58140c34..61bd7080e2d 100644 --- a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperConfParamOps.java +++ b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperConfParamOps.java @@ -18,16 +18,17 @@ */ package org.apache.syncope.common.keymaster.client.zookeeper; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.util.Map; import java.util.Optional; import java.util.TreeMap; import org.apache.curator.framework.CuratorFramework; import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.common.keymaster.client.api.KeymasterException; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.zookeeper.KeeperException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.databind.json.JsonMapper; /** * Implements {@link ConfParamOps} via Apache Curator / Zookeeper. @@ -36,7 +37,7 @@ public class ZookeeperConfParamOps implements ConfParamOps { protected static final Logger LOG = LoggerFactory.getLogger(ConfParamOps.class); - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); protected static final String CONF_PATH = "/conf"; diff --git a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOps.java b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOps.java index f6d212d81ca..79726bc4693 100644 --- a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOps.java +++ b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOps.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.common.keymaster.client.zookeeper; -import com.fasterxml.jackson.databind.json.JsonMapper; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -33,11 +31,13 @@ import org.apache.syncope.common.keymaster.client.api.model.JPADomain; import org.apache.syncope.common.keymaster.client.api.model.Neo4jDomain; import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; +import tools.jackson.databind.json.JsonMapper; /** * Implements {@link DomainOps} via Apache Curator / Zookeeper. @@ -46,7 +46,7 @@ public class ZookeeperDomainOps implements DomainOps, InitializingBean { protected static final Logger LOG = LoggerFactory.getLogger(DomainOps.class); - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); protected static final String DOMAIN_PATH = "/domains"; @@ -86,7 +86,7 @@ public void afterPropertiesSet() throws Exception { LOG.info("Domain {} created", domain.getKey()); watcher.added(domain); - } catch (IOException e) { + } catch (Exception e) { LOG.debug("Could not parse {}", new String(newData.getData()), e); } } diff --git a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOps.java b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOps.java index d6b374005ab..4057e8c667b 100644 --- a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOps.java +++ b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOps.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.common.keymaster.client.zookeeper; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -27,10 +26,12 @@ import org.apache.syncope.common.keymaster.client.api.KeymasterException; import org.apache.syncope.common.keymaster.client.api.ServiceOps; import org.apache.syncope.common.keymaster.client.api.model.NetworkService; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.zookeeper.CreateMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import tools.jackson.databind.json.JsonMapper; /** * Implements {@link ServiceOps} via Apache Curator / Zookeeper. @@ -39,7 +40,7 @@ public class ZookeeperServiceOps implements ServiceOps { protected static final Logger LOG = LoggerFactory.getLogger(ServiceOps.class); - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); protected static final String SERVICE_PATH = "/services"; diff --git a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsITCase.java b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsITCase.java index a3e9af1e90a..b52249e8cec 100644 --- a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsITCase.java +++ b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsITCase.java @@ -51,7 +51,7 @@ public void crud() { jdbcURL("jdbc:h2:mem:syncopetest;DB_CLOSE_DELAY=-1"). dbUsername("sa"). dbPassword(""). - databasePlatform("org.apache.openjpa.jdbc.sql.H2Dictionary"). + databasePlatform("org.hibernate.dialect.H2Dialect"). transactionIsolation(JPADomain.TransactionIsolation.TRANSACTION_READ_UNCOMMITTED). adminPassword("password"). adminCipherAlgorithm(CipherAlgorithm.BCRYPT). diff --git a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperTestContentLoader.java b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperTestContentLoader.java index 36ef717682a..4a6cd85215a 100644 --- a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperTestContentLoader.java +++ b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperTestContentLoader.java @@ -18,17 +18,18 @@ */ package org.apache.syncope.common.keymaster.client.zookeeper; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.util.Map; import java.util.Optional; import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; public class ZookeeperTestContentLoader implements InitializingBean { - private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + private static final JsonMapper MAPPER = new SyncopeJsonMapper(); @Autowired private ConfParamOps confParamOps; diff --git a/common/keymaster/pom.xml b/common/keymaster/pom.xml index 1d0b22959ea..9e7bf3c910e 100644 --- a/common/keymaster/pom.xml +++ b/common/keymaster/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-common - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common Keymaster diff --git a/common/keymaster/self/client-self/pom.xml b/common/keymaster/self/client-self/pom.xml index ec2590f4391..db517a37d22 100644 --- a/common/keymaster/self/client-self/pom.xml +++ b/common/keymaster/self/client-self/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.common.keymaster syncope-common-keymaster-self - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common Keymaster Client Self @@ -64,7 +64,7 @@ under the License. - com.fasterxml.jackson.jakarta.rs + tools.jackson.jakarta.rs jackson-jakarta-rs-json-provider diff --git a/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientContext.java b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientContext.java index 3f604bedaf5..a0d5b3c71e0 100644 --- a/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientContext.java +++ b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientContext.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.common.keymaster.client.self; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import java.util.List; import java.util.regex.Pattern; import org.apache.cxf.ext.logging.LoggingFeature; @@ -28,6 +26,7 @@ import org.apache.syncope.common.keymaster.client.api.DomainOps; import org.apache.syncope.common.keymaster.client.api.KeymasterProperties; import org.apache.syncope.common.keymaster.client.api.ServiceOps; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -38,6 +37,7 @@ import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.type.AnnotatedTypeMetadata; +import tools.jackson.jakarta.rs.json.JacksonJsonProvider; @EnableConfigurationProperties(KeymasterProperties.class) @Configuration(proxyBeanMethods = false) @@ -68,7 +68,7 @@ public JAXRSClientFactoryBean selfKeymasterRESTClientFactoryBean(final Keymaster restClientFactoryBean.setInheritHeaders(true); restClientFactoryBean.getFeatures().add(new LoggingFeature()); restClientFactoryBean.setProviders(List.of( - new JacksonJsonProvider(JsonMapper.builder().findAndAddModules().build()), + new JacksonJsonProvider(new SyncopeJsonMapper()), new SelfKeymasterClientExceptionMapper())); return restClientFactoryBean; } diff --git a/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterConfParamOps.java b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterConfParamOps.java index b3e5d43f308..644fbb53efa 100644 --- a/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterConfParamOps.java +++ b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterConfParamOps.java @@ -18,26 +18,26 @@ */ package org.apache.syncope.common.keymaster.client.self; -import com.fasterxml.jackson.databind.json.JsonMapper; import jakarta.ws.rs.core.Response; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.io.InputStream; import java.util.Map; import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean; import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.common.keymaster.client.api.KeymasterException; import org.apache.syncope.common.keymaster.rest.api.service.ConfParamService; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.rest.api.RESTHeaders; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.databind.json.JsonMapper; public class SelfKeymasterConfParamOps extends SelfKeymasterOps implements ConfParamOps { private static final Logger LOG = LoggerFactory.getLogger(ConfParamOps.class); - private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + private static final JsonMapper MAPPER = new SyncopeJsonMapper(); public SelfKeymasterConfParamOps(final JAXRSClientFactoryBean clientFactory) { super(clientFactory); @@ -56,7 +56,7 @@ public T get(final String domain, final String key, final T defaultValue, fi } try { return MAPPER.readValue(response.readEntity(InputStream.class), reference); - } catch (IOException e) { + } catch (Exception e) { LOG.error("Could not deserialize response", e); return defaultValue; } @@ -73,7 +73,7 @@ public void set(final String domain, final String key, final T value) { client(ConfParamService.class, Map.of(RESTHeaders.DOMAIN, domain)). set(key, new ByteArrayInputStream(baos.toByteArray())); - } catch (IOException e) { + } catch (Exception e) { throw new KeymasterException("Could not serialize " + value, e); } } diff --git a/common/keymaster/self/pom.xml b/common/keymaster/self/pom.xml index c155f1e8f32..baba298eca0 100644 --- a/common/keymaster/self/pom.xml +++ b/common/keymaster/self/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.common syncope-common-keymaster - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common Keymaster Self diff --git a/common/keymaster/self/rest-api/pom.xml b/common/keymaster/self/rest-api/pom.xml index 35bb8047620..814b704fef1 100644 --- a/common/keymaster/self/rest-api/pom.xml +++ b/common/keymaster/self/rest-api/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.common.keymaster syncope-common-keymaster-self - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common Keymaster Self REST API diff --git a/common/pom.xml b/common/pom.xml index 62f2733dddf..dd9525928bf 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Common diff --git a/core/am/logic/pom.xml b/core/am/logic/pom.xml index 6093bfc9c77..2ade67d2b3b 100644 --- a/core/am/logic/pom.xml +++ b/core/am/logic/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.core syncope-core-am - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core AM Logic diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogic.java index 2877126edd8..4e1728d5731 100644 --- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogic.java +++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogic.java @@ -56,7 +56,7 @@ public List list() { @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") public void delete(final String owner) { authProfileDAO.findByOwner(owner).ifPresent(profile -> { - profile.setGoogleMfaAuthAccounts(List.of()); + profile.getGoogleMfaAuthAccounts().clear(); authProfileDAO.save(profile); }); } @@ -66,10 +66,7 @@ public void delete(final long id) { authProfileDAO.findAll(Pageable.unpaged()). stream(). filter(Objects::nonNull). - filter(profile -> profile. - getGoogleMfaAuthAccounts(). - stream(). - allMatch(acct -> acct.getId() == id)). + filter(profile -> profile.getGoogleMfaAuthAccounts().stream().allMatch(acct -> acct.getId() == id)). findFirst(). ifPresentOrElse( profile -> { @@ -85,7 +82,7 @@ public void delete(final long id) { @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") public void deleteAll() { authProfileDAO.findAll(Pageable.unpaged()).forEach(profile -> { - profile.setGoogleMfaAuthAccounts(List.of()); + profile.getGoogleMfaAuthAccounts().clear(); authProfileDAO.save(profile); }); } @@ -94,23 +91,21 @@ public void deleteAll() { public void create(final GoogleMfaAuthAccount account) { AuthProfile profile = authProfile(account.getUsername()); - List accounts = profile.getGoogleMfaAuthAccounts(); - accounts.add(account); - profile.setGoogleMfaAuthAccounts(accounts); + profile.getGoogleMfaAuthAccounts().removeIf(acct -> acct.getId() == account.getId()); + profile.getGoogleMfaAuthAccounts().add(account); + authProfileDAO.save(profile); } @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") public void update(final GoogleMfaAuthAccount account) { - AuthProfile authProfile = authProfileDAO.findByOwner(account.getUsername()). + AuthProfile profile = authProfileDAO.findByOwner(account.getUsername()). orElseThrow(() -> new NotFoundException("Could not find account for Owner " + account.getUsername())); - List accounts = authProfile.getGoogleMfaAuthAccounts(); - if (accounts.removeIf(acct -> acct.getId() == account.getId())) { - accounts.add(account); - authProfile.setGoogleMfaAuthAccounts(accounts); - authProfileDAO.save(authProfile); - } + profile.getGoogleMfaAuthAccounts().removeIf(acct -> acct.getId() == account.getId()); + profile.add(account); + + authProfileDAO.save(profile); } @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogic.java index b5a984a5c2a..0606c11762b 100644 --- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogic.java +++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogic.java @@ -44,9 +44,7 @@ public GoogleMfaAuthTokenLogic( } protected void removeTokenAndSave(final AuthProfile profile, final Predicate criteria) { - List tokens = profile.getGoogleMfaAuthTokens(); - if (tokens.removeIf(criteria)) { - profile.setGoogleMfaAuthTokens(tokens); + if (profile.getGoogleMfaAuthTokens().removeIf(criteria)) { authProfileDAO.save(profile); } } @@ -66,7 +64,7 @@ public void delete(final String owner, final int otp) { @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") public void delete(final String owner) { authProfileDAO.findByOwner(owner).ifPresent(profile -> { - profile.setGoogleMfaAuthTokens(List.of()); + profile.getGoogleMfaAuthTokens().clear(); authProfileDAO.save(profile); }); } @@ -80,7 +78,7 @@ public void delete(final int otp) { @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") public void deleteAll() { authProfileDAO.findAll(Pageable.unpaged()).forEach(profile -> { - profile.setGoogleMfaAuthTokens(List.of()); + profile.getGoogleMfaAuthTokens().clear(); authProfileDAO.save(profile); }); } @@ -89,10 +87,9 @@ public void deleteAll() { public void store(final String owner, final GoogleMfaAuthToken token) { AuthProfile profile = authProfile(owner); - List tokens = profile.getGoogleMfaAuthTokens(); - tokens.removeIf(t -> t.getOtp() == token.getOtp()); - tokens.add(token); - profile.setGoogleMfaAuthTokens(tokens); + profile.getGoogleMfaAuthTokens().removeIf(t -> t.getOtp() == token.getOtp()); + profile.add(token); + authProfileDAO.save(profile); } @@ -122,6 +119,6 @@ public List list() { public List read(final String owner) { return authProfileDAO.findByOwner(owner). map(AuthProfile::getGoogleMfaAuthTokens). - orElseGet(List::of); + orElseGet(List::of); } } diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/ImpersonationLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/ImpersonationLogic.java index 12e034a71d6..4285ed01cab 100644 --- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/ImpersonationLogic.java +++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/ImpersonationLogic.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.logic.wa; -import java.util.ArrayList; import java.util.List; import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.common.lib.wa.ImpersonationAccount; @@ -53,9 +52,7 @@ public void create(final String owner, final ImpersonationAccount account) { if (profile.getImpersonationAccounts().stream(). noneMatch(acct -> acct.getImpersonated().equalsIgnoreCase(account.getImpersonated()))) { - List accounts = new ArrayList<>(profile.getImpersonationAccounts()); - accounts.add(account); - profile.setImpersonationAccounts(accounts); + profile.add(account); } authProfileDAO.save(profile); @@ -64,9 +61,9 @@ public void create(final String owner, final ImpersonationAccount account) { @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") public void delete(final String owner, final String impersonated) { authProfileDAO.findByOwner(owner).ifPresent(profile -> { - List accounts = profile.getImpersonationAccounts(); - if (accounts.removeIf(acct -> acct.getImpersonated().equalsIgnoreCase(impersonated))) { - profile.setImpersonationAccounts(accounts); + if (profile.getImpersonationAccounts(). + removeIf(acct -> acct.getImpersonated().equalsIgnoreCase(impersonated))) { + authProfileDAO.save(profile); } }); diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/MfaTrusStorageLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/MfaTrusStorageLogic.java index 78a8280d25c..dd98da8f776 100644 --- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/MfaTrusStorageLogic.java +++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/MfaTrusStorageLogic.java @@ -87,7 +87,7 @@ public Page search( map(AuthProfile::getMfaTrustedDevices).filter(Objects::nonNull).flatMap(List::stream) : authProfileDAO.findByOwner(principal). map(AuthProfile::getMfaTrustedDevices).filter(Objects::nonNull).map(List::stream). - orElseGet(Stream::empty)). + orElseGet(Stream::empty)). filter(device -> { EqualsBuilder builder = new EqualsBuilder(); builder.appendSuper(device.getExpirationDate().isAfter(ZonedDateTime.now())); @@ -123,28 +123,23 @@ public Page search( @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") public void create(final String owner, final MfaTrustedDevice device) { AuthProfile profile = authProfile(owner); - - List devices = profile.getMfaTrustedDevices(); - devices.add(device); - profile.setMfaTrustedDevices(devices); + profile.add(device); authProfileDAO.save(profile); } @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") public void delete(final OffsetDateTime expirationDate, final String recordKey) { authProfileDAO.findAll(Pageable.unpaged(Sort.by("owner"))).forEach(profile -> { - List devices = profile.getMfaTrustedDevices(); - if (devices != null) { - if (recordKey != null) { - devices.removeIf(device -> recordKey.equals(device.getRecordKey())); - } else if (expirationDate != null) { - devices.removeIf(device -> device.getExpirationDate().isBefore(expirationDate.toZonedDateTime())); - } else { - devices = List.of(); - } - profile.setMfaTrustedDevices(devices); - authProfileDAO.save(profile); + if (recordKey != null) { + profile.getMfaTrustedDevices(). + removeIf(device -> recordKey.equals(device.getRecordKey())); + } else if (expirationDate != null) { + profile.getMfaTrustedDevices(). + removeIf(device -> device.getExpirationDate().isBefore(expirationDate.toZonedDateTime())); + } else { + profile.getMfaTrustedDevices().clear(); } + authProfileDAO.save(profile); }); } } diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogic.java index fe6863e69fd..77456e2bc23 100644 --- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogic.java +++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogic.java @@ -21,7 +21,6 @@ import java.util.List; import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.common.lib.wa.WebAuthnAccount; -import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential; import org.apache.syncope.core.logic.AbstractAuthProfileLogic; import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO; import org.apache.syncope.core.persistence.api.dao.NotFoundException; @@ -54,8 +53,7 @@ public List list() { @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") @Transactional(readOnly = true) public WebAuthnAccount read(final String owner) { - return authProfileDAO.findByOwner(owner).stream(). - findFirst(). + return authProfileDAO.findByOwner(owner). map(profile -> new WebAuthnAccount.Builder(). credentials(profile.getWebAuthnDeviceCredentials()).build()). orElseThrow(() -> new NotFoundException("Could not find account for Owner " + owner)); @@ -64,36 +62,38 @@ public WebAuthnAccount read(final String owner) { @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") public void delete(final String owner) { authProfileDAO.findByOwner(owner).ifPresent(profile -> { - profile.setWebAuthnDeviceCredentials(List.of()); + profile.getWebAuthnDeviceCredentials().clear(); authProfileDAO.save(profile); }); } @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") public void delete(final String owner, final String credentialId) { - authProfileDAO.findByOwner(owner).stream().findFirst(). - ifPresent(profile -> { - List credentials = profile.getWebAuthnDeviceCredentials(); - if (credentials.removeIf(acct -> acct.getIdentifier().equals(credentialId))) { - profile.setWebAuthnDeviceCredentials(credentials); - authProfileDAO.save(profile); - } - }); + authProfileDAO.findByOwner(owner).ifPresent(profile -> { + if (profile.getWebAuthnDeviceCredentials().removeIf(acct -> acct.getIdentifier().equals(credentialId))) { + authProfileDAO.save(profile); + } + }); } @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") public void create(final String owner, final WebAuthnAccount account) { AuthProfile profile = authProfile(owner); - profile.setWebAuthnDeviceCredentials(account.getCredentials()); + profile.getWebAuthnDeviceCredentials().clear(); + account.getCredentials().forEach(profile::add); + authProfileDAO.save(profile); } @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") public void update(final String owner, final WebAuthnAccount account) { - authProfileDAO.findByOwner(owner).ifPresent(profile -> { - profile.setWebAuthnDeviceCredentials(account.getCredentials()); - authProfileDAO.save(profile); - }); + AuthProfile profile = authProfileDAO.findByOwner(owner). + orElseThrow(() -> new NotFoundException("Could not find account for Owner " + owner)); + + profile.getWebAuthnDeviceCredentials().clear(); + account.getCredentials().forEach(profile::add); + + authProfileDAO.save(profile); } } diff --git a/core/am/pom.xml b/core/am/pom.xml index 7c188c941b7..2556edc1cf0 100644 --- a/core/am/pom.xml +++ b/core/am/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core AM diff --git a/core/am/rest-cxf/pom.xml b/core/am/rest-cxf/pom.xml index bbd68e590b3..06a54fa30d2 100644 --- a/core/am/rest-cxf/pom.xml +++ b/core/am/rest-cxf/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.core syncope-core-am - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core AM REST CXF diff --git a/core/idm/logic/pom.xml b/core/idm/logic/pom.xml index 2bfecadc81c..2ee1cdc6673 100644 --- a/core/idm/logic/pom.xml +++ b/core/idm/logic/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.core syncope-core-idm - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core IdM Logic @@ -39,7 +39,7 @@ under the License. - com.fasterxml.jackson.dataformat + tools.jackson.dataformat jackson-dataformat-csv @@ -87,6 +87,11 @@ under the License. embedded-postgres test + + org.slf4j + jcl-over-slf4j + test + org.slf4j slf4j-simple @@ -142,13 +147,30 @@ under the License. + + org.apache.maven.plugins + maven-dependency-plugin + true + + + set-bundles + process-test-resources + + copy + + + + + org.apache.maven.plugins maven-surefire-plugin + ${project.build.directory}/test-classes file:${bundles.directory}/ true + slf4j diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java index c08e606f764..2ebca8fc6cc 100644 --- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java +++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java @@ -88,7 +88,7 @@ protected void securityChecks(final Set effectiveRealms, final String re protected ConnInstance doSave(final ConnInstance connInstance) { ConnInstance merged = connInstanceDAO.save(connInstance); - merged.getResources().forEach(resource -> { + resourceDAO.findByConnInstance(merged.getKey()).forEach(resource -> { try { connectorManager.registerConnector(resource); } catch (NotFoundException e) { @@ -138,10 +138,11 @@ public ConnInstanceTO delete(final String key) { connInstance.getAdminRealm().getFullPath()); securityChecks(effectiveRealms, connInstance.getAdminRealm().getFullPath(), connInstance.getKey()); - if (!connInstance.getResources().isEmpty()) { + List resources = resourceDAO.findByConnInstance(connInstance.getKey()); + if (!resources.isEmpty()) { SyncopeClientException associatedResources = SyncopeClientException.build( ClientExceptionType.AssociatedResources); - connInstance.getResources().forEach(resource -> associatedResources.getElements().add(resource.getKey())); + resources.forEach(resource -> associatedResources.getElements().add(resource.getKey())); throw associatedResources; } diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java index 24f0760e41d..ac1b977f30a 100644 --- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java +++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.logic; -import com.fasterxml.jackson.dataformat.csv.CsvSchema; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Method; @@ -106,6 +105,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Transactional; +import tools.jackson.dataformat.csv.CsvSchema; public class ReconciliationLogic extends AbstractLogic { diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java index 6285867dfb2..04bb0acf608 100644 --- a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java +++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java @@ -23,9 +23,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.fasterxml.jackson.databind.MappingIterator; -import com.fasterxml.jackson.dataformat.csv.CsvMapper; -import com.fasterxml.jackson.dataformat.csv.CsvSchema; import java.io.IOException; import java.io.InputStream; import java.io.PipedInputStream; @@ -45,6 +42,9 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.transaction.annotation.Transactional; +import tools.jackson.databind.MappingIterator; +import tools.jackson.dataformat.csv.CsvMapper; +import tools.jackson.dataformat.csv.CsvSchema; @Transactional public class ReconciliationLogicTest extends AbstractTest { diff --git a/core/idm/pom.xml b/core/idm/pom.xml index a34fb442779..af5738b9742 100644 --- a/core/idm/pom.xml +++ b/core/idm/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core IdM diff --git a/core/idm/rest-cxf/pom.xml b/core/idm/rest-cxf/pom.xml index 81b8107011a..481e2a299b8 100644 --- a/core/idm/rest-cxf/pom.xml +++ b/core/idm/rest-cxf/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.core syncope-core-idm - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core IdM REST CXF diff --git a/core/idrepo/logic/pom.xml b/core/idrepo/logic/pom.xml index d3e5a38b7fb..2591dbb2c78 100644 --- a/core/idrepo/logic/pom.xml +++ b/core/idrepo/logic/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.core syncope-core-idrepo - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core IdRepo Logic @@ -154,6 +154,7 @@ under the License. ${project.build.directory}/test-classes file:${bundles.directory}/ true + slf4j diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyTypeClassLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyTypeClassLogic.java index 03e7f3e3027..b82b481c7e6 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyTypeClassLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyTypeClassLogic.java @@ -78,10 +78,7 @@ public AnyTypeClassTO update(final AnyTypeClassTO anyTypeClassTO) { AnyTypeClass anyTypeClass = anyTypeClassDAO.findById(anyTypeClassTO.getKey()). orElseThrow(() -> new NotFoundException("AnyTypeClass " + anyTypeClassTO.getKey())); - binder.update(anyTypeClass, anyTypeClassTO); - anyTypeClass = anyTypeClassDAO.save(anyTypeClass); - - return binder.getAnyTypeClassTO(anyTypeClass); + return binder.getAnyTypeClassTO(anyTypeClassDAO.save(binder.update(anyTypeClass, anyTypeClassTO))); } @PreAuthorize("hasRole('" + IdRepoEntitlement.ANYTYPECLASS_DELETE + "')") diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java index 1acd1039ff8..a9daef303fd 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java @@ -299,8 +299,7 @@ public GroupTO unlink(final String key, final Collection resources) { resources(resources.stream(). map(r -> new StringPatchItem.Builder().operation(PatchOperation.DELETE).value(r).build()). toList()). - udynMembershipCond(groupTO.getUDynMembershipCond()). - adynMembershipConds(groupTO.getADynMembershipConds()). + dynMembershipConds(groupTO.getDynMembershipConds()). build(); return binder.getGroupTO(provisioningManager.unlink(req, AuthContextUtils.getUsername(), REST_CONTEXT)); @@ -315,8 +314,7 @@ public GroupTO link(final String key, final Collection resources) { resources(resources.stream(). map(r -> new StringPatchItem.Builder().operation(PatchOperation.ADD_REPLACE).value(r).build()). toList()). - udynMembershipCond(groupTO.getUDynMembershipCond()). - adynMembershipConds(groupTO.getADynMembershipConds()). + dynMembershipConds(groupTO.getDynMembershipConds()). build(); return binder.getGroupTO(provisioningManager.link(req, AuthContextUtils.getUsername(), REST_CONTEXT)); @@ -333,8 +331,7 @@ public ProvisioningResult unassign( resources(resources.stream(). map(r -> new StringPatchItem.Builder().operation(PatchOperation.DELETE).value(r).build()). toList()). - udynMembershipCond(groupTO.getUDynMembershipCond()). - adynMembershipConds(groupTO.getADynMembershipConds()). + dynMembershipConds(groupTO.getDynMembershipConds()). build(); return update(req, nullPriorityAsync); @@ -355,8 +352,7 @@ public ProvisioningResult assign( resources(resources.stream(). map(r -> new StringPatchItem.Builder().operation(PatchOperation.ADD_REPLACE).value(r).build()). toList()). - udynMembershipCond(groupTO.getUDynMembershipCond()). - adynMembershipConds(groupTO.getADynMembershipConds()). + dynMembershipConds(groupTO.getDynMembershipConds()). build(); return update(req, nullPriorityAsync); diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java index 25673963738..c5b9b217240 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java @@ -504,8 +504,7 @@ public UserLogic userLogic( final UserDataBinder binder, final UserProvisioningManager provisioningManager, final SyncopeLogic syncopeLogic, - final RuleProvider ruleProvider, - final EncryptorManager encryptorManager) { + final RuleProvider ruleProvider) { return new UserLogic( realmSearchDAO, @@ -521,7 +520,6 @@ public UserLogic userLogic( binder, provisioningManager, syncopeLogic, - ruleProvider, - encryptorManager); + ruleProvider); } } diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RelationshipTypeLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RelationshipTypeLogic.java index 9a27fcf6298..aa62395788e 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RelationshipTypeLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RelationshipTypeLogic.java @@ -83,8 +83,7 @@ public RelationshipTypeTO update(final RelationshipTypeTO relationshipTypeTO) { RelationshipType relationshipType = relationshipTypeDAO.findById(relationshipTypeTO.getKey()). orElseThrow(() -> new NotFoundException("RelationshipType " + relationshipTypeTO.getKey())); - binder.update(relationshipType, relationshipTypeTO); - relationshipType = relationshipTypeDAO.save(relationshipType); + relationshipType = relationshipTypeDAO.save(binder.update(relationshipType, relationshipTypeTO)); publisher.publishEvent( new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, relationshipType, AuthContextUtils.getDomain())); diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java index 87a9fecdac1..cf98405c2ac 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java @@ -48,7 +48,6 @@ import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.common.lib.types.PatchOperation; import org.apache.syncope.common.rest.api.beans.ComplianceQuery; -import org.apache.syncope.core.persistence.api.EncryptorManager; import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; @@ -115,8 +114,6 @@ public record Self(UserTO user, String entitlements, String delegations) { protected final RuleProvider ruleEnforcer; - protected final EncryptorManager encryptorManager; - public UserLogic( final RealmSearchDAO realmSearchDAO, final AnyTypeDAO anyTypeDAO, @@ -131,8 +128,7 @@ public UserLogic( final UserDataBinder binder, final UserProvisioningManager provisioningManager, final SyncopeLogic syncopeLogic, - final RuleProvider ruleEnforcer, - final EncryptorManager encryptorManager) { + final RuleProvider ruleEnforcer) { super(realmSearchDAO, anyTypeDAO, templateUtils); @@ -147,7 +143,6 @@ public UserLogic( this.provisioningManager = provisioningManager; this.syncopeLogic = syncopeLogic; this.ruleEnforcer = ruleEnforcer; - this.encryptorManager = encryptorManager; } @PreAuthorize("isAuthenticated() and not(hasRole('" + IdRepoEntitlement.MUST_CHANGE_PASSWORD + "'))") @@ -251,7 +246,7 @@ public ProvisioningResult selfUpdate(final UserUR userUR, final boolean List authStatuses = List.of(confParamOps.get(AuthContextUtils.getDomain(), "authentication.statuses", new String[] {}, String[].class)); if (!authStatuses.contains(updated.getEntity().getStatus())) { - accessTokenDAO.findByOwner(updated.getEntity().getUsername()).ifPresent(accessTokenDAO::delete); + accessTokenDAO.deleteByOwner(updated.getEntity().getUsername()); } return updated; @@ -383,7 +378,7 @@ public ProvisioningResult mustChangePassword( build(); ProvisioningResult result = selfUpdate(userUR, nullPriorityAsync); - accessTokenDAO.findByOwner(result.getEntity().getUsername()).ifPresent(accessTokenDAO::delete); + accessTokenDAO.deleteByOwner(result.getEntity().getUsername()); return result; } @@ -440,29 +435,26 @@ public void compliance(final ComplianceQuery query) { } @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") - @Transactional public void requestPasswordReset(final String username, final String securityAnswer) { - User user = userDAO.findByUsername(username). + String key = userDAO.findKey(username). orElseThrow(() -> new NotFoundException("User " + username)); if (syncopeLogic.isPwdResetRequiringSecurityQuestions() - && (securityAnswer == null || !encryptorManager.getInstance(). - verify(securityAnswer, user.getCipherAlgorithm(), user.getSecurityAnswer()))) { + && (securityAnswer == null || !provisioningManager.checkSecurityAnswer(key, securityAnswer))) { throw SyncopeClientException.build(ClientExceptionType.InvalidSecurityAnswer); } - provisioningManager.requestPasswordReset(user.getKey(), AuthContextUtils.getUsername(), REST_CONTEXT); + provisioningManager.requestPasswordReset(key, AuthContextUtils.getUsername(), REST_CONTEXT); } @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") - @Transactional public void confirmPasswordReset(final String token, final String password) { - User user = userDAO.findByToken(token). + String key = userDAO.findByToken(token). orElseThrow(() -> new NotFoundException("User with token " + token)); provisioningManager.confirmPasswordReset( - user.getKey(), token, password, AuthContextUtils.getUsername(), REST_CONTEXT); + key, token, password, AuthContextUtils.getUsername(), REST_CONTEXT); } @PreAuthorize("isAuthenticated() " @@ -640,14 +632,12 @@ public ProvisioningResult provision( } @PreAuthorize("hasRole('" + IdRepoEntitlement.USER_SEARCH + "')") - @Transactional(readOnly = true) public void verifySecurityAnswer(final String username, final String securityAnswer) { - User user = userDAO.findByUsername(username). + String key = userDAO.findKey(username). orElseThrow(() -> new NotFoundException("User " + username)); if (syncopeLogic.isPwdResetRequiringSecurityQuestions() - && (securityAnswer == null || !encryptorManager.getInstance(). - verify(securityAnswer, user.getCipherAlgorithm(), user.getSecurityAnswer()))) { + && (securityAnswer == null || !provisioningManager.checkSecurityAnswer(key, securityAnswer))) { throw SyncopeClientException.build(ClientExceptionType.InvalidSecurityAnswer); } diff --git a/core/idrepo/pom.xml b/core/idrepo/pom.xml index ff2e760010c..1da1b5c69bc 100644 --- a/core/idrepo/pom.xml +++ b/core/idrepo/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core IdRepo diff --git a/core/idrepo/rest-cxf/pom.xml b/core/idrepo/rest-cxf/pom.xml index ceb08e94864..1e2d86e3043 100644 --- a/core/idrepo/rest-cxf/pom.xml +++ b/core/idrepo/rest-cxf/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.core syncope-core-idrepo - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core IdRepo REST CXF @@ -50,7 +50,7 @@ under the License. - com.fasterxml.jackson.jakarta.rs + tools.jackson.jakarta.rs jackson-jakarta-rs-json-provider diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java index 884b1a77361..34f20645270 100644 --- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java +++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.rest.cxf; -import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import io.swagger.v3.oas.models.security.SecurityScheme; import jakarta.validation.Validator; import java.util.HashMap; @@ -135,6 +134,7 @@ import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.core.task.AsyncTaskExecutor; +import tools.jackson.jakarta.rs.json.JacksonJsonProvider; @PropertySource("classpath:errorMessages.properties") @EnableConfigurationProperties(RESTProperties.class) diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java index 6eb45bc7bad..0a7077989f9 100644 --- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java +++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java @@ -64,15 +64,15 @@ @Provider public class RestServiceExceptionMapper implements ExceptionMapper { - private static final Logger LOG = LoggerFactory.getLogger(RestServiceExceptionMapper.class); + protected static final Logger LOG = LoggerFactory.getLogger(RestServiceExceptionMapper.class); - private final ValidationExceptionMapper validationEM = new ValidationExceptionMapper(); + protected final ValidationExceptionMapper validationEM = new ValidationExceptionMapper(); - private static final String UNIQUE_MSG_KEY = "UniqueConstraintViolation"; + protected static final String UNIQUE_MSG_KEY = "UniqueConstraintViolation"; - private static final Map EXCEPTION_CODE_MAP = new HashMap<>() { + protected static final Map EXCEPTION_CODE_MAP = new HashMap<>() { - private static final long serialVersionUID = -7688359318035249200L; + protected static final long serialVersionUID = -7688359318035249200L; { put("23000", UNIQUE_MSG_KEY); @@ -86,6 +86,26 @@ public RestServiceExceptionMapper(final Environment env) { this.env = env; } + protected String getPersistenceErrorMessage(final Throwable ex) { + Throwable throwable = ExceptionUtils.getRootCause(ex); + + String message = null; + if (throwable instanceof SQLException sqlException) { + String messageKey = EXCEPTION_CODE_MAP.get(sqlException.getSQLState()); + if (messageKey != null) { + message = env.getProperty("errMessage." + messageKey); + } + } else if (isExtityExists(ex)) { + + message = env.getProperty("errMessage." + UNIQUE_MSG_KEY); + } + + return Optional.ofNullable(message). + orElseGet(() -> Optional.ofNullable(ex.getCause()). + map(Throwable::getMessage). + orElseGet(ex::getMessage)); + } + @Override public Response toResponse(final Exception ex) { if (ex instanceof NotFoundException) { @@ -111,10 +131,7 @@ public Response toResponse(final Exception ex) { || ExceptionUtils.getRootCause(ex) instanceof DelegatedAdministrationException) { builder = builder(ClientExceptionType.DelegatedAdministration, ExceptionUtils.getRootCauseMessage(ex)); - } else if (ex instanceof EntityExistsException || ex instanceof DuplicateException - || ((ex instanceof PersistenceException || ex instanceof DataIntegrityViolationException) - && (ex.getCause() instanceof EntityExistsException || ex.getMessage().contains("already exists")))) { - + } else if (isExtityExists(ex)) { builder = builder(ClientExceptionType.EntityExists, getPersistenceErrorMessage( ex instanceof PersistenceException || ex instanceof DataIntegrityViolationException ? ex.getCause() : ex)); @@ -162,7 +179,16 @@ public Response toResponse(final Exception ex) { return Optional.ofNullable(builder).map(ResponseBuilder::build).orElse(null); } - private static ResponseBuilder getSyncopeClientExceptionResponse(final SyncopeClientException ex) { + protected static boolean isExtityExists(final Throwable ex) { + return ex instanceof EntityExistsException + || ex instanceof DuplicateException + || ex.getCause() instanceof EntityExistsException + || ex.getMessage().contains("already exists") + || ex.getMessage().contains("UNIQUE") + || ex.getMessage().contains("Duplicate"); + } + + protected static ResponseBuilder getSyncopeClientExceptionResponse(final SyncopeClientException ex) { ResponseBuilder builder = Response.status(ex.getType().getResponseStatus()); builder.header(RESTHeaders.ERROR_CODE, ex.getType().name()); @@ -178,7 +204,7 @@ private static ResponseBuilder getSyncopeClientExceptionResponse(final SyncopeCl return builder.entity(error); } - private static ResponseBuilder getSyncopeClientCompositeExceptionResponse( + protected static ResponseBuilder getSyncopeClientCompositeExceptionResponse( final SyncopeClientCompositeException ex) { if (ex.getExceptions().size() == 1) { return getSyncopeClientExceptionResponse(ex.getExceptions().iterator().next()); @@ -204,7 +230,7 @@ private static ResponseBuilder getSyncopeClientCompositeExceptionResponse( return builder.entity(errors); } - private static ResponseBuilder processInvalidEntityExceptions(final Exception ex) { + protected static ResponseBuilder processInvalidEntityExceptions(final Exception ex) { InvalidEntityException iee = null; if (ex instanceof InvalidEntityException invalidEntityException) { @@ -252,7 +278,7 @@ private static ResponseBuilder processInvalidEntityExceptions(final Exception ex return null; } - private static ResponseBuilder processBadRequestExceptions(final Exception ex) { + protected static ResponseBuilder processBadRequestExceptions(final Exception ex) { // This exception might be raised by Flowable (if enabled) Class ibatisPersistenceException = null; try { @@ -286,7 +312,7 @@ private static ResponseBuilder processBadRequestExceptions(final Exception ex) { return null; } - private static ResponseBuilder builder(final ClientExceptionType hType, final String msg) { + protected static ResponseBuilder builder(final ClientExceptionType hType, final String msg) { ResponseBuilder builder = Response.status(hType.getResponseStatus()). header(RESTHeaders.ERROR_CODE, hType.name()). header(RESTHeaders.ERROR_INFO, hType.getInfoHeaderValue(msg)); @@ -306,7 +332,7 @@ private static ResponseBuilder builder(final ClientExceptionType hType, final St * @param response model to construct {@link ResponseBuilder} from * @return new {@link ResponseBuilder} instance initialized from given response */ - private static ResponseBuilder builder(final Response response) { + protected static ResponseBuilder builder(final Response response) { ResponseBuilder builder = JAXRSUtils.toResponseBuilder(response.getStatus()); builder.entity(response.getEntity()); response.getMetadata().forEach((key, value) -> { @@ -317,26 +343,4 @@ private static ResponseBuilder builder(final Response response) { return builder; } - - private String getPersistenceErrorMessage(final Throwable ex) { - Throwable throwable = ExceptionUtils.getRootCause(ex); - - String message = null; - if (throwable instanceof SQLException sqlException) { - String messageKey = EXCEPTION_CODE_MAP.get(sqlException.getSQLState()); - if (messageKey != null) { - message = env.getProperty("errMessage." + messageKey); - } - } else if (throwable instanceof EntityExistsException - || throwable instanceof DuplicateException - || ex.getMessage().contains("already exists")) { - - message = env.getProperty("errMessage." + UNIQUE_MSG_KEY); - } - - return Optional.ofNullable(message). - orElseGet(() -> Optional.ofNullable(ex.getCause()). - map(Throwable::getMessage). - orElseGet(ex::getMessage)); - } } diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemResponse.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemResponse.java index e1dfe705523..b8e19946897 100644 --- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemResponse.java +++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemResponse.java @@ -24,6 +24,7 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.ws.rs.core.HttpHeaders; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; @@ -193,6 +194,12 @@ public void sendRedirect(final String location) { setHeader(HttpHeaders.LOCATION, location); } + @Override + public void sendRedirect(final String location, final int sc, final boolean clearBuffer) throws IOException { + setStatus(sc); + setHeader(HttpHeaders.LOCATION, location); + } + @Override public void setStatus(final int sc) { this.status = sc; diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java index 7a970bddd36..d4fd78b5c3a 100644 --- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java +++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java @@ -62,5 +62,4 @@ public void update(final DynRealmTO roleTO) { public void delete(final String key) { logic.delete(key); } - } diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceTest.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceTest.java index cdfb89bc04c..eebbc66863c 100644 --- a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceTest.java +++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceTest.java @@ -28,8 +28,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Request; import jakarta.ws.rs.core.Response; @@ -80,6 +78,8 @@ import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.test.util.ReflectionTestUtils; +import tools.jackson.core.type.TypeReference; +import tools.jackson.jakarta.rs.json.JacksonJsonProvider; @SpringJUnitConfig(classes = { IdRepoRESTCXFTestContext.class }) public class AnyObjectServiceTest { diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/IdRepoRESTCXFTestContext.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/IdRepoRESTCXFTestContext.java index 361f0673dd3..14b6d44296d 100644 --- a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/IdRepoRESTCXFTestContext.java +++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/IdRepoRESTCXFTestContext.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.rest.cxf.service; -import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import org.apache.cxf.jaxrs.ext.search.SearchContextProvider; import org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor; import org.apache.cxf.transport.common.gzip.GZIPInInterceptor; @@ -32,6 +31,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; +import tools.jackson.jakarta.rs.json.JacksonJsonProvider; @Configuration(proxyBeanMethods = false) public class IdRepoRESTCXFTestContext { diff --git a/core/metrics-starter/pom.xml b/core/metrics-starter/pom.xml index a729f439225..0956b365524 100644 --- a/core/metrics-starter/pom.xml +++ b/core/metrics-starter/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core Spring Boot Metrics diff --git a/core/metrics-starter/src/main/java/org/apache/syncope/core/starter/JPAMetricsContext.java b/core/metrics-starter/src/main/java/org/apache/syncope/core/starter/JPAMetricsContext.java index 4e455b7e2f5..fc1c218cfdd 100644 --- a/core/metrics-starter/src/main/java/org/apache/syncope/core/starter/JPAMetricsContext.java +++ b/core/metrics-starter/src/main/java/org/apache/syncope/core/starter/JPAMetricsContext.java @@ -27,11 +27,11 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.boot.actuate.metrics.data.MetricsRepositoryMethodInvocationListener; -import org.springframework.boot.actuate.metrics.jdbc.DataSourcePoolMetrics; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration; +import org.springframework.boot.data.metrics.MetricsRepositoryMethodInvocationListener; +import org.springframework.boot.jdbc.autoconfigure.DataSourcePoolMetadataProvidersConfiguration; import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider; +import org.springframework.boot.jdbc.metrics.DataSourcePoolMetrics; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; diff --git a/core/metrics-starter/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsContext.java b/core/metrics-starter/src/main/java/org/springframework/boot/cache/autoconfigure/metrics/CacheMetricsContext.java similarity index 94% rename from core/metrics-starter/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsContext.java rename to core/metrics-starter/src/main/java/org/springframework/boot/cache/autoconfigure/metrics/CacheMetricsContext.java index da04a795a89..62f82b58736 100644 --- a/core/metrics-starter/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsContext.java +++ b/core/metrics-starter/src/main/java/org/springframework/boot/cache/autoconfigure/metrics/CacheMetricsContext.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.springframework.boot.actuate.autoconfigure.metrics.cache; +package org.springframework.boot.cache.autoconfigure.metrics; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; diff --git a/core/metrics-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/core/metrics-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 4ceb3ba1afd..ddaaa4a5ae3 100644 --- a/core/metrics-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/core/metrics-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -16,4 +16,4 @@ # under the License. org.apache.syncope.core.starter.MetricsContext org.apache.syncope.core.starter.JPAMetricsContext -org.springframework.boot.actuate.autoconfigure.metrics.cache.CacheMetricsContext +org.springframework.boot.cache.autoconfigure.metrics.CacheMetricsContext diff --git a/core/persistence-api/pom.xml b/core/persistence-api/pom.xml index 42b23aaf4ab..168766d1fd5 100644 --- a/core/persistence-api/pom.xml +++ b/core/persistence-api/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core Persistence API @@ -43,6 +43,10 @@ under the License. jakarta.validation-api + + org.springframework + spring-tx + org.springframework.data spring-data-commons diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/InvalidEntityException.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/InvalidEntityException.java index 66822d8b448..e3f76105eaa 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/InvalidEntityException.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/InvalidEntityException.java @@ -56,7 +56,7 @@ public InvalidEntityException( this.entityClassSimpleName = entityClass.getSimpleName(); - entityViolationType.setMessage(Optional.ofNullable(message).map(String::trim).orElse(StringUtils.EMPTY)); + entityViolationType.message(Optional.ofNullable(message).map(String::trim).orElse(StringUtils.EMPTY)); this.violations.put(entityClass, EnumSet.noneOf(EntityViolationType.class)); this.violations.get(entityClass).add(entityViolationType); @@ -86,9 +86,10 @@ public InvalidEntityException( } catch (IllegalArgumentException e) { entityViolationType = EntityViolationType.Standard; } - entityViolationType.setMessage(message); - entityViolationType.setPropertyPath(violation.getPropertyPath().toString()); - entityViolationType.setInvalidValue(violation.getInvalidValue()); + entityViolationType. + message(message). + propertyPath(violation.getPropertyPath().toString()). + invalidValue(violation.getInvalidValue()); if (!this.violations.containsKey(violation.getLeafBean().getClass())) { this.violations.put(violation.getLeafBean().getClass(), EnumSet.noneOf(EntityViolationType.class)); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyChecker.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyChecker.java new file mode 100644 index 00000000000..5f58cf23e21 --- /dev/null +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyChecker.java @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.api.dao; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.AnyUtils; +import org.apache.syncope.core.persistence.api.entity.Attributable; +import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.apache.syncope.core.persistence.api.entity.Groupable; +import org.apache.syncope.core.persistence.api.entity.Membership; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.Relatable; +import org.apache.syncope.core.persistence.api.entity.Relationship; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +import org.apache.syncope.core.persistence.api.entity.Schema; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.transaction.annotation.Transactional; + +public class AnyChecker { + + protected static final Logger LOG = LoggerFactory.getLogger(AnyChecker.class); + + protected final PlainSchemaDAO plainSchemaDAO; + + public AnyChecker(final PlainSchemaDAO plainSchemaDAO) { + this.plainSchemaDAO = plainSchemaDAO; + } + + @Transactional(readOnly = true) + @SuppressWarnings("unchecked") + public AllowedSchemas findAllowedSchemas(final Any any, final Class reference) { + AllowedSchemas result = new AllowedSchemas<>(); + + // schemas given by type and aux classes + Set typeOwnClasses = new HashSet<>(); + typeOwnClasses.addAll(any.getType().getClasses()); + typeOwnClasses.addAll(any.getAuxClasses()); + + typeOwnClasses.forEach(typeClass -> { + if (reference.equals(PlainSchema.class)) { + result.self().addAll((Collection) typeClass.getPlainSchemas()); + } else if (reference.equals(DerSchema.class)) { + result.self().addAll((Collection) typeClass.getDerSchemas()); + } + }); + + // schemas given by group type extensions + Map> gTypeExtensionClasses = new HashMap<>(); + switch (any) { + case User user -> + user.getMemberships().forEach(memb -> memb.getRightEnd().getTypeExtensions(). + forEach(typeExt -> gTypeExtensionClasses.put(memb.getRightEnd(), typeExt.getAuxClasses()))); + case AnyObject anyObject -> + anyObject.getMemberships().forEach(memb -> memb.getRightEnd().getTypeExtensions().stream(). + filter(typeExt -> any.getType().equals(typeExt.getAnyType())). + forEach(typeExt -> gTypeExtensionClasses.put(memb.getRightEnd(), typeExt.getAuxClasses()))); + default -> { + } + } + gTypeExtensionClasses.entrySet().stream().peek( + entry -> result.memberships().put(entry.getKey(), new HashSet<>())). + forEach(entry -> entry.getValue().forEach(typeClass -> { + if (reference.equals(PlainSchema.class)) { + result.memberships().get(entry.getKey()). + addAll((Collection) typeClass.getPlainSchemas()); + } else if (reference.equals(DerSchema.class)) { + result.memberships().get(entry.getKey()). + addAll((Collection) typeClass.getDerSchemas()); + } + })); + + // schemas given by relationship type extensions + Map> rTypeExtensionClasses = new HashMap<>(); + switch (any) { + case User user -> + user.getRelationships().stream().map(Relationship::getType).distinct(). + forEach(rt -> rt.getTypeExtensions(). + forEach(typeExt -> rTypeExtensionClasses.put(rt, typeExt.getAuxClasses()))); + case AnyObject anyObject -> + anyObject.getRelationships().stream().map(Relationship::getType).distinct(). + forEach(rt -> rt.getTypeExtensions(). + forEach(typeExt -> rTypeExtensionClasses.put(rt, typeExt.getAuxClasses()))); + default -> { + } + } + rTypeExtensionClasses.entrySet().stream().peek( + entry -> result.relationshipTypes().put(entry.getKey(), new HashSet<>())). + forEach(entry -> entry.getValue().forEach(typeClass -> { + if (reference.equals(PlainSchema.class)) { + result.relationshipTypes().get(entry.getKey()). + addAll((Collection) typeClass.getPlainSchemas()); + } else if (reference.equals(DerSchema.class)) { + result.relationshipTypes().get(entry.getKey()). + addAll((Collection) typeClass.getDerSchemas()); + } + })); + + return result; + } + + @Transactional(readOnly = true) + public void checkBeforeSave(final T attributable, final AnyUtils anyUtils) { + if (attributable instanceof Any any) { + AllowedSchemas allowed = findAllowedSchemas(any, PlainSchema.class); + + for (PlainAttr attr : any.getPlainAttrs()) { + String schema = Optional.ofNullable(attr).map(PlainAttr::getSchema).orElse(null); + if (schema != null && !allowed.selfContains(schema)) { + throw new InvalidEntityException( + anyUtils.anyClass(), + EntityViolationType.InvalidPlainAttr.propertyPath("plainAttrs"), + schema + " not allowed for this instance"); + } + } + if (any instanceof Groupable groupable) { + for (Membership membership : groupable.getMemberships()) { + for (PlainAttr attr : groupable.getPlainAttrs(membership)) { + String schema = Optional.ofNullable(attr).map(PlainAttr::getSchema).orElse(null); + if (schema != null && !allowed.membershipsContains(membership.getRightEnd(), schema)) { + throw new InvalidEntityException( + anyUtils.anyClass(), + EntityViolationType.InvalidPlainAttr.propertyPath("plainAttrs"), + schema + " not allowed for membership of group " + + membership.getRightEnd().getName()); + } + } + } + } + if (any instanceof Relatable relatable) { + for (Relationship relationship : relatable.getRelationships()) { + for (PlainAttr attr : relatable.getPlainAttrs(relationship)) { + String schema = Optional.ofNullable(attr).map(PlainAttr::getSchema).orElse(null); + if (schema != null && !allowed.relationshipTypesContains(relationship.getType(), schema)) { + throw new InvalidEntityException( + anyUtils.anyClass(), + EntityViolationType.InvalidPlainAttr.propertyPath("plainAttrs"), + schema + " not allowed for relationships of type " + + relationship.getType().getKey()); + } + } + } + } + } + + // check UNIQUE constraints + attributable.getPlainAttrs().stream(). + filter(attr -> attr.getUniqueValue() != null). + forEach(attr -> { + if (plainSchemaDAO.existsPlainAttrUniqueValue( + anyUtils, + attributable.getKey(), + plainSchemaDAO.findById(attr.getSchema()). + orElseThrow(() -> new NotFoundException("PlainSchema " + attr.getSchema())), + attr.getUniqueValue())) { + + throw new DuplicateException("Duplicate value found for " + + attr.getSchema() + "=" + attr.getUniqueValue().getValueAsString()); + } else { + LOG.debug("No duplicate value found for {}={}", + attr.getSchema(), attr.getUniqueValue().getValueAsString()); + } + }); + } +} diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java index 038c318c7d6..530c42d4b90 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java @@ -28,7 +28,6 @@ import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Relationship; -import org.apache.syncope.core.persistence.api.entity.Schema; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -69,11 +68,11 @@ default SearchCond getAllMatchingCond() { return SearchCond.of(idCond); } - AllowedSchemas findAllowedSchemas(A any, Class reference); - List findDynRealms(String key); Collection findAllResourceKeys(String key); void deleteRelationship(Relationship relationship); + + void evict(Class entityClass, String key); } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java index 4970ba7b8d5..506dbac975a 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java @@ -87,9 +87,7 @@ record DynMembershipInfo(Set before, Set after) { long countUDynMembers(Group group); - void clearADynMembers(Group group); - - void clearUDynMembers(Group group); + void clearDynMembers(Group group); /** * Evaluates all the dynamic group membership conditions against the given anyObject (invoked during save). diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmChecker.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmChecker.java new file mode 100644 index 00000000000..e22d94ec969 --- /dev/null +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmChecker.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.api.dao; + +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.transaction.annotation.Transactional; + +public class RealmChecker { + + protected static final Logger LOG = LoggerFactory.getLogger(RealmChecker.class); + + protected final PlainSchemaDAO plainSchemaDAO; + + public RealmChecker(final PlainSchemaDAO plainSchemaDAO) { + this.plainSchemaDAO = plainSchemaDAO; + } + + @Transactional(readOnly = true) + public void checkBeforeSave(final Realm realm) { + Set allowed = realm.getAnyTypeClasses().stream(). + flatMap(atc -> atc.getPlainSchemas().stream()).map(PlainSchema::getKey).collect(Collectors.toSet()); + + for (PlainAttr attr : realm.getPlainAttrs()) { + String schema = Optional.ofNullable(attr).map(PlainAttr::getSchema).orElse(null); + if (schema != null && !allowed.contains(schema)) { + throw new InvalidEntityException( + realm.getClass(), + EntityViolationType.InvalidPlainAttr.propertyPath("plainAttrs"), + schema + " not allowed for this instance"); + } + } + + // check UNIQUE constraints + realm.getPlainAttrs().stream(). + filter(attr -> attr.getUniqueValue() != null). + forEach(attr -> { + if (plainSchemaDAO.existsPlainAttrUniqueValue( + realm.getKey(), + plainSchemaDAO.findById(attr.getSchema()). + orElseThrow(() -> new NotFoundException("PlainSchema " + attr.getSchema())), + attr.getUniqueValue())) { + + throw new DuplicateException("Duplicate value found for " + + attr.getSchema() + "=" + attr.getUniqueValue().getValueAsString()); + } else { + LOG.debug("No duplicate value found for {}={}", + attr.getSchema(), attr.getUniqueValue().getValueAsString()); + } + }); + } +} diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java index 23bbbdc1d0f..26a2e7853b5 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java @@ -42,4 +42,6 @@ public interface RealmDAO extends DAO { List findByActionsContaining(Implementation logicActions); Page findAll(Pageable pageable); + + void evict(String key); } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskExecDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskExecDAO.java index 5594986999f..88246db30b7 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskExecDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskExecDAO.java @@ -44,7 +44,5 @@ List> findAll( OffsetDateTime after, Pageable pageable); - > void saveAndAdd(TaskType type, String taskKey, TaskExec execution); - void delete(TaskType type, String key); } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java index 540c72f80fb..cc7caeddea8 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java @@ -54,7 +54,7 @@ public interface UserDAO extends AnyDAO { Optional findByUsername(String username); - Optional findByToken(String token); + Optional findByToken(String token); List findBySecurityQuestion(SecurityQuestion securityQuestion); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstance.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstance.java index 69a51d5d57a..2e30c9caa9c 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstance.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstance.java @@ -58,16 +58,9 @@ public interface ConnInstance extends Entity { Set getCapabilities(); - boolean add(ExternalResource resource); - - List getResources(); - - void setConf(List conf); - List getConf(); void setConnRequestTimeout(Integer timeout); Integer getConnRequestTimeout(); - } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynGroupMembership.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynGroupMembership.java index 58c6cb96e23..170def52e5e 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynGroupMembership.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynGroupMembership.java @@ -20,7 +20,7 @@ import org.apache.syncope.core.persistence.api.entity.group.Group; -public interface DynGroupMembership extends DynMembership { +public interface DynGroupMembership extends DynMembership { Group getGroup(); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynMembership.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynMembership.java index 0334322808b..8331597a977 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynMembership.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynMembership.java @@ -18,7 +18,11 @@ */ package org.apache.syncope.core.persistence.api.entity; -public interface DynMembership extends Entity { +public interface DynMembership extends Entity { + + AnyType getAnyType(); + + void setAnyType(AnyType anyType); String getFIQLCond(); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealmMembership.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealmMembership.java index 90a5d0a41bd..182313496bb 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealmMembership.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealmMembership.java @@ -18,14 +18,9 @@ */ package org.apache.syncope.core.persistence.api.entity; -public interface DynRealmMembership extends DynMembership { +public interface DynRealmMembership extends DynMembership { DynRealm getDynRealm(); void setDynRealm(DynRealm dynRealm); - - AnyType getAnyType(); - - void setAnyType(AnyType anyType); - } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AuthProfile.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AuthProfile.java index bd922215110..f79e3ab8ea5 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AuthProfile.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AuthProfile.java @@ -32,23 +32,23 @@ public interface AuthProfile extends Entity { void setOwner(String owner); + boolean add(GoogleMfaAuthToken googleMfaAuthToken); + List getGoogleMfaAuthTokens(); - void setGoogleMfaAuthTokens(List tokens); + boolean add(GoogleMfaAuthAccount googleMfaAuthAccount); List getGoogleMfaAuthAccounts(); - void setGoogleMfaAuthAccounts(List accounts); + boolean add(MfaTrustedDevice mfaTrustedDevice); List getMfaTrustedDevices(); - void setMfaTrustedDevices(List records); + boolean add(WebAuthnDeviceCredential webAuthnDeviceCredential); List getWebAuthnDeviceCredentials(); - void setWebAuthnDeviceCredentials(List credentials); + boolean add(ImpersonationAccount impersonationAccount); List getImpersonationAccounts(); - - void setImpersonationAccounts(List accounts); } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/group/Group.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/group/Group.java index d2a9a79ef5c..b8254858d87 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/group/Group.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/group/Group.java @@ -21,9 +21,8 @@ import java.util.List; import java.util.Optional; import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; import org.apache.syncope.core.persistence.api.entity.Relatable; -import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; -import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; import org.apache.syncope.core.persistence.api.entity.user.User; public interface Group extends Relatable { @@ -40,15 +39,11 @@ public interface Group extends Relatable { void setUserOwner(User userOwner); - UDynGroupMembership getUDynMembership(); + boolean add(DynGroupMembership dynGroupMembership); - void setUDynMembership(UDynGroupMembership uDynMembership); + Optional getDynMembership(AnyType anyType); - boolean add(ADynGroupMembership dynGroupMembership); - - Optional getADynMembership(AnyType anyType); - - List getADynMemberships(); + List getDynMemberships(); boolean add(GroupTypeExtension typeExtension); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/ConfParam.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/ConfParam.java index be15aa063ea..889420c2ec3 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/ConfParam.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/ConfParam.java @@ -18,8 +18,8 @@ */ package org.apache.syncope.core.persistence.api.entity.keymaster; -import com.fasterxml.jackson.databind.JsonNode; import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity; +import tools.jackson.databind.JsonNode; public interface ConfParam extends ProvidedKeyEntity { diff --git a/core/persistence-common/pom.xml b/core/persistence-common/pom.xml index d396d628846..e531ae9ed84 100644 --- a/core/persistence-common/pom.xml +++ b/core/persistence-common/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core Persistence Common diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/CommonPersistenceContext.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/CommonPersistenceContext.java index df30a882b72..18e524347a0 100644 --- a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/CommonPersistenceContext.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/CommonPersistenceContext.java @@ -23,8 +23,10 @@ import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.RealmChecker; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtils; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; @@ -35,6 +37,7 @@ import org.apache.syncope.core.persistence.api.utils.RealmUtils; import org.apache.syncope.core.persistence.common.attrvalue.DefaultPlainAttrValidationManager; import org.apache.syncope.core.persistence.common.content.KeymasterConfParamLoader; +import org.apache.syncope.core.persistence.common.dao.AnyFinder; import org.apache.syncope.core.persistence.common.entity.DefaultAnyUtils; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -154,4 +157,16 @@ public KeymasterConfParamLoader keymasterConfParamLoader( return new KeymasterConfParamLoader(confParamOps, ctx); } + + @ConditionalOnMissingBean + @Bean + public AnyFinder anyFinder(final @Lazy PlainSchemaDAO plainSchemaDAO, final @Lazy AnySearchDAO anySearchDAO) { + return new AnyFinder(plainSchemaDAO, anySearchDAO); + } + + @ConditionalOnMissingBean + @Bean + public RealmChecker realmChecker(final @Lazy PlainSchemaDAO plainSchemaDAO) { + return new RealmChecker(plainSchemaDAO); + } } diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/BinaryValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/BinaryValidator.java index 5a1a4414eb9..05bb14193d4 100644 --- a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/BinaryValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/BinaryValidator.java @@ -18,19 +18,20 @@ */ package org.apache.syncope.core.persistence.common.attrvalue; -import com.fasterxml.jackson.databind.json.JsonMapper; import jakarta.ws.rs.core.MediaType; -import java.io.IOException; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.core.persistence.api.attrvalue.InvalidPlainAttrValueException; import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.tika.Tika; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.json.JsonMapper; public class BinaryValidator extends AbstractValidator { private static final long serialVersionUID = 1344152444666540361L; - private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + private static final JsonMapper MAPPER = new SyncopeJsonMapper(); private static final Tika TIKA = new Tika(); @@ -67,7 +68,7 @@ private static boolean isValidJSON(final String value) { try { MAPPER.readTree(value); return true; - } catch (IOException e) { + } catch (JacksonException e) { LOG.debug("Invalid JSON string: {}", value, e); return false; } diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/KeymasterConfParamLoader.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/KeymasterConfParamLoader.java index fc7d91fafff..1d1eb7040ee 100644 --- a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/KeymasterConfParamLoader.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/KeymasterConfParamLoader.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.core.persistence.common.content; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.io.InputStream; import java.util.Map; import java.util.Optional; @@ -31,6 +29,8 @@ import org.slf4j.LoggerFactory; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.transaction.annotation.Transactional; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; /** * Initialize Keymaster with default content if no data is present already. diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyCheck.java deleted file mode 100644 index 39ce7678c2d..00000000000 --- a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyCheck.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.common.validation; - -import jakarta.validation.Constraint; -import jakarta.validation.Payload; -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.TYPE }) -@Retention(RetentionPolicy.RUNTIME) -@Constraint(validatedBy = AnyValidator.class) -@Documented -public @interface AnyCheck { - - String message() default "{org.apache.syncope.core.persistence.validation.any}"; - - Class[] groups() default {}; - - Class[] payload() default {}; -} diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyValidator.java deleted file mode 100644 index 766f6fcca84..00000000000 --- a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyValidator.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.common.validation; - -import jakarta.validation.ConstraintValidatorContext; -import java.util.Optional; -import org.apache.syncope.common.lib.types.EntityViolationType; -import org.apache.syncope.core.persistence.api.ApplicationContextProvider; -import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; -import org.apache.syncope.core.persistence.api.entity.Any; -import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; -import org.apache.syncope.core.persistence.api.entity.Groupable; -import org.apache.syncope.core.persistence.api.entity.Membership; -import org.apache.syncope.core.persistence.api.entity.PlainAttr; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; -import org.apache.syncope.core.persistence.api.entity.Relatable; -import org.apache.syncope.core.persistence.api.entity.Relationship; - -public class AnyValidator extends AbstractValidator { - - @Override - public boolean isValid(final Any any, final ConstraintValidatorContext context) { - context.disableDefaultConstraintViolation(); - - AllowedSchemas allowedPlainSchemas = - ApplicationContextProvider.getApplicationContext().getBean(AnyUtilsFactory.class). - getInstance(any.getType().getKind()).dao().findAllowedSchemas(any, PlainSchema.class); - - for (PlainAttr attr : any.getPlainAttrs()) { - String plainSchema = Optional.ofNullable(attr).map(PlainAttr::getSchema).orElse(null); - if (plainSchema != null && !allowedPlainSchemas.selfContains(plainSchema)) { - context.buildConstraintViolationWithTemplate( - getTemplate(EntityViolationType.InvalidPlainAttr, - plainSchema + " not allowed for this instance")). - addPropertyNode("plainAttrs").addConstraintViolation(); - return false; - } - } - if (any instanceof Groupable groupable) { - for (Membership membership : groupable.getMemberships()) { - for (PlainAttr attr : groupable.getPlainAttrs(membership)) { - String plainSchema = Optional.ofNullable(attr).map(PlainAttr::getSchema).orElse(null); - if (plainSchema != null - && !allowedPlainSchemas.membershipsContains(membership.getRightEnd(), plainSchema)) { - - context.buildConstraintViolationWithTemplate( - getTemplate(EntityViolationType.InvalidPlainAttr, - plainSchema + " not allowed for membership of group " - + membership.getRightEnd().getName())). - addPropertyNode("plainAttrs").addConstraintViolation(); - return false; - } - } - } - } - if (any instanceof Relatable relatable) { - for (Relationship relationship : relatable.getRelationships()) { - for (PlainAttr attr : relatable.getPlainAttrs(relationship)) { - String plainSchema = Optional.ofNullable(attr).map(PlainAttr::getSchema).orElse(null); - if (plainSchema != null - && !allowedPlainSchemas.relationshipTypesContains(relationship.getType(), plainSchema)) { - - context.buildConstraintViolationWithTemplate( - getTemplate(EntityViolationType.InvalidPlainAttr, - plainSchema + " not allowed for relationships of type " - + relationship.getType().getKey())). - addPropertyNode("plainAttrs").addConstraintViolation(); - return false; - } - } - } - } - - return true; - } -} diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/GroupValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/GroupValidator.java index ca993f9406e..0c9827a08a5 100644 --- a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/GroupValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/GroupValidator.java @@ -24,8 +24,8 @@ import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.EntityViolationType; import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; import org.apache.syncope.core.persistence.api.entity.Entity; -import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; import org.apache.syncope.core.persistence.api.entity.group.Group; public class GroupValidator extends AbstractValidator { @@ -55,20 +55,20 @@ public boolean isValid(final Group group, final ConstraintValidatorContext conte if (isValid) { Set anyTypes = new HashSet<>(); - for (ADynGroupMembership memb : group.getADynMemberships()) { + for (DynGroupMembership memb : group.getDynMemberships()) { anyTypes.add(memb.getAnyType()); - if (memb.getAnyType().getKind() != AnyTypeKind.ANY_OBJECT) { + if (memb.getAnyType().getKind() == AnyTypeKind.GROUP) { isValid = false; context.buildConstraintViolationWithTemplate( getTemplate(EntityViolationType.InvalidADynMemberships, - "No user or group dynamic membership condition are allowed here")). - addPropertyNode("aDynMemberships").addConstraintViolation(); + "No group dynamic membership condition are allowed here")). + addPropertyNode("dynMemberships").addConstraintViolation(); } } - if (isValid && anyTypes.size() < group.getADynMemberships().size()) { + if (isValid && anyTypes.size() < group.getDynMemberships().size()) { context.buildConstraintViolationWithTemplate( getTemplate(EntityViolationType.InvalidADynMemberships, "Each dynamic membership condition requires a different " diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RealmValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RealmValidator.java index 59298282829..212d5df5609 100644 --- a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RealmValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RealmValidator.java @@ -19,14 +19,9 @@ package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.EntityViolationType; import org.apache.syncope.core.persistence.api.dao.RealmDAO; -import org.apache.syncope.core.persistence.api.entity.PlainAttr; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.Realm; public class RealmValidator extends AbstractValidator { @@ -63,20 +58,6 @@ public boolean isValid(final Realm realm, final ConstraintValidatorContext conte } } - Set allowedPlainSchemas = realm.getAnyTypeClasses().stream(). - flatMap(atc -> atc.getPlainSchemas().stream()).map(PlainSchema::getKey).collect(Collectors.toSet()); - for (PlainAttr attr : realm.getPlainAttrs()) { - String plainSchema = Optional.ofNullable(attr).map(PlainAttr::getSchema).orElse(null); - if (plainSchema != null && !allowedPlainSchemas.contains(plainSchema)) { - isValid = false; - - context.buildConstraintViolationWithTemplate( - getTemplate(EntityViolationType.InvalidPlainAttr, - plainSchema + " not allowed for this instance")). - addPropertyNode("plainAttrs").addConstraintViolation(); - } - } - return isValid; } } diff --git a/core/persistence-jpa/pom.xml b/core/persistence-jpa/pom.xml index ec102968bf8..2ed47a6d3ca 100644 --- a/core/persistence-jpa/pom.xml +++ b/core/persistence-jpa/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core Persistence JPA @@ -48,10 +48,9 @@ under the License. org.springframework.boot spring-boot-starter-data-jpa - - org.apache.openjpa - openjpa + org.hibernate.orm + hibernate-jcache @@ -61,6 +60,11 @@ under the License. + + com.github.ben-manes.caffeine + jcache + test + ${jdbcdriver.groupId} ${jdbcdriver.artifactId} @@ -125,17 +129,25 @@ under the License. - org.apache.openjpa - openjpa-maven-plugin - true + org.hibernate.orm + hibernate-maven-plugin - ${project.basedir}/src/main/resources/persistence-enhance.xml - org/apache/syncope/core/persistence/jpa/entity/**/*.class + + + ${project.build.outputDirectory}/org/apache/syncope/core/persistence/jpa/entity/ + + + + + org.apache.syncope.core + syncope-core-persistence-common + ${project.version} + + - enhancer - process-classes + enhance enhance @@ -150,6 +162,7 @@ under the License. ${syncope.connid.location} true + slf4j @@ -204,40 +217,6 @@ under the License. ojdbc11 - - - openjpa-maven-plugin - - - - sql - - true - - - - clean verify - - - - org.apache.openjpa - openjpa-maven-plugin - true - - org.postgresql.Driver - - - - process-classes - - ${action} - - - - - - - diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/ConnectorManagerRemoteCommitListener.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ConnectorManagerRemoteCommitListener.java similarity index 50% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/ConnectorManagerRemoteCommitListener.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ConnectorManagerRemoteCommitListener.java index 30b240f28c0..38582444fcf 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/ConnectorManagerRemoteCommitListener.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ConnectorManagerRemoteCommitListener.java @@ -16,33 +16,41 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.openjpa; +package org.apache.syncope.core.persistence.jpa; +import jakarta.persistence.EntityManagerFactory; import java.io.Serializable; -import java.util.Collection; -import java.util.List; -import org.apache.openjpa.event.RemoteCommitEvent; -import org.apache.openjpa.event.RemoteCommitListener; -import org.apache.openjpa.util.StringId; +import javax.cache.event.CacheEntryCreatedListener; +import javax.cache.event.CacheEntryEvent; +import javax.cache.event.CacheEntryListenerException; +import javax.cache.event.CacheEntryRemovedListener; +import javax.cache.event.CacheEntryUpdatedListener; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; -import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.jpa.entity.JPAConnInstance; import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource; import org.apache.syncope.core.provisioning.api.ConnectorManager; import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.hibernate.cache.spi.CacheImplementor; +import org.hibernate.internal.SessionFactoryImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Takes care of connectors' Spring beans (un)registration in case HA is set up and the actual change is performed by - * another node in the OpenJPA cluster. + * another node in the Hibernate cluster. */ -public class ConnectorManagerRemoteCommitListener implements RemoteCommitListener, Serializable { +public class ConnectorManagerRemoteCommitListener + implements CacheEntryCreatedListener, + CacheEntryUpdatedListener, + CacheEntryRemovedListener, + Serializable { private static final long serialVersionUID = 5260753255454140460L; protected static final Logger LOG = LoggerFactory.getLogger(ConnectorManagerRemoteCommitListener.class); + protected final EntityManagerFactory entityManagerFactory; + protected final ConnectorManager connectorManager; protected final ExternalResourceDAO resourceDAO; @@ -50,10 +58,12 @@ public class ConnectorManagerRemoteCommitListener implements RemoteCommitListene protected final String domain; public ConnectorManagerRemoteCommitListener( + final EntityManagerFactory entityManagerFactory, final ConnectorManager connectorManager, final ExternalResourceDAO resourceDAO, final String domain) { + this.entityManagerFactory = entityManagerFactory; this.connectorManager = connectorManager; this.resourceDAO = resourceDAO; this.domain = domain; @@ -72,20 +82,13 @@ protected void registerForExternalResource(final String resourceKey) { } protected void registerForConnInstance(final String connInstanceKey) { - AuthContextUtils.runAsAdmin(domain, () -> { - List resources = resourceDAO.findByConnInstance(connInstanceKey); - if (resources.isEmpty()) { - LOG.debug("No resources found for connInstance '{}', ignoring", connInstanceKey); + AuthContextUtils.runAsAdmin(domain, () -> resourceDAO.findByConnInstance(connInstanceKey).forEach(resource -> { + try { + connectorManager.registerConnector(resource); + } catch (Exception e) { + LOG.error("While registering connector {} for resource {}", connInstanceKey, resource, e); } - - resources.forEach(resource -> { - try { - connectorManager.registerConnector(resource); - } catch (Exception e) { - LOG.error("While registering connector {} for resource {}", connInstanceKey, resource, e); - } - }); - }); + })); } protected void unregister(final String resourceKey) { @@ -100,47 +103,55 @@ protected void unregister(final String resourceKey) { () -> LOG.debug("No resource found for '{}', ignoring", resourceKey))); } - @SuppressWarnings("unchecked") @Override - public void afterCommit(final RemoteCommitEvent event) { - if (event.getPayloadType() == RemoteCommitEvent.PAYLOAD_OIDS_WITH_ADDS) { - ((Collection) event.getPersistedObjectIds()).stream(). - filter(StringId.class::isInstance). - map(StringId.class::cast). - forEach(id -> { - if (JPAExternalResource.class.isAssignableFrom(id.getType())) { - registerForExternalResource(id.getId()); - } else if (JPAConnInstance.class.isAssignableFrom(id.getType())) { - registerForConnInstance(id.getId()); - } - }); + public void onCreated(final Iterable> events) + throws CacheEntryListenerException { + + CacheImplementor l1Cache = entityManagerFactory.unwrap(SessionFactoryImpl.class).getCache(); + + for (CacheEntryEvent event : events) { + String[] split = event.getKey().toString().split("#"); + if (split.length > 1) { + if (JPAExternalResource.class.getName().equals(split[0]) + && !l1Cache.contains(JPAExternalResource.class, split[1])) { + + registerForExternalResource(split[1]); + } else if (JPAConnInstance.class.getName().equals(split[0]) + && !l1Cache.contains(JPAConnInstance.class, split[1])) { + + registerForConnInstance(split[1]); + } + } } + } - if (event.getPayloadType() != RemoteCommitEvent.PAYLOAD_EXTENTS) { - ((Collection) event.getUpdatedObjectIds()).stream(). - filter(StringId.class::isInstance). - map(StringId.class::cast). - forEach(id -> { - if (JPAExternalResource.class.isAssignableFrom(id.getType())) { - registerForExternalResource(id.getId()); - } else if (JPAConnInstance.class.isAssignableFrom(id.getType())) { - registerForConnInstance(id.getId()); - } - }); - - ((Collection) event.getDeletedObjectIds()).stream(). - filter(StringId.class::isInstance). - map(StringId.class::cast). - forEach(id -> { - if (JPAExternalResource.class.isAssignableFrom(id.getType())) { - unregister(id.getId()); - } - }); + @Override + public void onUpdated(final Iterable> events) + throws CacheEntryListenerException { + + for (CacheEntryEvent event : events) { + String[] split = event.getKey().toString().split("#"); + if (split.length > 1) { + if (JPAExternalResource.class.getName().equals(split[0])) { + registerForExternalResource(split[1]); + } else if (JPAConnInstance.class.getName().equals(split[0])) { + registerForConnInstance(split[1]); + } + } } } @Override - public void close() { - // nothing to do + public void onRemoved(final Iterable> events) + throws CacheEntryListenerException { + + for (CacheEntryEvent event : events) { + String[] split = event.getKey().toString().split("#"); + if (split.length > 1) { + if (JPAExternalResource.class.getName().equals(split[0])) { + unregister(split[1]); + } + } + } } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java index 72108df484d..e9dbe5dac13 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java @@ -83,8 +83,7 @@ public void register(final JPADomain domain) { domainHolder().getDomains().put(domain.getKey(), initedDataSource); // DomainRoutingEntityManagerFactory#domain - beanFactory().getBean(DomainRoutingEntityManagerFactory.class). - domain(domain, initedDataSource, ctx.getEnvironment().getProperty("openjpaMetaDataFactory")); + beanFactory().getBean(DomainRoutingEntityManagerFactory.class).domain(domain, initedDataSource); // domainContentXML beanFactory().registerBeanDefinition(domain.getKey() + "ContentXML", diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MariaDBPersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MariaDBPersistenceContext.java index 4af10b53c39..5af3bf5e1f9 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MariaDBPersistenceContext.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MariaDBPersistenceContext.java @@ -20,6 +20,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; +import java.util.TimeZone; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; @@ -35,6 +36,9 @@ import org.apache.syncope.core.persistence.jpa.dao.repo.MariaDBPlainSchemaRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExt; import org.apache.syncope.core.persistence.jpa.entity.MariaDBEntityFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; @@ -46,6 +50,23 @@ prefix = PersistenceProperties.PREFIX, name = PersistenceProperties.DB_TYPE, havingValue = "MARIADB") public class MariaDBPersistenceContext { + /** + * This is required for correct handling of OffsetDateTime values. + */ + public static class GlobalTimezoneBeanFactoryPostProcessor implements BeanFactoryPostProcessor { + + @Override + public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + } + } + + @ConditionalOnMissingBean + @Bean + public GlobalTimezoneBeanFactoryPostProcessor globalTimezoneBeanFactoryPostProcessor() { + return new GlobalTimezoneBeanFactoryPostProcessor(); + } + @ConditionalOnMissingBean @Bean public EntityFactory entityFactory() { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MySQLPersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MySQLPersistenceContext.java index 2a3b61b3227..4cd7dad6f02 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MySQLPersistenceContext.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MySQLPersistenceContext.java @@ -20,6 +20,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; +import java.util.TimeZone; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; @@ -35,6 +36,9 @@ import org.apache.syncope.core.persistence.jpa.dao.repo.MySQLPlainSchemaRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExt; import org.apache.syncope.core.persistence.jpa.entity.MySQLEntityFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; @@ -46,6 +50,23 @@ prefix = PersistenceProperties.PREFIX, name = PersistenceProperties.DB_TYPE, havingValue = "MYSQL") public class MySQLPersistenceContext { + /** + * This is required for correct handling of OffsetDateTime values. + */ + public static class GlobalTimezoneBeanFactoryPostProcessor implements BeanFactoryPostProcessor { + + @Override + public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + } + } + + @ConditionalOnMissingBean + @Bean + public GlobalTimezoneBeanFactoryPostProcessor globalTimezoneBeanFactoryPostProcessor() { + return new GlobalTimezoneBeanFactoryPostProcessor(); + } + @ConditionalOnMissingBean @Bean public EntityFactory entityFactory() { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java index f4b537eef92..2dc528a749a 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java @@ -23,6 +23,7 @@ import jakarta.persistence.ValidationMode; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import javax.sql.DataSource; import org.apache.syncope.common.keymaster.client.api.DomainOps; import org.apache.syncope.common.keymaster.client.api.model.JPADomain; @@ -31,6 +32,7 @@ import org.apache.syncope.core.persistence.api.DomainRegistry; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; @@ -61,6 +63,7 @@ import org.apache.syncope.core.persistence.api.dao.PersistenceInfoDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +import org.apache.syncope.core.persistence.api.dao.RealmChecker; import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; @@ -113,8 +116,6 @@ import org.apache.syncope.core.persistence.jpa.dao.repo.AnyTypeRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.AnyTypeRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.AttrRepoRepo; -import org.apache.syncope.core.persistence.jpa.dao.repo.AttrRepoRepoExt; -import org.apache.syncope.core.persistence.jpa.dao.repo.AttrRepoRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.AuditConfRepo; import org.apache.syncope.core.persistence.jpa.dao.repo.AuthModuleRepo; import org.apache.syncope.core.persistence.jpa.dao.repo.AuthModuleRepoExt; @@ -188,14 +189,14 @@ import org.apache.syncope.core.persistence.jpa.dao.repo.UserRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.WAConfigRepo; import org.apache.syncope.core.persistence.jpa.entity.task.JPATaskUtilsFactory; +import org.apache.syncope.core.persistence.jpa.hibernate.DomainJCacheRegionFactory; +import org.apache.syncope.core.persistence.jpa.hibernate.Jackson3JsonFormatMapper; import org.apache.syncope.core.persistence.jpa.spring.CommonEntityManagerFactoryConf; import org.apache.syncope.core.persistence.jpa.spring.DomainRoutingEntityManagerFactory; import org.apache.syncope.core.persistence.jpa.spring.MultiJarAwarePersistenceUnitPostProcessor; import org.apache.syncope.core.persistence.jpa.spring.SyncopeJPARepository; import org.apache.syncope.core.provisioning.api.ConnectorManager; import org.apache.syncope.core.spring.security.SecurityProperties; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -220,8 +221,6 @@ @Configuration(proxyBeanMethods = false) public class PersistenceContext { - private static final Logger OPENJPA_LOG = LoggerFactory.getLogger("org.apache.openjpa"); - @ConditionalOnMissingBean @Bean public CommonEntityManagerFactoryConf commonEMFConf( @@ -235,25 +234,18 @@ public CommonEntityManagerFactoryConf commonEMFConf( commonEMFConf.setPersistenceUnitPostProcessors(new MultiJarAwarePersistenceUnitPostProcessor()); Map jpaPropertyMap = new HashMap<>(); - jpaPropertyMap.put("openjpa.Log", "slf4j"); - if (OPENJPA_LOG.isDebugEnabled()) { - jpaPropertyMap.put("openjpa.Log", "SQL=TRACE"); - jpaPropertyMap.put("openjpa.ConnectionFactoryProperties", - "PrintParameters=true, PrettyPrint=true, PrettyPrintLineLength=120"); - } - - jpaPropertyMap.put("openjpa.NontransactionalWrite", false); - - jpaPropertyMap.put("openjpa.jdbc.MappingDefaults", - "ForeignKeyDeleteAction=restrict, JoinForeignKeyDeleteAction=restrict," - + "FieldStrategies='" - + "java.util.Locale=org.apache.syncope.core.persistence.jpa.openjpa.LocaleValueHandler," - + "java.lang.Boolean=org.apache.syncope.core.persistence.jpa.openjpa.BooleanValueHandler'"); + jpaPropertyMap.put("jakarta.persistence.schema-generation.database.action", "create"); - jpaPropertyMap.put("openjpa.DataCache", "true"); - jpaPropertyMap.put("openjpa.QueryCache", "true"); + jpaPropertyMap.put("hibernate.cache.use_second_level_cache", "true"); + jpaPropertyMap.put("hibernate.cache.use_query_cache", "true"); + jpaPropertyMap.put("hibernate.cache.region.factory_class", "jcache"); + jpaPropertyMap.put("hibernate.javax.cache.provider", props.getCacheProvider()); + Optional.ofNullable(props.getCacheURI()). + ifPresent(cacheURI -> jpaPropertyMap.put("hibernate.javax.cache.uri", cacheURI)); + jpaPropertyMap.put("hibernate.cache.region.factory_class", DomainJCacheRegionFactory.class.getName()); - jpaPropertyMap.put("openjpa.RemoteCommitProvider", props.getRemoteCommitProvider()); + // until https://hibernate.atlassian.net/browse/HHH-19890 https://github.com/hibernate/hibernate-orm/pull/11357 + jpaPropertyMap.put("hibernate.type.json_format_mapper", Jackson3JsonFormatMapper.class.getName()); commonEMFConf.setJpaPropertyMap(jpaPropertyMap); @@ -371,12 +363,6 @@ protected Class getRepositoryBaseClass(final RepositoryMetadata metadata) { }; } - @ConditionalOnMissingBean - @Bean - public AnyFinder anyFinder(final @Lazy PlainSchemaDAO plainSchemaDAO, final @Lazy AnySearchDAO anySearchDAO) { - return new AnyFinder(plainSchemaDAO, anySearchDAO); - } - @ConditionalOnMissingBean @Bean public AccessTokenRepoExt accessTokenRepoExt(final EntityManager entityManager) { @@ -392,6 +378,12 @@ public AccessTokenDAO accessTokenDAO( return jpaRepositoryFactory.getRepository(AccessTokenRepo.class, accessTokenRepoExt); } + @ConditionalOnMissingBean + @Bean + public AnyChecker anyChecker(final @Lazy PlainSchemaDAO plainSchemaDAO) { + return new AnyChecker(plainSchemaDAO); + } + @ConditionalOnMissingBean @Bean public AnyMatchDAO anyMatchDAO( @@ -420,19 +412,19 @@ public AnyMatchDAO anyMatchDAO( public AnyObjectRepoExt anyObjectRepoExt( final AnyUtilsFactory anyUtilsFactory, final @Lazy DynRealmDAO dynRealmDAO, - final @Lazy PlainSchemaDAO plainSchemaDAO, final @Lazy UserDAO userDAO, final @Lazy GroupDAO groupDAO, final EntityManager entityManager, + final AnyChecker anyChecker, final AnyFinder anyFinder) { return new AnyObjectRepoExtImpl( anyUtilsFactory, dynRealmDAO, - plainSchemaDAO, userDAO, groupDAO, entityManager, + anyChecker, anyFinder); } @@ -523,17 +515,8 @@ public AuthModuleDAO authModuleDAO( @ConditionalOnMissingBean @Bean - public AttrRepoRepoExt attrRepoRepoExt(final EntityManager entityManager) { - return new AttrRepoRepoExtImpl(entityManager); - } - - @ConditionalOnMissingBean - @Bean - public AttrRepoDAO attrRepoDAO( - final JpaRepositoryFactory jpaRepositoryFactory, - final AttrRepoRepoExt attrRepoRepoExt) { - - return jpaRepositoryFactory.getRepository(AttrRepoRepo.class, attrRepoRepoExt); + public AttrRepoDAO attrRepoDAO(final JpaRepositoryFactory jpaRepositoryFactory) { + return jpaRepositoryFactory.getRepository(AttrRepoRepo.class); } @ConditionalOnMissingBean @@ -679,7 +662,6 @@ public GroupRepoExt groupRepoExt( final ApplicationEventPublisher publisher, final AnyUtilsFactory anyUtilsFactory, final @Lazy DynRealmDAO dynRealmDAO, - final @Lazy PlainSchemaDAO plainSchemaDAO, final @Lazy RealmDAO realmDAO, final AnyMatchDAO anyMatchDAO, final @Lazy UserDAO userDAO, @@ -687,13 +669,13 @@ public GroupRepoExt groupRepoExt( final AnySearchDAO anySearchDAO, final SearchCondVisitor searchCondVisitor, final EntityManager entityManager, + final AnyChecker anyChecker, final AnyFinder anyFinder) { return new GroupRepoExtImpl( anyUtilsFactory, publisher, dynRealmDAO, - plainSchemaDAO, realmDAO, anyMatchDAO, userDAO, @@ -701,6 +683,7 @@ public GroupRepoExt groupRepoExt( anySearchDAO, searchCondVisitor, entityManager, + anyChecker, anyFinder); } @@ -826,9 +809,10 @@ public RealmDAO realmDAO( final RealmSearchDAO realmSearchDAO, final PlainSchemaDAO plainSchemaDAO, final ApplicationEventPublisher publisher, - final EntityManager entityManager) { + final EntityManager entityManager, + final RealmChecker realmChecker) { - return new JPARealmDAO(roleDAO, realmSearchDAO, plainSchemaDAO, publisher, entityManager); + return new JPARealmDAO(roleDAO, realmSearchDAO, plainSchemaDAO, publisher, entityManager, realmChecker); } @ConditionalOnMissingBean @@ -1017,19 +1001,18 @@ public UserRepoExt userRepoExt( final SecurityProperties securityProperties, final AnyUtilsFactory anyUtilsFactory, final @Lazy DynRealmDAO dynRealmDAO, - final @Lazy PlainSchemaDAO plainSchemaDAO, final RoleDAO roleDAO, final AccessTokenDAO accessTokenDAO, final @Lazy GroupDAO groupDAO, final DelegationDAO delegationDAO, final FIQLQueryDAO fiqlQueryDAO, final EntityManager entityManager, + final AnyChecker anyChecker, final AnyFinder anyFinder) { return new UserRepoExtImpl( anyUtilsFactory, dynRealmDAO, - plainSchemaDAO, roleDAO, accessTokenDAO, groupDAO, @@ -1037,6 +1020,7 @@ public UserRepoExt userRepoExt( fiqlQueryDAO, securityProperties, entityManager, + anyChecker, anyFinder); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java index cbee91b013b..adc5c4e3c31 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java @@ -28,26 +28,26 @@ public class PersistenceProperties extends AbstractPersistenceProperties { + + public AccessPolicyConfConverter() { + super(AccessPolicyConf.class); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AttrListConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AttrListConverter.java new file mode 100644 index 00000000000..204b817a58c --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AttrListConverter.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import tools.jackson.core.type.TypeReference; + +@Converter +public class AttrListConverter implements AttributeConverter, String> { + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + @Override + public String convertToDatabaseColumn(final List attribute) { + return Optional.ofNullable(attribute).map(POJOHelper::serialize).orElse(null); + } + + @Override + public List convertToEntityAttribute(final String dbData) { + return Optional.ofNullable(dbData).map(data -> POJOHelper.deserialize(data, TYPEREF)).orElseGet(ArrayList::new); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AttrReleasePolicyConfConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AttrReleasePolicyConfConverter.java new file mode 100644 index 00000000000..426138147ee --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AttrReleasePolicyConfConverter.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import org.apache.syncope.common.lib.policy.AttrReleasePolicyConf; + +@Converter +public class AttrReleasePolicyConfConverter extends SerializableConverter { + + public AttrReleasePolicyConfConverter() { + super(AttrReleasePolicyConf.class); + } +} diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/anyobject/ADynGroupMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AttrRepoConfConverter.java similarity index 70% rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/anyobject/ADynGroupMembership.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AttrRepoConfConverter.java index 19723cf887a..47fc0009615 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/anyobject/ADynGroupMembership.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AttrRepoConfConverter.java @@ -16,14 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.entity.anyobject; +package org.apache.syncope.core.persistence.jpa.converters; -import org.apache.syncope.core.persistence.api.entity.AnyType; -import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; +import jakarta.persistence.Converter; +import org.apache.syncope.common.lib.attr.AttrRepoConf; -public interface ADynGroupMembership extends DynGroupMembership { +@Converter +public class AttrRepoConfConverter extends SerializableConverter { - AnyType getAnyType(); - - void setAnyType(AnyType anyType); + public AttrRepoConfConverter() { + super(AttrRepoConf.class); + } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AuthModuleConfConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AuthModuleConfConverter.java new file mode 100644 index 00000000000..ccff2a3f012 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AuthModuleConfConverter.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import org.apache.syncope.common.lib.auth.AuthModuleConf; + +@Converter +public class AuthModuleConfConverter extends SerializableConverter { + + public AuthModuleConfConverter() { + super(AuthModuleConf.class); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AuthPolicyConfConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AuthPolicyConfConverter.java new file mode 100644 index 00000000000..9648ed9ca08 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/AuthPolicyConfConverter.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import org.apache.syncope.common.lib.policy.AuthPolicyConf; + +@Converter +public class AuthPolicyConfConverter extends SerializableConverter { + + public AuthPolicyConfConverter() { + super(AuthPolicyConf.class); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ConnConfPropertyListConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ConnConfPropertyListConverter.java new file mode 100644 index 00000000000..f71eaf691b4 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ConnConfPropertyListConverter.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import java.util.List; +import org.apache.syncope.common.lib.types.ConnConfProperty; +import tools.jackson.core.type.TypeReference; + +@Converter +public class ConnConfPropertyListConverter extends SerializableListConverter { + + protected static final TypeReference> TYPEREF = + new TypeReference>() { + }; + + @Override + protected TypeReference> typeRef() { + return TYPEREF; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ConnPoolConfConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ConnPoolConfConverter.java new file mode 100644 index 00000000000..e6e80fef396 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ConnPoolConfConverter.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import org.apache.syncope.common.lib.types.ConnPoolConf; + +@Converter +public class ConnPoolConfConverter extends SerializableConverter { + + public ConnPoolConfConverter() { + super(ConnPoolConf.class); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ConnectorCapabilitySetConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ConnectorCapabilitySetConverter.java new file mode 100644 index 00000000000..3aa2e663a76 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ConnectorCapabilitySetConverter.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import java.util.Set; +import org.apache.syncope.common.lib.types.ConnectorCapability; +import tools.jackson.core.type.TypeReference; + +@Converter +public class ConnectorCapabilitySetConverter extends SerializableSetConverter { + + protected static final TypeReference> TYPEREF = + new TypeReference>() { + }; + + @Override + protected TypeReference> typeRef() { + return TYPEREF; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AttrRepoRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/GoogleMfaAuthAccountListConverter.java similarity index 57% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AttrRepoRepoExtImpl.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/GoogleMfaAuthAccountListConverter.java index 556350f0b4b..eb729f1e110 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AttrRepoRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/GoogleMfaAuthAccountListConverter.java @@ -16,23 +16,22 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao.repo; +package org.apache.syncope.core.persistence.jpa.converters; -import jakarta.persistence.EntityManager; -import org.apache.syncope.core.persistence.api.entity.am.AttrRepo; -import org.apache.syncope.core.persistence.jpa.entity.am.JPAAttrRepo; +import jakarta.persistence.Converter; +import java.util.List; +import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount; +import tools.jackson.core.type.TypeReference; -public class AttrRepoRepoExtImpl implements AttrRepoRepoExt { +@Converter +public class GoogleMfaAuthAccountListConverter extends SerializableListConverter { - protected final EntityManager entityManager; - - public AttrRepoRepoExtImpl(final EntityManager entityManager) { - this.entityManager = entityManager; - } + protected static final TypeReference> TYPEREF = + new TypeReference>() { + }; @Override - public AttrRepo save(final AttrRepo attrRepo) { - ((JPAAttrRepo) attrRepo).list2json(); - return entityManager.merge(attrRepo); + protected TypeReference> typeRef() { + return TYPEREF; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/GoogleMfaAuthTokenListConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/GoogleMfaAuthTokenListConverter.java new file mode 100644 index 00000000000..3d10aeb5eb0 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/GoogleMfaAuthTokenListConverter.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import java.util.List; +import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken; +import tools.jackson.core.type.TypeReference; + +@Converter +public class GoogleMfaAuthTokenListConverter extends SerializableListConverter { + + protected static final TypeReference> TYPEREF = + new TypeReference>() { + }; + + @Override + protected TypeReference> typeRef() { + return TYPEREF; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ImpersonationAccountListConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ImpersonationAccountListConverter.java new file mode 100644 index 00000000000..c013e4f7a2f --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ImpersonationAccountListConverter.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import java.util.List; +import org.apache.syncope.common.lib.wa.ImpersonationAccount; +import tools.jackson.core.type.TypeReference; + +@Converter +public class ImpersonationAccountListConverter extends SerializableListConverter { + + protected static final TypeReference> TYPEREF = + new TypeReference>() { + }; + + @Override + protected TypeReference> typeRef() { + return TYPEREF; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ItemListConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ItemListConverter.java new file mode 100644 index 00000000000..cdffc18bc38 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ItemListConverter.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import java.util.List; +import org.apache.syncope.common.lib.to.Item; +import tools.jackson.core.type.TypeReference; + +@Converter +public class ItemListConverter extends SerializableListConverter { + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + @Override + protected TypeReference> typeRef() { + return TYPEREF; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/Locale2StringMapConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/Locale2StringMapConverter.java new file mode 100644 index 00000000000..78658783e44 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/Locale2StringMapConverter.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import java.util.HashMap; +import java.util.Locale; +import tools.jackson.core.type.TypeReference; + +@Converter +public class Locale2StringMapConverter extends SerializableMapConverter { + + protected static final TypeReference> TYPEREF = + new TypeReference>() { + }; + + @Override + protected TypeReference> typeRef() { + return TYPEREF; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/MfaTrustedDeviceListConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/MfaTrustedDeviceListConverter.java new file mode 100644 index 00000000000..eb4f336b906 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/MfaTrustedDeviceListConverter.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import java.util.List; +import org.apache.syncope.common.lib.wa.MfaTrustedDevice; +import tools.jackson.core.type.TypeReference; + +@Converter +public class MfaTrustedDeviceListConverter extends SerializableListConverter { + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + @Override + protected TypeReference> typeRef() { + return TYPEREF; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/OIDCGrantTypeSetConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/OIDCGrantTypeSetConverter.java new file mode 100644 index 00000000000..066026a431f --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/OIDCGrantTypeSetConverter.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import java.util.Set; +import org.apache.syncope.common.lib.types.OIDCGrantType; +import tools.jackson.core.type.TypeReference; + +@Converter +public class OIDCGrantTypeSetConverter extends SerializableSetConverter { + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + @Override + protected TypeReference> typeRef() { + return TYPEREF; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/OIDCResponseTypeSetConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/OIDCResponseTypeSetConverter.java new file mode 100644 index 00000000000..2469ea9914c --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/OIDCResponseTypeSetConverter.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import java.util.Set; +import org.apache.syncope.common.lib.types.OIDCResponseType; +import tools.jackson.core.type.TypeReference; + +@Converter +public class OIDCResponseTypeSetConverter extends SerializableSetConverter { + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + @Override + protected TypeReference> typeRef() { + return TYPEREF; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AttrRepoRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/OrgUnitConverter.java similarity index 73% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AttrRepoRepoExt.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/OrgUnitConverter.java index 7724b79c39c..ec4517c2953 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AttrRepoRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/OrgUnitConverter.java @@ -16,12 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao.repo; +package org.apache.syncope.core.persistence.jpa.converters; -import org.apache.syncope.core.persistence.api.entity.am.AttrRepo; +import jakarta.persistence.Converter; +import org.apache.syncope.common.lib.to.OrgUnit; -@FunctionalInterface -public interface AttrRepoRepoExt { +@Converter +public class OrgUnitConverter extends SerializableConverter { - AttrRepo save(AttrRepo attrRepo); + public OrgUnitConverter() { + super(OrgUnit.class); + } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/PasswordManagementConfConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/PasswordManagementConfConverter.java new file mode 100644 index 00000000000..c423e718650 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/PasswordManagementConfConverter.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import org.apache.syncope.common.lib.password.PasswordManagementConf; + +@Converter +public class PasswordManagementConfConverter extends SerializableConverter { + + public PasswordManagementConfConverter() { + super(PasswordManagementConf.class); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JSONEntityListener.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/PlainAttrListConverter.java similarity index 51% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JSONEntityListener.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/PlainAttrListConverter.java index e3b980405de..652d9954f32 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JSONEntityListener.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/PlainAttrListConverter.java @@ -16,33 +16,38 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.entity; +package org.apache.syncope.core.persistence.jpa.converters; -import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import java.util.ArrayList; import java.util.List; import java.util.Optional; -import org.apache.syncope.core.persistence.api.entity.Attributable; import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import tools.jackson.core.type.TypeReference; -public abstract class JSONEntityListener { +@Converter +public class PlainAttrListConverter implements AttributeConverter, String> { protected static final TypeReference> TYPEREF = new TypeReference>() { }; - protected List getAttrs(final String plainAttrsJSON) { - return POJOHelper.deserialize(plainAttrsJSON, TYPEREF); + @Override + public String convertToDatabaseColumn(final List attribute) { + return Optional.ofNullable(attribute).map(POJOHelper::serialize).orElse(null); } - protected void json2list(final AbstractAttributable entity, final boolean clearFirst) { - if (clearFirst) { - entity.getPlainAttrsList().clear(); - } - if (entity.getPlainAttrsJSON() != null) { - getAttrs(entity.getPlainAttrsJSON()).stream().filter(PlainAttr::isValid).peek(attr -> { - attr.getValues().forEach(value -> value.setAttr(attr)); - Optional.ofNullable(attr.getUniqueValue()).ifPresent(value -> value.setAttr(attr)); - }).forEach(attr -> entity.add(attr)); - } + @Override + public List convertToEntityAttribute(final String dbData) { + List plainAttrs = new ArrayList<>(); + + Optional.ofNullable(dbData).map(data -> POJOHelper.deserialize(data, TYPEREF)). + ifPresent(attrs -> attrs.stream().filter(PlainAttr::isValid).peek(attr -> { + attr.getValues().forEach(value -> value.setAttr(attr)); + Optional.ofNullable(attr.getUniqueValue()).ifPresent(value -> value.setAttr(attr)); + }).forEach(plainAttrs::add)); + + return plainAttrs; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ProvisionListConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ProvisionListConverter.java new file mode 100644 index 00000000000..d17f7097298 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/ProvisionListConverter.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import java.util.List; +import org.apache.syncope.common.lib.to.Provision; +import tools.jackson.core.type.TypeReference; + +@Converter +public class ProvisionListConverter extends SerializableListConverter { + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + @Override + protected TypeReference> typeRef() { + return TYPEREF; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDynMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/SerializableConverter.java similarity index 51% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDynMembership.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/SerializableConverter.java index 4dd4727c02b..7a727ab4fc4 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDynMembership.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/SerializableConverter.java @@ -16,29 +16,28 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.entity; +package org.apache.syncope.core.persistence.jpa.converters; -import jakarta.persistence.MappedSuperclass; -import jakarta.validation.constraints.NotNull; -import org.apache.syncope.core.persistence.api.entity.Any; -import org.apache.syncope.core.persistence.api.entity.DynMembership; +import jakarta.persistence.AttributeConverter; +import java.io.Serializable; +import java.util.Optional; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; -@MappedSuperclass -public abstract class AbstractDynMembership - extends AbstractGeneratedKeyEntity implements DynMembership { +abstract class SerializableConverter implements AttributeConverter { - private static final long serialVersionUID = 921821654690948787L; + protected final Class reference; - @NotNull - private String fiql; + protected SerializableConverter(final Class reference) { + this.reference = reference; + } @Override - public String getFIQLCond() { - return fiql; + public String convertToDatabaseColumn(final T attribute) { + return Optional.ofNullable(attribute).map(POJOHelper::serialize).orElse(null); } @Override - public void setFIQLCond(final String fiql) { - this.fiql = fiql; + public T convertToEntityAttribute(final String dbData) { + return Optional.ofNullable(dbData).map(c -> POJOHelper.deserialize(c, reference)).orElse(null); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/SerializableListConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/SerializableListConverter.java new file mode 100644 index 00000000000..66038b5ffa8 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/SerializableListConverter.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.AttributeConverter; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import tools.jackson.core.type.TypeReference; + +abstract class SerializableListConverter implements AttributeConverter, String> { + + protected abstract TypeReference> typeRef(); + + @Override + public String convertToDatabaseColumn(final List attribute) { + return Optional.ofNullable(attribute).map(POJOHelper::serialize).orElse(null); + } + + @Override + public List convertToEntityAttribute(final String dbData) { + return Optional.ofNullable(dbData). + map(data -> POJOHelper.deserialize(data, typeRef())).orElseGet(ArrayList::new); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/SerializableMapConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/SerializableMapConverter.java new file mode 100644 index 00000000000..d7a31b74866 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/SerializableMapConverter.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.AttributeConverter; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import tools.jackson.core.type.TypeReference; + +abstract class SerializableMapConverter + implements AttributeConverter, String> { + + protected abstract TypeReference> typeRef(); + + @Override + public String convertToDatabaseColumn(final Map attribute) { + return Optional.ofNullable(attribute).map(POJOHelper::serialize).orElse(null); + } + + @Override + public Map convertToEntityAttribute(final String dbData) { + return Optional.ofNullable(dbData). + map(data -> POJOHelper.deserialize(data, typeRef())).orElseGet(HashMap::new); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/SerializableSetConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/SerializableSetConverter.java new file mode 100644 index 00000000000..778660ba891 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/SerializableSetConverter.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.AttributeConverter; +import java.io.Serializable; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import tools.jackson.core.type.TypeReference; + +abstract class SerializableSetConverter implements AttributeConverter, String> { + + protected abstract TypeReference> typeRef(); + + @Override + public String convertToDatabaseColumn(final Set attribute) { + return Optional.ofNullable(attribute).map(POJOHelper::serialize).orElse(null); + } + + @Override + public Set convertToEntityAttribute(final String dbData) { + return Optional.ofNullable(dbData). + map(data -> POJOHelper.deserialize(data, typeRef())).orElseGet(HashSet::new); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/String2StringMapConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/String2StringMapConverter.java new file mode 100644 index 00000000000..79ce3f6b51d --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/String2StringMapConverter.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import java.util.HashMap; +import tools.jackson.core.type.TypeReference; + +@Converter +public class String2StringMapConverter extends SerializableMapConverter { + + protected static final TypeReference> TYPEREF = + new TypeReference>() { + }; + + @Override + protected TypeReference> typeRef() { + return TYPEREF; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/StringListConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/StringListConverter.java new file mode 100644 index 00000000000..976621841dc --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/StringListConverter.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import java.util.List; +import tools.jackson.core.type.TypeReference; + +@Converter +public class StringListConverter extends SerializableListConverter { + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + @Override + protected TypeReference> typeRef() { + return TYPEREF; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/StringSetConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/StringSetConverter.java new file mode 100644 index 00000000000..777fe2da2ad --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/StringSetConverter.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import java.util.Set; +import tools.jackson.core.type.TypeReference; + +@Converter +public class StringSetConverter extends SerializableSetConverter { + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + @Override + protected TypeReference> typeRef() { + return TYPEREF; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/TicketExpirationPolicyConfConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/TicketExpirationPolicyConfConverter.java new file mode 100644 index 00000000000..74c55b7623a --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/TicketExpirationPolicyConfConverter.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import org.apache.syncope.common.lib.policy.TicketExpirationPolicyConf; + +@Converter +public class TicketExpirationPolicyConfConverter extends SerializableConverter { + + public TicketExpirationPolicyConfConverter() { + super(TicketExpirationPolicyConf.class); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/UsernameAttributeProviderConfConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/UsernameAttributeProviderConfConverter.java new file mode 100644 index 00000000000..1cf0f4fe5f7 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/UsernameAttributeProviderConfConverter.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import org.apache.syncope.common.lib.clientapps.UsernameAttributeProviderConf; + +@Converter +public class UsernameAttributeProviderConfConverter extends SerializableConverter { + + public UsernameAttributeProviderConfConverter() { + super(UsernameAttributeProviderConf.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractDynMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/WebAuthnDeviceCredentialListConverter.java similarity index 57% rename from core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractDynMembership.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/WebAuthnDeviceCredentialListConverter.java index 300fd4ad6cb..ef89aa59488 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractDynMembership.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/WebAuthnDeviceCredentialListConverter.java @@ -16,27 +16,22 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.neo4j.entity; +package org.apache.syncope.core.persistence.jpa.converters; -import jakarta.validation.constraints.NotNull; -import org.apache.syncope.core.persistence.api.entity.Any; -import org.apache.syncope.core.persistence.api.entity.DynMembership; +import jakarta.persistence.Converter; +import java.util.List; +import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential; +import tools.jackson.core.type.TypeReference; -public abstract class AbstractDynMembership - extends AbstractGeneratedKeyNode implements DynMembership { +@Converter +public class WebAuthnDeviceCredentialListConverter extends SerializableListConverter { - private static final long serialVersionUID = 921821654690948787L; - - @NotNull - private String fiql; - - @Override - public String getFIQLCond() { - return fiql; - } + protected static final TypeReference> TYPEREF = + new TypeReference>() { + }; @Override - public void setFIQLCond(final String fiql) { - this.fiql = fiql; + protected TypeReference> typeRef() { + return TYPEREF; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/XmlSecAlgorithmListConverter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/XmlSecAlgorithmListConverter.java new file mode 100644 index 00000000000..96ba9412346 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/converters/XmlSecAlgorithmListConverter.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.converters; + +import jakarta.persistence.Converter; +import java.util.List; +import org.apache.syncope.common.lib.types.XmlSecAlgorithm; +import tools.jackson.core.type.TypeReference; + +@Converter +public class XmlSecAlgorithmListConverter extends SerializableListConverter { + + protected static final TypeReference> TYPEREF = + new TypeReference>() { + }; + + @Override + protected TypeReference> typeRef() { + return TYPEREF; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAAnySearchDAO.java index 8b700b82b64..1fb2a22689d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAAnySearchDAO.java @@ -33,9 +33,6 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.openjpa.jdbc.meta.MappingRepository; -import org.apache.openjpa.jdbc.sql.OracleDictionary; -import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.AnyTypeKind; @@ -71,6 +68,8 @@ import org.apache.syncope.core.persistence.api.utils.RealmUtils; import org.apache.syncope.core.persistence.common.dao.AbstractAnySearchDAO; import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.hibernate.dialect.OracleDialect; +import org.hibernate.internal.SessionFactoryImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -155,12 +154,8 @@ protected AbstractJPAAnySearchDAO( protected boolean isOracle() { return IS_ORACLE.computeIfAbsent( AuthContextUtils.getDomain(), - k -> { - OpenJPAEntityManagerFactorySPI emfspi = entityManagerFactory.unwrap( - OpenJPAEntityManagerFactorySPI.class); - return ((MappingRepository) emfspi.getConfiguration(). - getMetaDataRepositoryInstance()).getDBDictionary() instanceof OracleDictionary; - }); + k -> entityManagerFactory.unwrap(SessionFactoryImpl.class).getJdbcServices(). + getDialect() instanceof OracleDialect); } protected SearchSupport.SearchView defaultSV(final SearchSupport svs) { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditEventDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditEventDAO.java index 180338ea1b6..8226c7e033e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditEventDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditEventDAO.java @@ -51,7 +51,7 @@ protected int setParameter(final List parameters, final Object parameter protected AuditEventCriteriaBuilder entityKey(final String entityKey) { if (entityKey != null) { query.append(andIfNeeded()). - append("(before_value LIKE '%\"key\":\"").append(entityKey).append("\"%' OR "). + append("(beforeValue LIKE '%\"key\":\"").append(entityKey).append("\"%' OR "). append("inputs LIKE '%\"key\":\"").append(entityKey).append("\"%' OR "). append("output LIKE '%\"key\":\"").append(entityKey).append("\"%' OR "). append("throwable LIKE '%\"key\":\"").append(entityKey).append("\"%')"); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAEntityCacheDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAEntityCacheDAO.java index e95eda19c45..935f0e7f626 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAEntityCacheDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAEntityCacheDAO.java @@ -19,20 +19,14 @@ package org.apache.syncope.core.persistence.jpa.dao; import jakarta.persistence.EntityManagerFactory; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; +import java.util.HashMap; import java.util.Map; -import org.apache.openjpa.datacache.CacheStatistics; -import org.apache.openjpa.datacache.CacheStatisticsSPI; -import org.apache.openjpa.datacache.QueryKey; -import org.apache.openjpa.kernel.QueryStatistics; -import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory; -import org.apache.openjpa.persistence.QueryResultCacheImpl; import org.apache.syncope.core.persistence.api.dao.EntityCacheDAO; import org.apache.syncope.core.persistence.api.entity.Entity; -import org.apache.syncope.core.persistence.api.utils.FormatUtils; +import org.hibernate.Cache; +import org.hibernate.internal.SessionFactoryImpl; +import org.hibernate.stat.EntityStatistics; +import org.hibernate.stat.Statistics; public class JPAEntityCacheDAO implements EntityCacheDAO { @@ -42,109 +36,105 @@ public JPAEntityCacheDAO(final EntityManagerFactory entityManagerFactory) { this.entityManagerFactory = entityManagerFactory; } - protected CacheStatisticsSPI cacheStatisticsSPI() { - return (CacheStatisticsSPI) entityManagerFactory.unwrap(OpenJPAEntityManagerFactory.class). - getStoreCache().getStatistics(); - } - - protected QueryStatistics queryStatistics() { - return ((QueryResultCacheImpl) entityManagerFactory.unwrap(OpenJPAEntityManagerFactory.class). - getQueryResultCache()).getDelegate().getStatistics(); + protected Statistics statistics() { + return entityManagerFactory.unwrap(SessionFactoryImpl.class).getStatistics(); } @Override public Map getStatistics() { - Map result = new LinkedHashMap<>(); - - CacheStatistics cacheStats = cacheStatisticsSPI(); - - Map storeCache = new LinkedHashMap<>(); - result.put("storeCache", storeCache); - - storeCache.put("enabled", cacheStats.isEnabled()); - storeCache.put("activation", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format( - cacheStats.start().toInstant().atOffset(FormatUtils.DEFAULT_OFFSET))); - storeCache.put("last_update", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format( - cacheStats.since().toInstant().atOffset(FormatUtils.DEFAULT_OFFSET))); - storeCache.put("hits", cacheStats.getHitCount()); - storeCache.put("reads", cacheStats.getReadCount()); - storeCache.put("writes", cacheStats.getWriteCount()); - storeCache.put("total_hits", cacheStats.getTotalHitCount()); - storeCache.put("total_reads", cacheStats.getTotalReadCount()); - storeCache.put("total_writes", cacheStats.getTotalWriteCount()); - - List> storeCacheDetails = new ArrayList<>(); - storeCache.put("details", storeCacheDetails); - cacheStats.classNames().forEach(className -> { - Map classMap = new LinkedHashMap<>(); - classMap.put("region", className); - classMap.put("hits", cacheStats.getHitCount(className)); - classMap.put("reads", cacheStats.getReadCount(className)); - classMap.put("writes", cacheStats.getWriteCount(className)); - storeCache.put("total_hits", cacheStats.getTotalHitCount(className)); - storeCache.put("total_reads", cacheStats.getTotalReadCount(className)); - storeCache.put("total_writes", cacheStats.getTotalWriteCount(className)); - storeCacheDetails.add(classMap); - }); - - QueryStatistics queryStats = queryStatistics(); - - Map queryCache = new LinkedHashMap<>(); - result.put("queryCache", queryCache); - - queryCache.put("activation", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format( - queryStats.start().toInstant().atOffset(FormatUtils.DEFAULT_OFFSET))); - queryCache.put("last_update", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format( - queryStats.since().toInstant().atOffset(FormatUtils.DEFAULT_OFFSET))); - queryCache.put("hits", queryStats.getHitCount()); - queryCache.put("executions", queryStats.getExecutionCount()); - queryCache.put("evictions", queryStats.getEvictionCount()); - queryCache.put("total_hits", queryStats.getTotalHitCount()); - queryCache.put("total_executions", queryStats.getTotalExecutionCount()); - queryCache.put("total_evictions", queryStats.getTotalEvictionCount()); - - List> queryCacheDetails = new ArrayList<>(); - queryCache.put("details", queryCacheDetails); - - queryStats.keys().forEach(queryKey -> { - Map queryKeyMap = new LinkedHashMap<>(); - queryKeyMap.put("query_key", queryKey.toString()); - queryCache.put("hits", queryStats.getHitCount(queryKey)); - queryCache.put("executions", queryStats.getExecutionCount(queryKey)); - queryCache.put("total_hits", queryStats.getTotalHitCount(queryKey)); - queryCache.put("total_executions", queryStats.getTotalExecutionCount(queryKey)); - queryCacheDetails.add(queryKeyMap); - }); - - return result; + Statistics statistics = statistics(); + + Map general = new HashMap<>(); + general.put("start time", statistics.getStart()); + general.put("sessions opened", statistics.getSessionOpenCount()); + general.put("sessions closed", statistics.getSessionCloseCount()); + general.put("transactions", statistics.getTransactionCount()); + general.put("successful transactions", statistics.getSuccessfulTransactionCount()); + general.put("optimistic lock failures", statistics.getOptimisticFailureCount()); + general.put("flushes", statistics.getFlushCount()); + general.put("connections obtained", statistics.getConnectCount()); + general.put("statements prepared", statistics.getPrepareStatementCount()); + general.put("statements closed", statistics.getCloseStatementCount()); + general.put("second level cache puts", statistics.getSecondLevelCachePutCount()); + general.put("second level cache hits", statistics.getSecondLevelCacheHitCount()); + general.put("second level cache misses", statistics.getSecondLevelCacheMissCount()); + general.put("entities loaded", statistics.getEntityLoadCount()); + general.put("entities updated", statistics.getEntityUpdateCount()); + general.put("entities upserted", statistics.getEntityUpsertCount()); + general.put("entities inserted", statistics.getEntityInsertCount()); + general.put("entities deleted", statistics.getEntityDeleteCount()); + general.put("entities fetched", statistics.getEntityFetchCount()); + general.put("collections loaded", statistics.getCollectionLoadCount()); + general.put("collections updated", statistics.getCollectionUpdateCount()); + general.put("collections removed", statistics.getCollectionRemoveCount()); + general.put("collections recreated", statistics.getCollectionRecreateCount()); + general.put("collections fetched", statistics.getCollectionFetchCount()); + general.put("naturalId queries executed to database", statistics.getNaturalIdQueryExecutionCount()); + general.put("naturalId cache puts", statistics.getNaturalIdCachePutCount()); + general.put("naturalId cache hits", statistics.getNaturalIdCacheHitCount()); + general.put("naturalId cache misses", statistics.getNaturalIdCacheMissCount()); + general.put("naturalId max query time", statistics.getNaturalIdQueryExecutionMaxTime()); + general.put("queries executed to database", statistics.getQueryExecutionCount()); + general.put("query cache puts", statistics.getQueryCachePutCount()); + general.put("query cache hits", statistics.getQueryCacheHitCount()); + general.put("query cache misses", statistics.getQueryCacheMissCount()); + general.put("update timestamps cache puts", statistics.getUpdateTimestampsCachePutCount()); + general.put("update timestamps cache hits", statistics.getUpdateTimestampsCacheHitCount()); + general.put("update timestamps cache misses", statistics.getUpdateTimestampsCacheMissCount()); + general.put("max query time", statistics.getQueryExecutionMaxTime()); + general.put("query plan cache hits", statistics.getQueryPlanCacheHitCount()); + general.put("query plan cache misses", statistics.getQueryPlanCacheMissCount()); + + Map entities = new HashMap<>(); + for (String entityName : statistics.getEntityNames()) { + EntityStatistics es = statistics.getEntityStatistics(entityName); + + Map entity = new HashMap<>(); + entity.put("cache hits", es.getCacheHitCount()); + entity.put("cache misses", es.getCacheMissCount()); + entity.put("cache puts", es.getCachePutCount()); + entity.put("cache removes", es.getCacheRemoveCount()); + entity.put("deletes", es.getDeleteCount()); + entity.put("fetches", es.getFetchCount()); + entity.put("inserts", es.getInsertCount()); + entity.put("loads", es.getLoadCount()); + entity.put("updates", es.getUpdateCount()); + entity.put("optimistic lock failures", es.getOptimisticFailureCount()); + entities.put(entityName, entity); + } + + return Map.of("general", general, "entities", entities); } @Override public void enableStatistics() { - cacheStatisticsSPI().enable(); + statistics().setStatisticsEnabled(true); } @Override public void disableStatistics() { - cacheStatisticsSPI().disable(); + statistics().setStatisticsEnabled(false); } @Override public void resetStatistics() { - cacheStatisticsSPI().reset(); - queryStatistics().reset(); + statistics().clear(); + } + + protected Cache cache() { + return entityManagerFactory.unwrap(SessionFactoryImpl.class).getCache(); } @Override public void evict(final Class entityClass, final String key) { - entityManagerFactory.unwrap(OpenJPAEntityManagerFactory.class).getStoreCache().evict(entityClass, key); + cache().evict(entityClass, key); } @Override public void clearCache() { - OpenJPAEntityManagerFactory emf = entityManagerFactory.unwrap(OpenJPAEntityManagerFactory.class); + Cache cache = cache(); - emf.getStoreCache().evictAll(); - emf.getQueryResultCache().evictAll(); + cache.evictAll(); + cache.evictQueryRegions(); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPersistenceInfoDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPersistenceInfoDAO.java index 6d818de0168..6966ce52080 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPersistenceInfoDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPersistenceInfoDAO.java @@ -19,23 +19,12 @@ package org.apache.syncope.core.persistence.jpa.dao; import jakarta.persistence.EntityManagerFactory; -import java.lang.reflect.Field; -import java.net.InetAddress; -import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; -import org.apache.commons.lang3.tuple.Triple; -import org.apache.openjpa.conf.OpenJPAConfiguration; -import org.apache.openjpa.event.RemoteCommitEventManager; -import org.apache.openjpa.event.RemoteCommitProvider; -import org.apache.openjpa.event.TCPRemoteCommitProvider; -import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; import org.apache.syncope.core.persistence.api.dao.PersistenceInfoDAO; +import org.hibernate.Version; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; public class JPAPersistenceInfoDAO implements PersistenceInfoDAO { @@ -51,63 +40,9 @@ public JPAPersistenceInfoDAO(final EntityManagerFactory entityManagerFactory) { public Map info() { Map result = new LinkedHashMap<>(); - OpenJPAEntityManagerFactorySPI emfspi = entityManagerFactory.unwrap(OpenJPAEntityManagerFactorySPI.class); - OpenJPAConfiguration conf = emfspi.getConfiguration(); - - Map properties = emfspi.getProperties(); - result.put("vendor", properties.get("VendorName")); - result.put("version", properties.get("VersionNumber")); - result.put("platform", properties.get("Platform")); - - Map remoteCommitProvider = new LinkedHashMap<>(); - result.put("remoteCommitProvider", remoteCommitProvider); - - RemoteCommitEventManager rcem = conf.getRemoteCommitEventManager(); - - remoteCommitProvider.put("remoteEventsEnabled", rcem.areRemoteEventsEnabled()); - remoteCommitProvider.put("transmitPersistedObjectIds", rcem.getTransmitPersistedObjectIds()); - remoteCommitProvider.put("failFast", rcem.isFailFast()); - - RemoteCommitProvider rcp = rcem.getRemoteCommitProvider(); - List> addresses = new ArrayList<>(); - if (rcp instanceof TCPRemoteCommitProvider) { - try { - Field addressesField = ReflectionUtils.findField(TCPRemoteCommitProvider.class, "_addresses"); - addressesField.setAccessible(true); - - Class hostClass = ClassUtils.forName( - "org.apache.openjpa.event.TCPRemoteCommitProvider$HostAddress", - ClassUtils.getDefaultClassLoader()); - Field addressField = ReflectionUtils.findField(hostClass, "_address"); - addressField.setAccessible(true); - Field portField = ReflectionUtils.findField(hostClass, "_port"); - portField.setAccessible(true); - Field isAvailableField = ReflectionUtils.findField(hostClass, "_isAvailable"); - isAvailableField.setAccessible(true); - - @SuppressWarnings("unchecked") - List hosts = (List) ReflectionUtils.getField(addressesField, rcp); - hosts.forEach(host -> { - InetAddress address = (InetAddress) ReflectionUtils.getField(addressField, host); - Integer port = (Integer) ReflectionUtils.getField(portField, host); - Boolean isAvailable = (Boolean) ReflectionUtils.getField(isAvailableField, host); - - addresses.add(Triple.of(address.getHostAddress(), port, isAvailable)); - }); - } catch (Exception e) { - LOG.error("Could not fetch information about TCPRemoteCommitProvider", e); - } - } - - remoteCommitProvider.put( - "addresses", - addresses.stream().map(address -> { - Map map = new LinkedHashMap<>(); - map.put("ip", address.getLeft()); - map.put("port", address.getMiddle()); - map.put("available", address.getRight()); - return map; - }).toList()); + result.put("vendor", Version.class.getPackage().getImplementationVendor()); + result.put("version", Version.class.getPackage().getImplementationVersion()); + result.put("title", Version.class.getPackage().getImplementationTitle()); return result; } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java index 968ab75eec1..8228f71fdce 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java @@ -27,9 +27,8 @@ import java.util.Optional; import org.apache.commons.lang3.Strings; import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.core.persistence.api.dao.DuplicateException; -import org.apache.syncope.core.persistence.api.dao.NotFoundException; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.RealmChecker; import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.RoleDAO; @@ -71,18 +70,22 @@ public class JPARealmDAO implements RealmDAO { protected final EntityManager entityManager; + protected final RealmChecker realmChecker; + public JPARealmDAO( final RoleDAO roleDAO, final RealmSearchDAO realmSearchDAO, final PlainSchemaDAO plainSchemaDAO, final ApplicationEventPublisher publisher, - final EntityManager entityManager) { + final EntityManager entityManager, + final RealmChecker realmChecker) { this.roleDAO = roleDAO; this.realmSearchDAO = realmSearchDAO; this.plainSchemaDAO = plainSchemaDAO; this.publisher = publisher; this.entityManager = entityManager; + this.realmChecker = realmChecker; } @Override @@ -210,23 +213,7 @@ public Page findAll(final Pageable pageable) { @Override public S save(final S realm) { - // check UNIQUE constraints - new ArrayList<>(((JPARealm) realm).getPlainAttrsList()).stream(). - filter(attr -> attr.getUniqueValue() != null). - forEach(attr -> { - if (plainSchemaDAO.existsPlainAttrUniqueValue( - realm.getKey(), - plainSchemaDAO.findById(attr.getSchema()). - orElseThrow(() -> new NotFoundException("PlainSchema " + attr.getSchema())), - attr.getUniqueValue())) { - - throw new DuplicateException("Duplicate value found for " - + attr.getSchema() + "=" + attr.getUniqueValue().getValueAsString()); - } else { - LOG.debug("No duplicate value found for {}={}", - attr.getSchema(), attr.getUniqueValue().getValueAsString()); - } - }); + realmChecker.checkBeforeSave(realm); String fullPathBefore = realm.getFullPath(); String fullPathAfter = realm.getParent() == null @@ -238,9 +225,6 @@ public S save(final S realm) { S merged = entityManager.merge(realm); - // ensure that entity listeners are invoked at this point - entityManager.flush(); - if (!fullPathAfter.equals(fullPathBefore)) { realmSearchDAO.findChildren(realm).forEach(this::save); } @@ -273,4 +257,9 @@ public void delete(final Realm realm) { new EntityLifecycleEvent<>(this, SyncDeltaType.DELETE, toBeDeleted, AuthContextUtils.getDomain())); }); } + + @Override + public void evict(final String key) { + Optional.ofNullable(entityManager.find(JPARealm.class, key)).ifPresent(entityManager::detach); + } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java index b5d656666aa..21c90912cf4 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java @@ -55,7 +55,6 @@ import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; import org.apache.syncope.core.persistence.jpa.entity.task.JPAMacroTask; import org.apache.syncope.core.persistence.jpa.entity.task.JPAMacroTaskCommand; -import org.apache.syncope.core.persistence.jpa.entity.task.JPANotificationTask; import org.apache.syncope.core.persistence.jpa.entity.task.JPAPropagationTask; import org.apache.syncope.core.persistence.jpa.entity.task.JPAPropagationTaskExec; import org.apache.syncope.core.persistence.jpa.entity.task.JPAPullTask; @@ -132,6 +131,9 @@ public Optional findByName(final TaskType type, final S @Override public Optional> findById(final String key) { Optional> task = findById(TaskType.SCHEDULED, key); + if (task.isEmpty()) { + task = findById(TaskType.LIVE_SYNC, key); + } if (task.isEmpty()) { task = findById(TaskType.PULL, key); } @@ -504,14 +506,6 @@ public long count( @Transactional(rollbackFor = { Throwable.class }) @Override public > T save(final T task) { - switch (task) { - case JPANotificationTask jpaNotificationTask -> - jpaNotificationTask.list2json(); - case JPAPushTask jpaPushTask -> - jpaPushTask.map2json(); - default -> { - } - } return entityManager.merge(task); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java index b4e0b1c9364..e4d1d35b537 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java @@ -242,18 +242,6 @@ public > S save(final S execution) { return entityManager.merge(execution); } - @Transactional(rollbackFor = { Throwable.class }) - @Override - public > void saveAndAdd( - final TaskType taskType, final String taskKey, final TaskExec execution) { - - Optional task = taskDAO.findById(taskType, taskKey); - if (task.isPresent()) { - task.get().add(execution); - taskDAO.save(task.get()); - } - } - @Override public void delete(final TaskType taskType, final String key) { findById(taskType, key).ifPresent(this::delete); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java index ff01f4f04ff..86f580c7a80 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java @@ -93,8 +93,7 @@ public SearchView membership() { } public SearchView dyngroupmembership() { - return new SearchView("sv" + anyTypeKind.name() + "dgm", - anyTypeKind == AnyTypeKind.USER ? GroupRepoExt.UDYNMEMB_TABLE : GroupRepoExt.ADYNMEMB_TABLE); + return new SearchView("sv" + anyTypeKind.name() + "dgm", GroupRepoExt.DYNMEMB_TABLE); } public SearchView role() { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java index c97c3d1de4c..77c8fc43c2d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java @@ -19,45 +19,27 @@ package org.apache.syncope.core.persistence.jpa.dao.repo; import jakarta.persistence.EntityManager; -import jakarta.persistence.Query; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.time.OffsetDateTime; import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.Set; -import javax.sql.DataSource; -import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; -import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; -import org.apache.syncope.core.persistence.api.dao.DuplicateException; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.NotFoundException; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.entity.Any; -import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; import org.apache.syncope.core.persistence.api.entity.AnyUtils; -import org.apache.syncope.core.persistence.api.entity.DerSchema; -import org.apache.syncope.core.persistence.api.entity.DynRealm; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.Relationship; -import org.apache.syncope.core.persistence.api.entity.RelationshipType; -import org.apache.syncope.core.persistence.api.entity.Schema; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; -import org.apache.syncope.core.persistence.api.entity.group.Group; -import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.common.dao.AnyFinder; -import org.apache.syncope.core.persistence.jpa.entity.AbstractAttributable; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject; import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser; -import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.hibernate.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.transaction.annotation.Propagation; +import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.transaction.annotation.Transactional; public abstract class AbstractAnyRepoExt implements AnyRepoExt { @@ -66,10 +48,10 @@ public abstract class AbstractAnyRepoExt implements AnyRepoExt protected final DynRealmDAO dynRealmDAO; - protected final PlainSchemaDAO plainSchemaDAO; - protected final EntityManager entityManager; + protected final AnyChecker anyChecker; + protected final AnyFinder anyFinder; protected final AnyUtils anyUtils; @@ -78,14 +60,14 @@ public abstract class AbstractAnyRepoExt implements AnyRepoExt protected AbstractAnyRepoExt( final DynRealmDAO dynRealmDAO, - final PlainSchemaDAO plainSchemaDAO, final EntityManager entityManager, + final AnyChecker anyChecker, final AnyFinder anyFinder, final AnyUtils anyUtils) { this.dynRealmDAO = dynRealmDAO; - this.plainSchemaDAO = plainSchemaDAO; this.entityManager = entityManager; + this.anyChecker = anyChecker; this.anyFinder = anyFinder; this.anyUtils = anyUtils; switch (anyUtils.anyTypeKind()) { @@ -103,12 +85,26 @@ protected AbstractAnyRepoExt( } } + protected T query(final String sql, final ResultSetExtractor rse, final String... parameters) { + return entityManager.unwrap(Session.class).doReturningWork(conn -> { + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + if (parameters != null) { + for (int i = 0; i < parameters.length; i++) { + stmt.setString(i + 1, parameters[i]); + } + } + + try (ResultSet rs = stmt.executeQuery()) { + return rse.extractData(rs); + } + } + }); + } + @Transactional(readOnly = true) @Override public Optional findLastChange(final String key) { - OpenJPAEntityManagerFactorySPI emf = entityManager.getEntityManagerFactory(). - unwrap(OpenJPAEntityManagerFactorySPI.class); - return new JdbcTemplate((DataSource) emf.getConfiguration().getConnectionFactory()).query( + return query( "SELECT creationDate, lastChangeDate FROM " + table + " WHERE id=?", rs -> { if (rs.next()) { @@ -148,94 +144,19 @@ public List findByDerAttrValue(final String expression, final String value, f return anyFinder.findByDerAttrValue(anyUtils.anyTypeKind(), expression, value, ignoreCaseMatch); } - @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) - @Override - @SuppressWarnings("unchecked") - public AllowedSchemas findAllowedSchemas(final A any, final Class reference) { - AllowedSchemas result = new AllowedSchemas<>(); - - // schemas given by type and aux classes - Set typeOwnClasses = new HashSet<>(); - typeOwnClasses.addAll(any.getType().getClasses()); - typeOwnClasses.addAll(any.getAuxClasses()); - - typeOwnClasses.forEach(typeClass -> { - if (reference.equals(PlainSchema.class)) { - result.self().addAll((Collection) typeClass.getPlainSchemas()); - } else if (reference.equals(DerSchema.class)) { - result.self().addAll((Collection) typeClass.getDerSchemas()); - } - }); - - // schemas given by group type extensions - Map> gTypeExtensionClasses = new HashMap<>(); - switch (any) { - case User user -> - user.getMemberships().forEach(memb -> memb.getRightEnd().getTypeExtensions(). - forEach(typeExt -> gTypeExtensionClasses.put(memb.getRightEnd(), typeExt.getAuxClasses()))); - case AnyObject anyObject -> - anyObject.getMemberships().forEach(memb -> memb.getRightEnd().getTypeExtensions().stream(). - filter(typeExt -> any.getType().equals(typeExt.getAnyType())). - forEach(typeExt -> gTypeExtensionClasses.put(memb.getRightEnd(), typeExt.getAuxClasses()))); - default -> { - } - } - gTypeExtensionClasses.entrySet().stream().peek( - entry -> result.memberships().put(entry.getKey(), new HashSet<>())). - forEach(entry -> entry.getValue().forEach(typeClass -> { - if (reference.equals(PlainSchema.class)) { - result.memberships().get(entry.getKey()). - addAll((Collection) typeClass.getPlainSchemas()); - } else if (reference.equals(DerSchema.class)) { - result.memberships().get(entry.getKey()). - addAll((Collection) typeClass.getDerSchemas()); - } - })); - - // schemas given by relationship type extensions - Map> rTypeExtensionClasses = new HashMap<>(); - switch (any) { - case User user -> - user.getRelationships().stream().map(Relationship::getType).distinct(). - forEach(rt -> rt.getTypeExtensions(). - forEach(typeExt -> rTypeExtensionClasses.put(rt, typeExt.getAuxClasses()))); - case AnyObject anyObject -> - anyObject.getRelationships().stream().map(Relationship::getType).distinct(). - forEach(rt -> rt.getTypeExtensions(). - forEach(typeExt -> rTypeExtensionClasses.put(rt, typeExt.getAuxClasses()))); - default -> { - } - } - rTypeExtensionClasses.entrySet().stream().peek( - entry -> result.relationshipTypes().put(entry.getKey(), new HashSet<>())). - forEach(entry -> entry.getValue().forEach(typeClass -> { - if (reference.equals(PlainSchema.class)) { - result.relationshipTypes().get(entry.getKey()). - addAll((Collection) typeClass.getPlainSchemas()); - } else if (reference.equals(DerSchema.class)) { - result.relationshipTypes().get(entry.getKey()). - addAll((Collection) typeClass.getDerSchemas()); - } - })); - - return result; - } - @Transactional(readOnly = true) @Override public List findDynRealms(final String key) { - Query query = entityManager.createNativeQuery( - "SELECT dynRealm_id FROM " + DynRealmRepoExt.DYNMEMB_TABLE + " WHERE any_id=?"); - query.setParameter(1, key); - - @SuppressWarnings("unchecked") - List result = query.getResultList(); - return result.stream(). - map(dynRealm -> dynRealmDAO.findById(dynRealm.toString())). - flatMap(Optional::stream). - map(DynRealm::getKey). - distinct(). - toList(); + return query( + "SELECT DISTINCT dynRealm_id FROM " + DynRealmRepoExt.DYNMEMB_TABLE + " WHERE any_id=?", + rs -> { + List result = new ArrayList<>(); + while (rs.next()) { + result.add(rs.getString(1)); + } + return result; + }, + key); } @Override @@ -243,38 +164,13 @@ public void deleteRelationship(final Relationship relati entityManager.remove(relationship); } - protected void checkBeforeSave(final T attributable) { - // check UNIQUE constraints - new ArrayList<>(attributable.getPlainAttrsList()).stream(). - filter(attr -> attr.getUniqueValue() != null). - forEach(attr -> { - if (plainSchemaDAO.existsPlainAttrUniqueValue( - anyUtils, - attributable.getKey(), - plainSchemaDAO.findById(attr.getSchema()). - orElseThrow(() -> new NotFoundException("PlainSchema " + attr.getSchema())), - attr.getUniqueValue())) { - - throw new DuplicateException("Duplicate value found for " - + attr.getSchema() + "=" + attr.getUniqueValue().getValueAsString()); - } else { - LOG.debug("No duplicate value found for {}={}", - attr.getSchema(), attr.getUniqueValue().getValueAsString()); - } - }); - - // update sysInfo - if (attributable instanceof Any any) { - OffsetDateTime now = OffsetDateTime.now(); - String who = AuthContextUtils.getWho(); - LOG.debug("Set last change date '{}' and modifier '{}' for '{}'", now, who, any); - any.setLastModifier(who); - any.setLastChangeDate(now); - } - } - @Override public void deleteById(final String key) { findById(key).ifPresent(this::delete); } + + @Override + public void evict(final Class entityClass, final String key) { + Optional.ofNullable(entityManager.find(entityClass, key)).ifPresent(entityManager::detach); + } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractPlainSchemaRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractPlainSchemaRepoExt.java index fab37b2ab86..d9d9e1bea17 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractPlainSchemaRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractPlainSchemaRepoExt.java @@ -26,7 +26,6 @@ import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.PlainSchema; -import org.apache.syncope.core.persistence.jpa.entity.AbstractSchema; import org.apache.syncope.core.persistence.jpa.entity.JPAPlainSchema; import org.apache.syncope.core.persistence.jpa.entity.JPARealm; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject; @@ -58,14 +57,6 @@ public List findByAnyTypeClasses(final Collection { void deleteMembership(AMembership membership); - List findDynGroups(String key); + List findDynGroups(String key); List> findAllRelationships(AnyObject anyObject); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtImpl.java index 699486b3dd0..148bdfd0729 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtImpl.java @@ -19,13 +19,11 @@ package org.apache.syncope.core.persistence.jpa.dao.repo; import jakarta.persistence.EntityManager; -import jakarta.persistence.Query; import jakarta.persistence.TypedQuery; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -35,9 +33,9 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.types.AnyEntitlement; import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; @@ -55,7 +53,6 @@ import org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.security.DelegatedAdministrationException; -import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; public class AnyObjectRepoExtImpl extends AbstractAnyRepoExt implements AnyObjectRepoExt { @@ -67,16 +64,16 @@ public class AnyObjectRepoExtImpl extends AbstractAnyRepoExt implemen public AnyObjectRepoExtImpl( final AnyUtilsFactory anyUtilsFactory, final DynRealmDAO dynRealmDAO, - final PlainSchemaDAO plainSchemaDAO, final UserDAO userDAO, final GroupDAO groupDAO, final EntityManager entityManager, + final AnyChecker anyChecker, final AnyFinder anyFinder) { super( dynRealmDAO, - plainSchemaDAO, entityManager, + anyChecker, anyFinder, anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT)); this.userDAO = userDAO; @@ -85,34 +82,35 @@ public AnyObjectRepoExtImpl( @Override public Map countByType() { - Query query = entityManager.createNativeQuery( + return query( "SELECT e.type_id, COUNT(e.id) " + "FROM " + JPAAnyObject.TABLE + " e " - + "GROUP BY e.type_id"); + + "GROUP BY e.type_id", + rs -> { + Map result = new HashMap<>(); + while (rs.next()) { + result.put(rs.getString(1), rs.getLong(2)); + } + return result; + }); - @SuppressWarnings("unchecked") - List results = query.getResultList(); - - Map countByRealm = new LinkedHashMap<>(results.size()); - results.forEach(result -> countByRealm.put(result[0].toString(), ((Number) result[1]).longValue())); - - return Collections.unmodifiableMap(countByRealm); } @Override public Map countByRealm(final String anyType) { - Query query = entityManager.createNativeQuery( + return query( "SELECT r.fullPath, COUNT(e.id) " + "FROM " + JPAAnyObject.TABLE + " e JOIN Realm r ON e.realm_id=r.id " + "WHERE e.type_id=? " - + "GROUP BY r.fullPath"); - query.setParameter(1, anyType); - - @SuppressWarnings("unchecked") - List results = query.getResultList(); - return results.stream().collect(Collectors.toMap( - result -> result[0].toString(), - result -> ((Number) result[1]).longValue())); + + "GROUP BY r.fullPath", + rs -> { + Map result = new HashMap<>(); + while (rs.next()) { + result.put(rs.getString(1), rs.getLong(2)); + } + return result; + }, + anyType); } @Transactional(readOnly = true) @@ -183,9 +181,6 @@ public List> findAllRelationships(final AnyObject a protected Pair doSave(final AnyObject anyObject) { AnyObject merged = entityManager.merge(anyObject); - // ensure that entity listeners are invoked at this point - entityManager.flush(); - GroupDAO.DynMembershipInfo dynGroupMembs = groupDAO.refreshDynMemberships(merged); dynRealmDAO.refreshDynMemberships(merged); @@ -195,13 +190,13 @@ protected Pair doSave(final AnyObject any @Override @SuppressWarnings("unchecked") public S save(final S anyObject) { - checkBeforeSave((JPAAnyObject) anyObject); + anyChecker.checkBeforeSave(anyObject, anyUtils); return (S) doSave(anyObject).getLeft(); } @Override public GroupDAO.DynMembershipInfo saveAndGetDynGroupMembs(final AnyObject anyObject) { - checkBeforeSave((JPAAnyObject) anyObject); + anyChecker.checkBeforeSave(anyObject, anyUtils); return doSave(anyObject).getRight(); } @@ -223,23 +218,29 @@ protected List findURelationships(final AnyObject anyObject) { return query.getResultList(); } - @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) - @Override - public List findDynGroups(final String key) { - Query query = entityManager.createNativeQuery( - "SELECT group_id FROM " + GroupRepoExt.ADYNMEMB_TABLE + " WHERE any_id=?"); - query.setParameter(1, key); + protected List findDynGroupKeys(final String key) { + return query( + "SELECT DISTINCT group_id FROM " + GroupRepoExt.DYNMEMB_TABLE + " WHERE any_id=?", + rs -> { + List result = new ArrayList<>(); + while (rs.next()) { + result.add(rs.getString(1)); + } + return result; + }, + key); + } - @SuppressWarnings("unchecked") - List result = query.getResultList(); - return result.stream(). - map(groupKey -> groupDAO.findById(groupKey.toString())). + @Transactional(readOnly = true) + @Override + public List findDynGroups(final String key) { + return findDynGroupKeys(key).stream(). + map(groupDAO::findById). flatMap(Optional::stream). - distinct(). - collect(Collectors.toList()); + toList(); } - @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Transactional(readOnly = true) @Override public Collection findAllGroups(final AnyObject anyObject) { Set result = new HashSet<>(); @@ -250,13 +251,17 @@ public Collection findAllGroups(final AnyObject anyObject) { return result; } - @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Transactional(readOnly = true) @Override public Collection findAllGroupKeys(final AnyObject anyObject) { - return findAllGroups(anyObject).stream().map(Group::getKey).toList(); + Set result = new HashSet<>(); + result.addAll(anyObject.getMemberships().stream().map(m -> m.getRightEnd().getKey()).toList()); + result.addAll(findDynGroupKeys(anyObject.getKey())); + + return result; } - @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Transactional(readOnly = true) @Override public Collection findAllResources(final AnyObject anyObject) { Set result = new HashSet<>(); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyRepoExt.java index 46f65686d19..34dc052d186 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyRepoExt.java @@ -22,10 +22,8 @@ import java.util.Collection; import java.util.List; import java.util.Optional; -import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.Relationship; -import org.apache.syncope.core.persistence.api.entity.Schema; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; public interface AnyRepoExt { @@ -36,8 +34,6 @@ public interface AnyRepoExt { List findByDerAttrValue(String expression, String value, boolean ignoreCaseMatch); - AllowedSchemas findAllowedSchemas(A any, Class reference); - List findDynRealms(String key); Collection findAllResourceKeys(String key); @@ -49,4 +45,6 @@ public interface AnyRepoExt { void deleteById(String key); void delete(A any); + + void evict(Class entityClass, String key); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AttrRepoRepo.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AttrRepoRepo.java index e03e34cfdfd..d7b9ec4b47e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AttrRepoRepo.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AttrRepoRepo.java @@ -23,6 +23,6 @@ import org.springframework.data.repository.ListCrudRepository; public interface AttrRepoRepo - extends ListCrudRepository, AttrRepoRepoExt, AttrRepoDAO { + extends ListCrudRepository, AttrRepoDAO { } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuthModuleRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuthModuleRepoExt.java index e2f55778be2..63f5ccbbf4e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuthModuleRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuthModuleRepoExt.java @@ -22,7 +22,5 @@ public interface AuthModuleRepoExt { - AuthModule save(AuthModule authModule); - void delete(AuthModule authModule); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuthModuleRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuthModuleRepoExtImpl.java index a6b9769ced5..6af4567c1dc 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuthModuleRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuthModuleRepoExtImpl.java @@ -23,7 +23,6 @@ import org.apache.syncope.core.persistence.api.dao.PolicyDAO; import org.apache.syncope.core.persistence.api.entity.am.AuthModule; import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; -import org.apache.syncope.core.persistence.jpa.entity.am.JPAAuthModule; public class AuthModuleRepoExtImpl implements AuthModuleRepoExt { @@ -36,12 +35,6 @@ public AuthModuleRepoExtImpl(final PolicyDAO policyDAO, final EntityManager enti this.entityManager = entityManager; } - @Override - public AuthModule save(final AuthModule authModule) { - ((JPAAuthModule) authModule).list2json(); - return entityManager.merge(authModule); - } - @Override public void delete(final AuthModule authModule) { policyDAO.findAll(AuthPolicy.class).stream(). diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConnInstanceRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConnInstanceRepoExt.java index 5df206df0a3..f36e24ef57a 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConnInstanceRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConnInstanceRepoExt.java @@ -27,7 +27,5 @@ public interface ConnInstanceRepoExt { List findAll(); - ConnInstance save(ConnInstance connector); - void deleteById(String key); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConnInstanceRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConnInstanceRepoExtImpl.java index a865725bccc..c5058b3718c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConnInstanceRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConnInstanceRepoExtImpl.java @@ -30,6 +30,7 @@ import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.security.DelegatedAdministrationException; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; public class ConnInstanceRepoExtImpl implements ConnInstanceRepoExt { @@ -66,8 +67,8 @@ public ConnInstance authFind(final String key) { @Override public List findAll() { - final Set authRealms = AuthContextUtils.getAuthorizations().get(IdMEntitlement.CONNECTOR_LIST); - if (authRealms == null || authRealms.isEmpty()) { + Set authRealms = AuthContextUtils.getAuthorizations().get(IdMEntitlement.CONNECTOR_LIST); + if (CollectionUtils.isEmpty(authRealms)) { return List.of(); } @@ -79,12 +80,6 @@ public List findAll() { toList(); } - @Override - public ConnInstance save(final ConnInstance connector) { - ((JPAConnInstance) connector).list2json(); - return entityManager.merge(connector); - } - @Override public void deleteById(final String key) { ConnInstance connInstance = entityManager.find(JPAConnInstance.class, key); @@ -92,7 +87,8 @@ public void deleteById(final String key) { return; } - connInstance.getResources().stream().map(ExternalResource::getKey).toList().forEach(resourceDAO::deleteById); + resourceDAO.findByConnInstance(connInstance.getKey()).stream(). + map(ExternalResource::getKey).toList().forEach(resourceDAO::deleteById); entityManager.remove(connInstance); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DelegationRepo.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DelegationRepo.java index 812da97238f..454498ed2f5 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DelegationRepo.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DelegationRepo.java @@ -22,6 +22,8 @@ import java.util.List; import java.util.Optional; import org.apache.syncope.core.persistence.api.dao.DelegationDAO; +import org.apache.syncope.core.persistence.api.entity.Delegation; +import org.apache.syncope.core.persistence.api.entity.Role; import org.apache.syncope.core.persistence.jpa.entity.JPADelegation; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.ListCrudRepository; @@ -30,6 +32,10 @@ public interface DelegationRepo extends ListCrudRepository, DelegationDAO { + @Query("SELECT e FROM #{#entityName} e WHERE :role MEMBER OF e.roles") + @Override + List findByRoles(@Param("role") Role role); + @Query("SELECT e.id FROM #{#entityName} e " + "WHERE e.delegating.id = :delegating " + "AND e.delegated.id = :delegated " diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DerSchemaRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DerSchemaRepoExt.java index 5aab3645ab5..415fd1c824f 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DerSchemaRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DerSchemaRepoExt.java @@ -27,7 +27,5 @@ public interface DerSchemaRepoExt { List findByAnyTypeClasses(Collection anyTypeClasses); - DerSchema save(DerSchema schema); - void deleteById(String key); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DerSchemaRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DerSchemaRepoExtImpl.java index 729ca3bb618..0ab143a8b88 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DerSchemaRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DerSchemaRepoExtImpl.java @@ -40,14 +40,6 @@ public List findByAnyTypeClasses(final Collection findAll(); - ExternalResource save(ExternalResource resource); - void deleteById(String key); void deleteMapping(String schemaKey); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ExternalResourceRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ExternalResourceRepoExtImpl.java index c8553fed177..28d62d62deb 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ExternalResourceRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ExternalResourceRepoExtImpl.java @@ -136,13 +136,6 @@ public List findAll() { toList(); } - @Transactional(rollbackFor = { Throwable.class }) - @Override - public ExternalResource save(final ExternalResource resource) { - ((JPAExternalResource) resource).list2json(); - return entityManager.merge(resource); - } - @Override public void deleteMapping(final String schemaKey) { findAll().forEach(resource -> { @@ -184,12 +177,6 @@ public void deleteById(final String key) { groupDAO.findByResourcesContaining(resource). forEach(group -> group.getResources().remove(resource)); - if (resource.getConnector() != null - && resource.getConnector().getResources() != null - && !resource.getConnector().getResources().isEmpty()) { - - resource.getConnector().getResources().remove(resource); - } resource.setConnector(null); entityManager.remove(resource); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java index 2068fea1203..74a42705acf 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java @@ -33,9 +33,7 @@ public interface GroupRepoExt extends AnyRepoExt { - String ADYNMEMB_TABLE = "ADynGroupMembers"; - - String UDYNMEMB_TABLE = "UDynGroupMembers"; + String DYNMEMB_TABLE = "DynGroupMembers"; void securityChecks(Set authRealms, String key, String realm); @@ -63,9 +61,7 @@ public interface GroupRepoExt extends AnyRepoExt { List findUDynMembers(Group group); - void clearADynMembers(Group group); - - void clearUDynMembers(Group group); + void clearDynMembers(Group group); GroupDAO.DynMembershipInfo refreshDynMemberships(AnyObject anyObject); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java index 54820640cb0..595f90bff67 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java @@ -23,21 +23,21 @@ import jakarta.persistence.TypedQuery; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.AnyDAO; import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; @@ -45,25 +45,22 @@ import org.apache.syncope.core.persistence.api.entity.AnyType; import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; import org.apache.syncope.core.persistence.api.entity.ExternalResource; -import org.apache.syncope.core.persistence.api.entity.Realm; -import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.group.GroupTypeExtension; -import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; import org.apache.syncope.core.persistence.api.entity.user.UMembership; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.api.search.SearchCondConverter; import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; import org.apache.syncope.core.persistence.api.utils.RealmUtils; import org.apache.syncope.core.persistence.common.dao.AnyFinder; -import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership; +import org.apache.syncope.core.persistence.jpa.entity.JPADynGroupMembership; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership; import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup; import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroupTypeExtension; -import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership; import org.apache.syncope.core.provisioning.api.event.EntityLifecycleEvent; import org.apache.syncope.core.spring.security.AuthContextUtils; @@ -94,7 +91,6 @@ public GroupRepoExtImpl( final AnyUtilsFactory anyUtilsFactory, final ApplicationEventPublisher publisher, final DynRealmDAO dynRealmDAO, - final PlainSchemaDAO plainSchemaDAO, final RealmDAO realmDAO, final AnyMatchDAO anyMatchDAO, final UserDAO userDAO, @@ -102,12 +98,13 @@ public GroupRepoExtImpl( final AnySearchDAO searchDAO, final SearchCondVisitor searchCondVisitor, final EntityManager entityManager, + final AnyChecker anyChecker, final AnyFinder anyFinder) { super( dynRealmDAO, - plainSchemaDAO, entityManager, + anyChecker, anyFinder, anyUtilsFactory.getInstance(AnyTypeKind.GROUP)); this.publisher = publisher; @@ -151,14 +148,17 @@ protected void securityChecks(final Group group) { @Override public Map countByRealm() { - Query query = entityManager.createQuery( - "SELECT e.realm, COUNT(e) FROM " + anyUtils.anyClass().getSimpleName() + " e GROUP BY e.realm"); - - @SuppressWarnings("unchecked") - List results = query.getResultList(); - return results.stream().collect(Collectors.toMap( - result -> ((Realm) result[0]).getFullPath(), - result -> ((Number) result[1]).longValue())); + return query( + "SELECT r.fullPath, COUNT(e.id) " + + "FROM " + JPAGroup.TABLE + " e JOIN Realm r ON e.realm_id=r.id " + + "GROUP BY r.fullPath", + rs -> { + Map result = new HashMap<>(); + while (rs.next()) { + result.put(rs.getString(1), rs.getLong(2)); + } + return result; + }); } @Transactional(readOnly = true) @@ -191,23 +191,25 @@ public Collection findAllResourceKeys(final String key) { @Transactional(readOnly = true) @Override public boolean existsAMembership(final String anyObjectKey, final String groupKey) { - Query query = entityManager.createNativeQuery( - "SELECT COUNT(*) FROM " + JPAAMembership.TABLE + " WHERE group_id=? AND anyobject_it=?"); - query.setParameter(1, groupKey); - query.setParameter(2, anyObjectKey); - - return ((Number) query.getSingleResult()).longValue() > 0; + return query( + "SELECT COUNT(*) FROM " + JPAAMembership.TABLE + " WHERE group_id=? AND anyobject_it=?", + rs -> { + rs.next(); + return rs.getLong(1); + }, + groupKey, anyObjectKey) > 0; } @Transactional(readOnly = true) @Override public boolean existsUMembership(final String userKey, final String groupKey) { - Query query = entityManager.createNativeQuery( - "SELECT COUNT(*) FROM " + JPAUMembership.TABLE + " WHERE group_id=? AND user_id=?"); - query.setParameter(1, groupKey); - query.setParameter(2, userKey); - - return ((Number) query.getSingleResult()).longValue() > 0; + return query( + "SELECT COUNT(*) FROM " + JPAUMembership.TABLE + " WHERE group_id=? AND user_id=?", + rs -> { + rs.next(); + return rs.getLong(1); + }, + groupKey, userKey) > 0; } @Override @@ -237,7 +239,7 @@ public List findUMemberships(final Group group, final Pageable page @Override public S save(final S group) { - checkBeforeSave((JPAGroup) group); + anyChecker.checkBeforeSave(group, anyUtils); return entityManager.merge(group); } @@ -246,49 +248,23 @@ public Group saveAndRefreshDynMemberships(final Group group) { Group merged = save(group); // refresh dynamic memberships - clearUDynMembers(merged); - if (merged.getUDynMembership() != null) { - SearchCond cond = SearchCondConverter.convert(searchCondVisitor, merged.getUDynMembership().getFIQLCond()); - long count = anySearchDAO.count( - realmDAO.getRoot(), true, Set.of(SyncopeConstants.ROOT_REALM), cond, AnyTypeKind.USER); - for (int page = 0; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE); page++) { - List matching = anySearchDAO.search( - realmDAO.getRoot(), - true, - Set.of(SyncopeConstants.ROOT_REALM), - cond, - PageRequest.of(page, AnyDAO.DEFAULT_PAGE_SIZE), - AnyTypeKind.USER); - - matching.forEach(user -> { - Query insert = entityManager.createNativeQuery( - "INSERT INTO " + UDYNMEMB_TABLE + " VALUES(?, ?)"); - insert.setParameter(1, user.getKey()); - insert.setParameter(2, merged.getKey()); - insert.executeUpdate(); - - publisher.publishEvent( - new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, user, AuthContextUtils.getDomain())); - }); - } - } - clearADynMembers(merged); - merged.getADynMemberships().forEach(memb -> { + clearDynMembers(merged); + merged.getDynMemberships().forEach(memb -> { SearchCond cond = SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond()); long count = anySearchDAO.count( - realmDAO.getRoot(), true, Set.of(SyncopeConstants.ROOT_REALM), cond, AnyTypeKind.ANY_OBJECT); + realmDAO.getRoot(), true, Set.of(SyncopeConstants.ROOT_REALM), cond, memb.getAnyType().getKind()); for (int page = 0; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE); page++) { - List matching = anySearchDAO.search( + List matching = anySearchDAO.search( realmDAO.getRoot(), true, Set.of(SyncopeConstants.ROOT_REALM), cond, PageRequest.of(page, AnyDAO.DEFAULT_PAGE_SIZE), - AnyTypeKind.ANY_OBJECT); + memb.getAnyType().getKind()); matching.forEach(any -> { Query insert = entityManager.createNativeQuery( - "INSERT INTO " + ADYNMEMB_TABLE + " VALUES(?, ?, ?)"); + "INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?, ?)"); insert.setParameter(1, any.getType().getKey()); insert.setParameter(2, any.getKey()); insert.setParameter(3, merged.getKey()); @@ -331,8 +307,7 @@ public void delete(final Group group) { new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, leftEnd, AuthContextUtils.getDomain())); }); - clearUDynMembers(group); - clearADynMembers(group); + clearDynMembers(group); entityManager.remove(group); } @@ -349,24 +324,24 @@ public List findTypeExtensions(final AnyTypeClass anyTypeCla @Override public long countADynMembers(final Group group) { - Query query = entityManager.createNativeQuery( - "SELECT COUNT(DISTINCT any_id) FROM " + ADYNMEMB_TABLE + " WHERE group_id=?"); - query.setParameter(1, group.getKey()); - - return ((Number) query.getSingleResult()).longValue(); + return query( + "SELECT COUNT(any_id) FROM " + DYNMEMB_TABLE + " WHERE group_id=?", + rs -> { + rs.next(); + return rs.getLong(1); + }, + group.getKey()); } @Override public long countUDynMembers(final Group group) { - if (group.getUDynMembership() == null) { - return 0; - } - - Query query = entityManager.createNativeQuery( - "SELECT COUNT(DISTINCT any_id) FROM " + UDYNMEMB_TABLE + " WHERE group_id=?"); - query.setParameter(1, group.getKey()); - - return ((Number) query.getSingleResult()).longValue(); + return query( + "SELECT COUNT(any_id) FROM " + DYNMEMB_TABLE + " WHERE group_id=?", + rs -> { + rs.next(); + return rs.getLong(1); + }, + group.getKey()); } @Transactional(readOnly = true) @@ -374,58 +349,48 @@ public long countUDynMembers(final Group group) { public List findADynMembers(final Group group) { List result = new ArrayList<>(); - group.getADynMemberships().forEach(memb -> { - Query query = entityManager.createNativeQuery( - "SELECT DISTINCT any_id FROM " + ADYNMEMB_TABLE + " WHERE group_id=? AND anyType_id=?"); - query.setParameter(1, group.getKey()); - query.setParameter(2, memb.getAnyType().getKey()); - - @SuppressWarnings("unchecked") - List queryResult = query.getResultList(); - result.addAll(queryResult.stream(). - map(Object::toString). - filter(anyObject -> !result.contains(anyObject)). - toList()); - }); + group.getDynMemberships().forEach(memb -> query( + "SELECT DISTINCT any_id FROM " + DYNMEMB_TABLE + " WHERE group_id=? AND anyType_id=?", + rs -> { + List list = new ArrayList<>(); + while (rs.next()) { + String anyObject = rs.getString(1); + if (!result.contains(anyObject)) { + result.add(anyObject); + } + } + return list; + }, + group.getKey(), memb.getAnyType().getKey())); return result; } @Override public List findUDynMembers(final Group group) { - if (group.getUDynMembership() == null) { - return List.of(); - } - - Query query = entityManager.createNativeQuery( - "SELECT DISTINCT any_id FROM " + UDYNMEMB_TABLE + " WHERE group_id=?"); - query.setParameter(1, group.getKey()); - - @SuppressWarnings("unchecked") - List result = query.getResultList(); - return result.stream(). - map(Object::toString). - toList(); - } - - @Override - public void clearADynMembers(final Group group) { - Query delete = entityManager.createNativeQuery("DELETE FROM " + ADYNMEMB_TABLE + " WHERE group_id=?"); - delete.setParameter(1, group.getKey()); - delete.executeUpdate(); + return query( + "SELECT DISTINCT any_id FROM " + DYNMEMB_TABLE + " WHERE group_id=? AND anyType_id=?", + rs -> { + List result = new ArrayList<>(); + while (rs.next()) { + result.add(rs.getString(1)); + } + return result; + }, + group.getKey(), AnyTypeKind.USER.name()); } @Override - public void clearUDynMembers(final Group group) { - Query delete = entityManager.createNativeQuery("DELETE FROM " + UDYNMEMB_TABLE + " WHERE group_id=?"); + public void clearDynMembers(final Group group) { + Query delete = entityManager.createNativeQuery("DELETE FROM " + DYNMEMB_TABLE + " WHERE group_id=?"); delete.setParameter(1, group.getKey()); delete.executeUpdate(); } - protected List findWithADynMemberships(final AnyType anyType) { - TypedQuery query = entityManager.createQuery( - "SELECT e FROM " + JPAADynGroupMembership.class.getSimpleName() + " e WHERE e.anyType=:anyType", - ADynGroupMembership.class); + protected List findWithDynMemberships(final AnyType anyType) { + TypedQuery query = entityManager.createQuery( + "SELECT e FROM " + JPADynGroupMembership.class.getSimpleName() + " e WHERE e.anyType=:anyType", + DynGroupMembership.class); query.setParameter("anyType", anyType); return query.getResultList(); } @@ -435,32 +400,34 @@ protected List findWithADynMemberships(final AnyType anyTyp public GroupDAO.DynMembershipInfo refreshDynMemberships(final AnyObject anyObject) { Set before = new HashSet<>(); Set after = new HashSet<>(); - findWithADynMemberships(anyObject.getType()).forEach(memb -> { + findWithDynMemberships(anyObject.getType()).forEach(memb -> { boolean matches = anyMatchDAO.matches( anyObject, SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond())); if (matches) { after.add(memb.getGroup().getKey()); } - Query query = entityManager.createNativeQuery( - "SELECT COUNT(group_id) FROM " + ADYNMEMB_TABLE + " WHERE group_id=? AND any_id=?"); - query.setParameter(1, memb.getGroup().getKey()); - query.setParameter(2, anyObject.getKey()); - boolean existing = ((Number) query.getSingleResult()).longValue() > 0; + boolean existing = query( + "SELECT COUNT(group_id) FROM " + DYNMEMB_TABLE + " WHERE group_id=? AND any_id=?", + rs -> { + rs.next(); + return rs.getLong(1); + }, + memb.getGroup().getKey(), anyObject.getKey()) > 0; if (existing) { before.add(memb.getGroup().getKey()); } if (matches && !existing) { Query insert = entityManager.createNativeQuery( - "INSERT INTO " + ADYNMEMB_TABLE + " VALUES(?, ?, ?)"); + "INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?, ?)"); insert.setParameter(1, anyObject.getType().getKey()); insert.setParameter(2, anyObject.getKey()); insert.setParameter(3, memb.getGroup().getKey()); insert.executeUpdate(); } else if (!matches && existing) { Query delete = entityManager.createNativeQuery( - "DELETE FROM " + ADYNMEMB_TABLE + " WHERE group_id=? AND any_id=?"); + "DELETE FROM " + DYNMEMB_TABLE + " WHERE group_id=? AND any_id=?"); delete.setParameter(1, memb.getGroup().getKey()); delete.setParameter(2, anyObject.getKey()); delete.executeUpdate(); @@ -477,7 +444,7 @@ public GroupDAO.DynMembershipInfo refreshDynMemberships(final AnyObject anyObjec public Set removeDynMemberships(final AnyObject anyObject) { List dynGroups = anyObjectDAO.findDynGroups(anyObject.getKey()); - Query delete = entityManager.createNativeQuery("DELETE FROM " + ADYNMEMB_TABLE + " WHERE any_id=?"); + Query delete = entityManager.createNativeQuery("DELETE FROM " + DYNMEMB_TABLE + " WHERE any_id=?"); delete.setParameter(1, anyObject.getKey()); delete.executeUpdate(); @@ -492,44 +459,39 @@ public Set removeDynMemberships(final AnyObject anyObject) { return before; } - protected List findWithUDynMemberships() { - TypedQuery query = entityManager.createQuery( - "SELECT e FROM " + JPAUDynGroupMembership.class.getSimpleName() + " e", - UDynGroupMembership.class); - - return query.getResultList(); - } - @Transactional @Override public GroupDAO.DynMembershipInfo refreshDynMemberships(final User user) { Set before = new HashSet<>(); Set after = new HashSet<>(); - findWithUDynMemberships().forEach(memb -> { + findWithDynMemberships(user.getType()).forEach(memb -> { boolean matches = anyMatchDAO.matches( user, SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond())); if (matches) { after.add(memb.getGroup().getKey()); } - Query query = entityManager.createNativeQuery( - "SELECT COUNT(group_id) FROM " + UDYNMEMB_TABLE + " WHERE group_id=? AND any_id=?"); - query.setParameter(1, memb.getGroup().getKey()); - query.setParameter(2, user.getKey()); - boolean existing = ((Number) query.getSingleResult()).longValue() > 0; + boolean existing = query( + "SELECT COUNT(group_id) FROM " + DYNMEMB_TABLE + " WHERE group_id=? AND any_id=?", + rs -> { + rs.next(); + return rs.getLong(1); + }, + memb.getGroup().getKey(), user.getKey()) > 0; if (existing) { before.add(memb.getGroup().getKey()); } if (matches && !existing) { Query insert = entityManager.createNativeQuery( - "INSERT INTO " + UDYNMEMB_TABLE + " VALUES(?, ?)"); - insert.setParameter(1, user.getKey()); - insert.setParameter(2, memb.getGroup().getKey()); + "INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?, ?)"); + insert.setParameter(1, user.getType().getKey()); + insert.setParameter(2, user.getKey()); + insert.setParameter(3, memb.getGroup().getKey()); insert.executeUpdate(); } else if (!matches && existing) { Query delete = entityManager.createNativeQuery( - "DELETE FROM " + UDYNMEMB_TABLE + " WHERE group_id=? AND any_id=?"); + "DELETE FROM " + DYNMEMB_TABLE + " WHERE group_id=? AND any_id=?"); delete.setParameter(1, memb.getGroup().getKey()); delete.setParameter(2, user.getKey()); delete.executeUpdate(); @@ -546,7 +508,7 @@ public GroupDAO.DynMembershipInfo refreshDynMemberships(final User user) { public Set removeDynMemberships(final User user) { List dynGroups = userDAO.findDynGroups(user.getKey()); - Query delete = entityManager.createNativeQuery("DELETE FROM " + UDYNMEMB_TABLE + " WHERE any_id=?"); + Query delete = entityManager.createNativeQuery("DELETE FROM " + DYNMEMB_TABLE + " WHERE any_id=?"); delete.setParameter(1, user.getKey()); delete.executeUpdate(); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NotificationRepo.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NotificationRepo.java index 07fb7931c82..bc807bebf46 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NotificationRepo.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NotificationRepo.java @@ -24,5 +24,4 @@ public interface NotificationRepo extends ListCrudRepository, NotificationRepoExt, NotificationDAO { - } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NotificationRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NotificationRepoExt.java index e20c9ddaddf..4537d459d24 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NotificationRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NotificationRepoExt.java @@ -18,11 +18,7 @@ */ package org.apache.syncope.core.persistence.jpa.dao.repo; -import org.apache.syncope.core.persistence.api.entity.Notification; - public interface NotificationRepoExt { - Notification save(Notification notification); - void deleteById(String key); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NotificationRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NotificationRepoExtImpl.java index e3a98c4ec0b..2ee305ccc55 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NotificationRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NotificationRepoExtImpl.java @@ -37,12 +37,6 @@ public NotificationRepoExtImpl(final TaskDAO taskDAO, final EntityManager entity this.entityManager = entityManager; } - @Override - public Notification save(final Notification notification) { - ((JPANotification) notification).list2json(); - return entityManager.merge(notification); - } - @Override public void deleteById(final String key) { Notification notification = entityManager.find(JPANotification.class, key); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OIDCRPClientAppRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OIDCRPClientAppRepoExt.java index 95b15cd4798..7bf89ff3ae8 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OIDCRPClientAppRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OIDCRPClientAppRepoExt.java @@ -21,6 +21,4 @@ import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp; public interface OIDCRPClientAppRepoExt extends ClientAppRepoExt { - - OIDCRPClientApp save(OIDCRPClientApp clientApp); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OIDCRPClientAppRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OIDCRPClientAppRepoExtImpl.java index 0adf7ee61be..5022af5a64d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OIDCRPClientAppRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OIDCRPClientAppRepoExtImpl.java @@ -42,10 +42,4 @@ public List findAllByPolicy(final Policy policy) { public List findAllByRealm(final Realm realm) { return findAllByRealm(realm, OIDCRPClientApp.class, JPAOIDCRPClientApp.class); } - - @Override - public OIDCRPClientApp save(final OIDCRPClientApp clientApp) { - ((JPAOIDCRPClientApp) clientApp).list2json(); - return entityManager.merge(clientApp); - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExt.java index e2bcb19a5ed..821eaf169e9 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExt.java @@ -35,7 +35,5 @@ public interface PlainSchemaRepoExt { boolean existsPlainAttrUniqueValue(AnyUtils anyUtils, String anyKey, PlainSchema schema, PlainAttrValue attrValue); - PlainSchema save(PlainSchema schema); - void deleteById(String key); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepo.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepo.java index 8004a10bfb1..829884bda0c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepo.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepo.java @@ -18,11 +18,19 @@ */ package org.apache.syncope.core.persistence.jpa.dao.repo; +import java.util.List; import org.apache.syncope.core.persistence.api.dao.RoleDAO; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.Role; import org.apache.syncope.core.persistence.jpa.entity.JPARole; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.ListCrudRepository; +import org.springframework.data.repository.query.Param; public interface RoleRepo extends ListCrudRepository, RoleRepoExt, RoleDAO { + @Query("SELECT e FROM #{#entityName} e WHERE :realm MEMBER OF e.realms") + @Override + List findByRealms(@Param("realm") Realm realm); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExt.java index 44ed439c998..4a3313ed055 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExt.java @@ -26,8 +26,6 @@ public interface RoleRepoExt { String DYNMEMB_TABLE = "DynRoleMembers"; - Role save(Role role); - void delete(Role role); Role saveAndRefreshDynMemberships(Role role); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExtImpl.java index a3e94a69b43..8aebb94a8fc 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExtImpl.java @@ -68,15 +68,9 @@ public RoleRepoExtImpl( this.entityManager = entityManager; } - @Override - public Role save(final Role role) { - ((JPARole) role).list2json(); - return entityManager.merge(role); - } - @Override public Role saveAndRefreshDynMemberships(final Role role) { - Role merged = save(role); + Role merged = entityManager.merge(role); // refresh dynamic memberships clearDynMembers(merged); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/SAML2SPClientAppRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/SAML2SPClientAppRepoExt.java index e4397aad0bc..166794407fe 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/SAML2SPClientAppRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/SAML2SPClientAppRepoExt.java @@ -21,6 +21,4 @@ import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp; public interface SAML2SPClientAppRepoExt extends ClientAppRepoExt { - - SAML2SPClientApp save(SAML2SPClientApp clientApp); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/SAML2SPClientAppRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/SAML2SPClientAppRepoExtImpl.java index ef8db54329e..b0f1a9eeb78 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/SAML2SPClientAppRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/SAML2SPClientAppRepoExtImpl.java @@ -42,10 +42,4 @@ public List findAllByPolicy(final Policy policy) { public List findAllByRealm(final Realm realm) { return findAllByRealm(realm, SAML2SPClientApp.class, JPASAML2SPClientApp.class); } - - @Override - public SAML2SPClientApp save(final SAML2SPClientApp clientApp) { - ((JPASAML2SPClientApp) clientApp).list2json(); - return entityManager.merge(clientApp); - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepo.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepo.java index 846521afea6..850c7794a13 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepo.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepo.java @@ -40,10 +40,6 @@ public interface UserRepo @Override Optional findUsername(@Param("key") String key); - @Query("SELECT e FROM #{#entityName} e WHERE e.token LIKE :token") - @Override - Optional findByToken(@Param("token") String token); - @Query("SELECT e FROM #{#entityName} e WHERE e.id IN (:keys)") @Override List findByKeys(@Param("keys") List keys); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExt.java index f91d920675f..28819043680 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExt.java @@ -21,6 +21,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.entity.ExternalResource; @@ -31,6 +32,8 @@ public interface UserRepoExt extends AnyRepoExt { + Optional findByToken(String token); + void securityChecks(Set authRealms, String key, String realm, Collection groups); Map countByRealm(); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java index 1a0502052ef..eb45038070c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java @@ -19,8 +19,9 @@ package org.apache.syncope.core.persistence.jpa.dao.repo; import jakarta.persistence.EntityManager; -import jakarta.persistence.Query; +import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -32,15 +33,14 @@ import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.DelegationDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.RoleDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.ExternalResource; -import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.entity.Role; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.user.UMembership; @@ -52,7 +52,6 @@ import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.security.DelegatedAdministrationException; import org.apache.syncope.core.spring.security.SecurityProperties; -import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; public class UserRepoExtImpl extends AbstractAnyRepoExt implements UserRepoExt { @@ -72,7 +71,6 @@ public class UserRepoExtImpl extends AbstractAnyRepoExt implements UserRep public UserRepoExtImpl( final AnyUtilsFactory anyUtilsFactory, final DynRealmDAO dynRealmDAO, - final PlainSchemaDAO plainSchemaDAO, final RoleDAO roleDAO, final AccessTokenDAO accessTokenDAO, final GroupDAO groupDAO, @@ -80,12 +78,13 @@ public UserRepoExtImpl( final FIQLQueryDAO fiqlQueryDAO, final SecurityProperties securityProperties, final EntityManager entityManager, + final AnyChecker anyChecker, final AnyFinder anyFinder) { super( dynRealmDAO, - plainSchemaDAO, entityManager, + anyChecker, anyFinder, anyUtilsFactory.getInstance(AnyTypeKind.USER)); this.roleDAO = roleDAO; @@ -96,28 +95,48 @@ public UserRepoExtImpl( this.securityProperties = securityProperties; } + @Transactional(readOnly = true) + @Override + public Optional findByToken(final String token) { + return query( + "SELECT id FROM " + JPAUser.TABLE + " WHERE token LIKE ?", + rs -> { + if (rs.next()) { + return Optional.of(rs.getString(1)); + } + return Optional.empty(); + }, + token); + } + @Override public Map countByRealm() { - Query query = entityManager.createQuery( - "SELECT e.realm, COUNT(e) FROM " + anyUtils.anyClass().getSimpleName() + " e GROUP BY e.realm"); - - @SuppressWarnings("unchecked") - List results = query.getResultList(); - return results.stream().collect(Collectors.toMap( - result -> ((Realm) result[0]).getFullPath(), - result -> ((Number) result[1]).longValue())); + return query( + "SELECT r.fullPath, COUNT(e.id) " + + "FROM " + JPAUser.TABLE + " e JOIN Realm r ON e.realm_id=r.id " + + "GROUP BY r.fullPath", + rs -> { + Map result = new HashMap<>(); + while (rs.next()) { + result.put(rs.getString(1), rs.getLong(2)); + } + return result; + }); } @Override public Map countByStatus() { - Query query = entityManager.createQuery( - "SELECT e.status, COUNT(e) FROM " + anyUtils.anyClass().getSimpleName() + " e GROUP BY e.status"); - - @SuppressWarnings("unchecked") - List results = query.getResultList(); - return results.stream().collect(Collectors.toMap( - result -> (String) result[0], - result -> ((Number) result[1]).longValue())); + return query( + "SELECT e.status, COUNT(e.id) " + + "FROM " + JPAUser.TABLE + " e " + + "GROUP BY e.status", + rs -> { + Map result = new HashMap<>(); + while (rs.next()) { + result.put(rs.getString(1), rs.getLong(2)); + } + return result; + }); } @Transactional(readOnly = true) @@ -169,26 +188,15 @@ public void deleteMembership(final UMembership membership) { } protected User checkBeforeSave(final User user) { - User merged = user; - if (user.getLinkedAccounts() == null) { - entityManager.flush(); - merged = entityManager.merge(user); - } - merged.getLinkedAccounts().stream().map(JPALinkedAccount.class::cast).forEach(JPALinkedAccount::list2json); - - super.checkBeforeSave((JPAUser) merged); - merged.getLinkedAccounts().forEach(account -> super.checkBeforeSave((JPALinkedAccount) account)); + anyChecker.checkBeforeSave(user, anyUtils); + user.getLinkedAccounts().forEach(account -> anyChecker.checkBeforeSave(account, anyUtils)); - return merged; + return user; } protected Pair doSave(final User user) { - entityManager.flush(); User merged = entityManager.merge(user); - // ensure that entity listeners are invoked at this point - entityManager.flush(); - roleDAO.refreshDynMemberships(merged); GroupDAO.DynMembershipInfo dynGroupMembs = groupDAO.refreshDynMemberships(merged); dynRealmDAO.refreshDynMemberships(merged); @@ -199,7 +207,7 @@ protected Pair doSave(final User user) { @SuppressWarnings("unchecked") @Override public S save(final S user) { - return (S) doSave(checkBeforeSave(user)).getLeft(); + return (S) doSave(checkBeforeSave(entityManager.merge(user))).getLeft(); } @Override @@ -223,7 +231,25 @@ public void delete(final User user) { entityManager.remove(user); } - @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Transactional(readOnly = true) + @Override + public List findDynRoles(final String key) { + return query( + "SELECT DISTINCT role_id FROM " + RoleRepoExt.DYNMEMB_TABLE + " WHERE any_id=?", + rs -> { + List result = new ArrayList<>(); + while (rs.next()) { + result.add(rs.getString(1)); + } + return result; + }, + key).stream(). + map(roleDAO::findById). + flatMap(Optional::stream). + collect(Collectors.toList()); + } + + @Transactional(readOnly = true) @Override public Collection findAllRoles(final User user) { Set result = new HashSet<>(); @@ -233,39 +259,29 @@ public Collection findAllRoles(final User user) { return result; } - @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) - @Override - public List findDynRoles(final String key) { - Query query = entityManager.createNativeQuery( - "SELECT role_id FROM " + RoleRepoExt.DYNMEMB_TABLE + " WHERE any_id=?"); - query.setParameter(1, key); - - @SuppressWarnings("unchecked") - List result = query.getResultList(); - return result.stream(). - map(roleKey -> roleDAO.findById(roleKey.toString())). - flatMap(Optional::stream). - distinct(). - collect(Collectors.toList()); + protected List findDynGroupKeys(final String key) { + return query( + "SELECT DISTINCT group_id FROM " + GroupRepoExt.DYNMEMB_TABLE + " WHERE any_id=?", + rs -> { + List result = new ArrayList<>(); + while (rs.next()) { + result.add(rs.getString(1)); + } + return result; + }, + key); } - @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Transactional(readOnly = true) @Override public List findDynGroups(final String key) { - Query query = entityManager.createNativeQuery( - "SELECT group_id FROM " + GroupRepoExt.UDYNMEMB_TABLE + " WHERE any_id=?"); - query.setParameter(1, key); - - @SuppressWarnings("unchecked") - List result = query.getResultList(); - return result.stream(). - map(groupKey -> groupDAO.findById(groupKey.toString())). + return findDynGroupKeys(key).stream(). + map(groupDAO::findById). flatMap(Optional::stream). - distinct(). collect(Collectors.toList()); } - @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Transactional(readOnly = true) @Override public Collection findAllGroups(final User user) { Set result = new HashSet<>(); @@ -276,19 +292,23 @@ public Collection findAllGroups(final User user) { return result; } - @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Transactional(readOnly = true) @Override public Collection findAllGroupKeys(final User user) { - return findAllGroups(user).stream().map(Group::getKey).toList(); + Set result = new HashSet<>(); + result.addAll(user.getMemberships().stream().map(m -> m.getRightEnd().getKey()).toList()); + result.addAll(findDynGroupKeys(user.getKey())); + + return result; } - @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Transactional(readOnly = true) @Override public Collection findAllGroupNames(final User user) { return findAllGroups(user).stream().map(Group::getName).toList(); } - @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Transactional(readOnly = true) @Override public Collection findAllResources(final User user) { Set result = new HashSet<>(); @@ -307,11 +327,12 @@ public Collection findAllResourceKeys(final String key) { @Transactional(readOnly = true) @Override public boolean linkedAccountExists(final String userKey, final String connObjectKeyValue) { - Query query = entityManager.createNativeQuery( - "SELECT COUNT(id) FROM " + JPALinkedAccount.TABLE + " WHERE owner_id=? AND connObjectKeyValue=?"); - query.setParameter(1, userKey); - query.setParameter(2, connObjectKeyValue); - - return ((Number) query.getSingleResult()).longValue() > 0; + return query( + "SELECT COUNT(id) FROM " + JPALinkedAccount.TABLE + " WHERE owner_id=? AND connObjectKeyValue=?", + rs -> { + rs.next(); + return rs.getLong(1); + }, + userKey, connObjectKeyValue) > 0; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAny.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAny.java index 62534271c1d..87f1dbbd409 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAny.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAny.java @@ -25,9 +25,7 @@ import java.time.OffsetDateTime; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.Realm; -import org.apache.syncope.core.persistence.common.validation.AnyCheck; -@AnyCheck @MappedSuperclass public abstract class AbstractAny extends AbstractAttributable implements Any { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAttributable.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAttributable.java index 08aea745884..9426dde0256 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAttributable.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAttributable.java @@ -18,26 +18,12 @@ */ package org.apache.syncope.core.persistence.jpa.entity; -import java.util.List; import org.apache.syncope.core.persistence.api.entity.Attributable; -import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.persistence.common.validation.AttributableCheck; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @AttributableCheck public abstract class AbstractAttributable extends AbstractGeneratedKeyEntity implements Attributable { private static final long serialVersionUID = -2072949733409392882L; - public abstract List getPlainAttrsList(); - - public abstract String getPlainAttrsJSON(); - - public abstract void setPlainAttrsJSON(String plainAttrs); - - public void list2json() { - setPlainAttrsJSON(getPlainAttrsList().isEmpty() - ? "[]" - : POJOHelper.serialize(getPlainAttrsList())); - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntityFactory.java index 25f86b426ce..1753e8572a4 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntityFactory.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntityFactory.java @@ -29,6 +29,7 @@ import org.apache.syncope.core.persistence.api.entity.ConnInstance; import org.apache.syncope.core.persistence.api.entity.Delegation; import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; import org.apache.syncope.core.persistence.api.entity.DynRealm; import org.apache.syncope.core.persistence.api.entity.DynRealmMembership; import org.apache.syncope.core.persistence.api.entity.Entity; @@ -58,7 +59,6 @@ import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity; import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp; import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry; -import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; @@ -91,7 +91,6 @@ import org.apache.syncope.core.persistence.api.entity.task.SchedTask; import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion; -import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; import org.apache.syncope.core.persistence.api.entity.user.UMembership; import org.apache.syncope.core.persistence.api.entity.user.URelationship; import org.apache.syncope.core.persistence.api.entity.user.User; @@ -105,7 +104,6 @@ import org.apache.syncope.core.persistence.jpa.entity.am.JPASAML2IdPEntity; import org.apache.syncope.core.persistence.jpa.entity.am.JPASAML2SPClientApp; import org.apache.syncope.core.persistence.jpa.entity.am.JPAWAConfigEntry; -import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject; @@ -138,7 +136,6 @@ import org.apache.syncope.core.persistence.jpa.entity.task.JPASchedTask; import org.apache.syncope.core.persistence.jpa.entity.user.JPALinkedAccount; import org.apache.syncope.core.persistence.jpa.entity.user.JPASecurityQuestion; -import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership; import org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser; @@ -245,10 +242,8 @@ public E newEntity(final Class reference) { result = (E) new JPASecurityQuestion(); } else if (reference.equals(AuditConf.class)) { result = (E) new JPAAuditConf(); - } else if (reference.equals(ADynGroupMembership.class)) { - result = (E) new JPAADynGroupMembership(); - } else if (reference.equals(UDynGroupMembership.class)) { - result = (E) new JPAUDynGroupMembership(); + } else if (reference.equals(DynGroupMembership.class)) { + result = (E) new JPADynGroupMembership(); } else if (reference.equals(AccessToken.class)) { result = (E) new JPAAccessToken(); } else if (reference.equals(Implementation.class)) { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractGroupableRelatable.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractGroupableRelatable.java index fb642236d43..3115f88f52c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractGroupableRelatable.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractGroupableRelatable.java @@ -37,14 +37,14 @@ public abstract class AbstractGroupableRelatable< @Override public List getPlainAttrs() { - return getPlainAttrsList().stream(). + return plainAttrs().stream(). filter(attr -> attr.getMembership() == null && attr.getRelationship() == null). toList(); } @Override public Optional getPlainAttr(final String plainSchema) { - return getPlainAttrsList().stream(). + return plainAttrs().stream(). filter(attr -> attr.getMembership() == null && attr.getRelationship() == null && plainSchema.equals(attr.getSchema())). findFirst(); @@ -52,7 +52,7 @@ public Optional getPlainAttr(final String plainSchema) { @Override public Optional getPlainAttr(final String plainSchema, final Membership membership) { - return getPlainAttrsList().stream(). + return plainAttrs().stream(). filter(attr -> plainSchema.equals(attr.getSchema()) && membership.getKey().equals(attr.getMembership())). findFirst(); @@ -60,7 +60,7 @@ public Optional getPlainAttr(final String plainSchema, final Membersh @Override public List getPlainAttrs(final Membership membership) { - return getPlainAttrsList().stream(). + return plainAttrs().stream(). filter(attr -> membership.getKey().equals(attr.getMembership())). toList(); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractRelatable.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractRelatable.java index 06ceb2973f2..3401490a0b0 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractRelatable.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractRelatable.java @@ -34,9 +34,11 @@ public abstract class AbstractRelatable< private static final long serialVersionUID = -2269285197388729673L; + protected abstract List plainAttrs(); + @Override public Optional getPlainAttr(final String plainSchema, final Relationship relationship) { - return getPlainAttrsList().stream(). + return plainAttrs().stream(). filter(attr -> plainSchema.equals(attr.getSchema()) && relationship.getKey().equals(attr.getRelationship())). findFirst(); @@ -44,7 +46,7 @@ public Optional getPlainAttr(final String plainSchema, final Relation @Override public List getPlainAttrs(final Relationship relationship) { - return getPlainAttrsList().stream(). + return plainAttrs().stream(). filter(attr -> relationship.getKey().equals(attr.getRelationship())). toList(); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractSchema.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractSchema.java index 8c29dd5e56a..43b83375eaa 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractSchema.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractSchema.java @@ -18,26 +18,20 @@ */ package org.apache.syncope.core.persistence.jpa.entity; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.Cacheable; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.Inheritance; import jakarta.persistence.InheritanceType; import jakarta.persistence.Lob; -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Optional; import org.apache.syncope.core.persistence.api.entity.Schema; import org.apache.syncope.core.persistence.common.validation.SchemaKeyCheck; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.persistence.jpa.converters.Locale2StringMapConverter; @Entity @Inheritance(strategy = InheritanceType.JOINED) @@ -50,49 +44,17 @@ public abstract class AbstractSchema extends AbstractProvidedKeyEntity implement public static final String TABLE = "SyncopeSchema"; - protected static final TypeReference> LABEL_TYPEREF = - new TypeReference>() { - }; - + @Convert(converter = Locale2StringMapConverter.class) @Lob - private String labels; - - @Transient - private Map labelMap = new HashMap<>(); + private Map labels = new HashMap<>(); @Override public Optional getLabel(final Locale locale) { - return Optional.ofNullable(labelMap.get(locale)); + return Optional.ofNullable(labels.get(locale)); } @Override public Map getLabels() { - return labelMap; - } - - protected void json2map(final boolean clearFirst) { - if (clearFirst) { - getLabels().clear(); - } - if (labels != null) { - getLabels().putAll(POJOHelper.deserialize(labels, LABEL_TYPEREF)); - } - } - - @PostLoad - public void postLoad() { - json2map(false); - } - - @PostPersist - @PostUpdate - public void postSave() { - json2map(true); - } - - @PrePersist - @PreUpdate - public void map2json() { - labels = POJOHelper.serialize(getLabels()); + return labels; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/EntityValidationListener.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/EntityValidationListener.java similarity index 96% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/EntityValidationListener.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/EntityValidationListener.java index 20f31a6b129..af2b0f68966 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/EntityValidationListener.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/EntityValidationListener.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.openjpa; +package org.apache.syncope.core.persistence.jpa.entity; import jakarta.persistence.PrePersist; import jakarta.persistence.PreUpdate; @@ -38,7 +38,7 @@ import org.slf4j.LoggerFactory; /** - * JPA validation listener implementing bean validation. + * JPA entity listener triggering bean validation. */ public class EntityValidationListener { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAuditEvent.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAuditEvent.java index 576168ae368..c0d0a15eee4 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAuditEvent.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAuditEvent.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.jpa.entity; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Lob; @@ -29,6 +28,7 @@ import java.util.Optional; import org.apache.syncope.core.persistence.api.entity.AuditEvent; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import tools.jackson.core.type.TypeReference; @Entity @Table(name = JPAAuditEvent.TABLE) @@ -51,8 +51,7 @@ public class JPAAuditEvent extends AbstractGeneratedKeyEntity implements AuditEv private OffsetDateTime when; @Lob - @Column(name = "before_value") - private String before; + private String beforeValue; @Lob private String inputs; @@ -95,12 +94,12 @@ public void setWhen(final OffsetDateTime when) { @Override public String getBefore() { - return before; + return beforeValue; } @Override public void setBefore(final String before) { - this.before = before; + this.beforeValue = before; } @Override diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java index 0b397b3ae19..05ecac65e64 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java @@ -18,19 +18,12 @@ */ package org.apache.syncope.core.persistence.jpa.entity; -import com.fasterxml.jackson.core.type.TypeReference; -import jakarta.persistence.CascadeType; import jakarta.persistence.Column; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.Lob; import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; @@ -38,15 +31,15 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.syncope.common.lib.types.ConnPoolConf; import org.apache.syncope.common.lib.types.ConnectorCapability; import org.apache.syncope.core.persistence.api.entity.ConnInstance; -import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.common.validation.ConnInstanceCheck; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.persistence.jpa.converters.ConnConfPropertyListConverter; +import org.apache.syncope.core.persistence.jpa.converters.ConnPoolConfConverter; +import org.apache.syncope.core.persistence.jpa.converters.ConnectorCapabilitySetConverter; @Entity @Table(name = JPAConnInstance.TABLE) @@ -57,14 +50,6 @@ public class JPAConnInstance extends AbstractGeneratedKeyEntity implements ConnI public static final String TABLE = "ConnInstance"; - protected static final TypeReference> CONNECTOR_CAPABILITY_TYPEREF = - new TypeReference>() { - }; - - protected static final TypeReference> CONN_CONF_PROPS_TYPEREF = - new TypeReference>() { - }; - @ManyToOne(fetch = FetchType.EAGER, optional = false) private JPARealm adminRealm; @@ -92,10 +77,9 @@ public class JPAConnInstance extends AbstractGeneratedKeyEntity implements ConnI @NotNull private String version; + @Convert(converter = ConnectorCapabilitySetConverter.class) @Lob - private String capabilities; - - private final Set capabilitiesSet = new HashSet<>(); + private Set capabilities = new HashSet<>(); /** * The main configuration for the connector instance. This is directly implemented by the Configuration bean class @@ -103,18 +87,13 @@ public class JPAConnInstance extends AbstractGeneratedKeyEntity implements ConnI * * @see org.identityconnectors.framework.api.ConfigurationProperty */ + @Convert(converter = ConnConfPropertyListConverter.class) @Lob - private String jsonConf; + private List jsonConf = new ArrayList<>(); @Column(unique = true) private String displayName; - /** - * External resources associated to the connector. - */ - @OneToMany(cascade = { CascadeType.ALL }, mappedBy = "connector") - private List resources = new ArrayList<>(); - /** * Connection request timeout. * It is not applied in case of sync, full reconciliation and search. @@ -122,7 +101,9 @@ public class JPAConnInstance extends AbstractGeneratedKeyEntity implements ConnI */ private Integer connRequestTimeout = DEFAULT_TIMEOUT; - private String poolConf; + @Convert(converter = ConnPoolConfConverter.class) + @Lob + private ConnPoolConf poolConf; @Override public Realm getAdminRealm() { @@ -177,14 +158,7 @@ public void setVersion(final String version) { @Override public List getConf() { - return StringUtils.isNotBlank(jsonConf) - ? POJOHelper.deserialize(jsonConf, CONN_CONF_PROPS_TYPEREF) - : new ArrayList<>(); - } - - @Override - public void setConf(final List conf) { - jsonConf = POJOHelper.serialize(conf); + return jsonConf; } @Override @@ -197,20 +171,9 @@ public void setDisplayName(final String displayName) { this.displayName = displayName; } - @Override - public List getResources() { - return resources; - } - - @Override - public boolean add(final ExternalResource resource) { - checkType(resource, JPAExternalResource.class); - return resources.contains((JPAExternalResource) resource) || resources.add((JPAExternalResource) resource); - } - @Override public Set getCapabilities() { - return capabilitiesSet; + return capabilities; } @Override @@ -225,37 +188,11 @@ public void setConnRequestTimeout(final Integer timeout) { @Override public ConnPoolConf getPoolConf() { - return Optional.ofNullable(poolConf).map(pc -> POJOHelper.deserialize(pc, ConnPoolConf.class)).orElse(null); + return poolConf; } @Override public void setPoolConf(final ConnPoolConf poolConf) { - this.poolConf = Optional.ofNullable(poolConf).map(POJOHelper::serialize).orElse(null); - } - - protected void json2list(final boolean clearFirst) { - if (clearFirst) { - getCapabilities().clear(); - } - if (capabilities != null) { - getCapabilities().addAll(POJOHelper.deserialize(capabilities, CONNECTOR_CAPABILITY_TYPEREF)); - } - } - - @PostLoad - public void postLoad() { - json2list(false); - } - - @PostPersist - @PostUpdate - public void postSave() { - json2list(true); - } - - @PrePersist - @PreUpdate - public void list2json() { - capabilities = POJOHelper.serialize(getCapabilities()); + this.poolConf = poolConf; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADerSchema.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADerSchema.java index 9df4d2f857a..3debdd7c2ed 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADerSchema.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADerSchema.java @@ -20,7 +20,7 @@ import jakarta.persistence.Entity; import jakarta.persistence.FetchType; -import jakarta.persistence.OneToOne; +import jakarta.persistence.ManyToOne; import jakarta.persistence.PrimaryKeyJoinColumn; import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; @@ -37,7 +37,7 @@ public class JPADerSchema extends AbstractSchema implements DerSchema { public static final String TABLE = "DerSchema"; - @OneToOne(fetch = FetchType.EAGER) + @ManyToOne(fetch = FetchType.EAGER) private JPAAnyTypeClass anyTypeClass; @NotNull diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAADynGroupMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynGroupMembership.java similarity index 64% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAADynGroupMembership.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynGroupMembership.java index 83857fcf3ef..364656a9a35 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAADynGroupMembership.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynGroupMembership.java @@ -16,43 +16,43 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.entity.anyobject; +package org.apache.syncope.core.persistence.jpa.entity; import jakarta.persistence.Entity; import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToOne; import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; import org.apache.syncope.core.persistence.api.entity.AnyType; -import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; -import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; import org.apache.syncope.core.persistence.api.entity.group.Group; -import org.apache.syncope.core.persistence.jpa.entity.AbstractDynMembership; -import org.apache.syncope.core.persistence.jpa.entity.JPAAnyType; import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup; @Entity -@Table(name = JPAADynGroupMembership.TABLE) -public class JPAADynGroupMembership extends AbstractDynMembership implements ADynGroupMembership { +@Table(name = JPADynGroupMembership.TABLE) +public class JPADynGroupMembership extends AbstractGeneratedKeyEntity implements DynGroupMembership { - private static final long serialVersionUID = -7336814163949640354L; + private static final long serialVersionUID = 8157856850557493134L; - public static final String TABLE = "ADynGroupMembership"; + public static final String TABLE = "DynGroupMembership"; - @OneToOne + @ManyToOne private JPAGroup group; @ManyToOne private JPAAnyType anyType; + @NotNull + private String fiql; + @Override public Group getGroup() { return group; } @Override - public void setGroup(final Group role) { - checkType(role, JPAGroup.class); - this.group = (JPAGroup) role; + public void setGroup(final Group group) { + checkType(group, JPAGroup.class); + this.group = (JPAGroup) group; } @Override @@ -65,4 +65,14 @@ public void setAnyType(final AnyType anyType) { checkType(anyType, JPAAnyType.class); this.anyType = (JPAAnyType) anyType; } + + @Override + public String getFIQLCond() { + return fiql; + } + + @Override + public void setFIQLCond(final String fiql) { + this.fiql = fiql; + } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealmMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealmMembership.java index 0996ef80878..997f0b4c113 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealmMembership.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealmMembership.java @@ -20,7 +20,6 @@ import jakarta.persistence.Entity; import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; import org.apache.syncope.core.persistence.api.entity.AnyType; @@ -35,7 +34,7 @@ public class JPADynRealmMembership extends AbstractGeneratedKeyEntity implements public static final String TABLE = "DynRealmMembership"; - @OneToOne + @ManyToOne private JPADynRealm dynRealm; @ManyToOne diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAExternalResource.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAExternalResource.java index f81d7987dce..acc66a91c6d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAExternalResource.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAExternalResource.java @@ -18,9 +18,9 @@ */ package org.apache.syncope.core.persistence.jpa.entity; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.Cacheable; import jakarta.persistence.CascadeType; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; @@ -30,20 +30,16 @@ import jakarta.persistence.Lob; import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.persistence.UniqueConstraint; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; -import org.apache.commons.lang3.StringUtils; +import java.util.function.Predicate; import org.apache.syncope.common.lib.to.OrgUnit; import org.apache.syncope.common.lib.to.Provision; import org.apache.syncope.common.lib.types.ConnConfProperty; @@ -59,12 +55,15 @@ import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; import org.apache.syncope.core.persistence.common.validation.ExternalResourceCheck; +import org.apache.syncope.core.persistence.jpa.converters.ConnConfPropertyListConverter; +import org.apache.syncope.core.persistence.jpa.converters.ConnectorCapabilitySetConverter; +import org.apache.syncope.core.persistence.jpa.converters.OrgUnitConverter; +import org.apache.syncope.core.persistence.jpa.converters.ProvisionListConverter; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAInboundPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPropagationPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushPolicy; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity @Table(name = JPAExternalResource.TABLE) @@ -76,18 +75,6 @@ public class JPAExternalResource extends AbstractProvidedKeyEntity implements Ex public static final String TABLE = "ExternalResource"; - protected static final TypeReference> CONN_CONF_PROPS_TYPEREF = - new TypeReference>() { - }; - - protected static final TypeReference> CONNECTOR_CAPABILITY_TYPEREF = - new TypeReference>() { - }; - - protected static final TypeReference> PROVISION_TYPEREF = - new TypeReference>() { - }; - /** * Should this resource enforce the mandatory constraints? */ @@ -142,20 +129,21 @@ public class JPAExternalResource extends AbstractProvidedKeyEntity implements Ex /** * Configuration properties that are override from the connector instance. */ + @Convert(converter = ConnConfPropertyListConverter.class) @Lob - private String jsonConf; + private List jsonConf = new ArrayList<>(); + @Convert(converter = ConnectorCapabilitySetConverter.class) @Lob - private String capabilitiesOverride; + private Set capabilitiesOverride = new HashSet<>(); + @Convert(converter = ProvisionListConverter.class) @Lob - private String provisions; - - @Transient - private final List provisionList = new ArrayList<>(); + private List provisions = new ArrayList<>(); + @Convert(converter = OrgUnitConverter.class) @Lob - private String orgUnit; + private OrgUnit orgUnit; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = TABLE + "PropAction", @@ -200,17 +188,17 @@ public Optional getProvisionByObjectClass(final String objectClass) { @Override public List getProvisions() { - return provisionList; + return provisions; } @Override public OrgUnit getOrgUnit() { - return Optional.ofNullable(orgUnit).map(ou -> POJOHelper.deserialize(ou, OrgUnit.class)).orElse(null); + return orgUnit; } @Override public void setOrgUnit(final OrgUnit orgUnit) { - this.orgUnit = orgUnit == null ? null : POJOHelper.serialize(orgUnit); + this.orgUnit = orgUnit; } @Override @@ -333,30 +321,22 @@ public void setProvisionSorter(final Implementation provisionSorter) { @Override public Optional> getConfOverride() { - return StringUtils.isBlank(jsonConf) - ? Optional.empty() - : Optional.of(POJOHelper.deserialize(jsonConf, CONN_CONF_PROPS_TYPEREF)); + return Optional.ofNullable(jsonConf).filter(Predicate.not(Collection::isEmpty)); } @Override public void setConfOverride(final Optional> confOverride) { - confOverride.ifPresentOrElse( - conf -> jsonConf = POJOHelper.serialize(conf), - () -> jsonConf = null); + jsonConf = confOverride.orElse(null); } @Override public Optional> getCapabilitiesOverride() { - return StringUtils.isBlank(capabilitiesOverride) - ? Optional.empty() - : Optional.of(POJOHelper.deserialize(capabilitiesOverride, CONNECTOR_CAPABILITY_TYPEREF)); + return Optional.ofNullable(capabilitiesOverride).filter(Predicate.not(Collection::isEmpty)); } @Override public void setCapabilitiesOverride(final Optional> capabilitiesOverride) { - capabilitiesOverride.ifPresentOrElse( - override -> this.capabilitiesOverride = POJOHelper.serialize(override), - () -> this.capabilitiesOverride = null); + this.capabilitiesOverride = capabilitiesOverride.orElse(null); } @Override @@ -371,30 +351,4 @@ public boolean add(final Implementation propagationAction) { public List getPropagationActions() { return propagationActions; } - - protected void json2list(final boolean clearFirst) { - if (clearFirst) { - getProvisions().clear(); - } - if (provisions != null) { - getProvisions().addAll(POJOHelper.deserialize(provisions, PROVISION_TYPEREF)); - } - } - - @PostLoad - public void postLoad() { - json2list(false); - } - - @PostPersist - @PostUpdate - public void postSave() { - json2list(true); - } - - @PrePersist - @PreUpdate - public void list2json() { - provisions = POJOHelper.serialize(getProvisions()); - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANotification.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANotification.java index 4da8e1d2ba4..734a1b0d586 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANotification.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANotification.java @@ -18,8 +18,8 @@ */ package org.apache.syncope.core.persistence.jpa.entity; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.CascadeType; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; @@ -28,13 +28,7 @@ import jakarta.persistence.Lob; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; @@ -46,7 +40,7 @@ import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.MailTemplate; import org.apache.syncope.core.persistence.api.entity.Notification; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.persistence.jpa.converters.StringListConverter; @Entity @Table(name = JPANotification.TABLE) @@ -56,25 +50,18 @@ public class JPANotification extends AbstractGeneratedKeyEntity implements Notif public static final String TABLE = "Notification"; - protected static final TypeReference> TYPEREF = new TypeReference>() { - }; - + @Convert(converter = StringListConverter.class) @Lob - private String events; - - @Transient - private List eventsList = new ArrayList<>(); + private List events = new ArrayList<>(); @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "notification") private List abouts = new ArrayList<>(); private String recipientsFIQL; + @Convert(converter = StringListConverter.class) @Lob - private String staticRecipients; - - @Transient - private List staticRecipientsList = new ArrayList<>(); + private List staticRecipients = new ArrayList<>(); @NotNull private String recipientAttrName; @@ -136,7 +123,7 @@ public void setRecipientsProvider(final Implementation recipientsProvider) { @Override public List getEvents() { - return eventsList; + return events; } @Override @@ -157,7 +144,7 @@ public List getAbouts() { @Override public List getStaticRecipients() { - return staticRecipientsList; + return staticRecipients; } @Override @@ -221,35 +208,4 @@ public boolean isActive() { public void setActive(final boolean active) { this.active = active; } - - protected void json2list(final boolean clearFirst) { - if (clearFirst) { - getEvents().clear(); - getStaticRecipients().clear(); - } - if (events != null) { - getEvents().addAll(POJOHelper.deserialize(events, TYPEREF)); - } - if (staticRecipients != null) { - getStaticRecipients().addAll(POJOHelper.deserialize(staticRecipients, TYPEREF)); - } - } - - @PostLoad - public void postLoad() { - json2list(false); - } - - @PostPersist - @PostUpdate - public void postSave() { - json2list(true); - } - - @PrePersist - @PreUpdate - public void list2json() { - events = POJOHelper.serialize(getEvents()); - staticRecipients = POJOHelper.serialize(getStaticRecipients()); - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java index 0522780bfcb..96c7bd91283 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java @@ -18,23 +18,16 @@ */ package org.apache.syncope.core.persistence.jpa.entity; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.Column; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.FetchType; import jakarta.persistence.Lob; import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToOne; -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; import jakarta.persistence.PrimaryKeyJoinColumn; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.validation.constraints.NotNull; import java.util.HashMap; import java.util.Map; @@ -45,7 +38,8 @@ import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.common.validation.PlainSchemaCheck; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.persistence.jpa.converters.String2StringMapConverter; +import tools.jackson.core.type.TypeReference; @Entity @Table(name = JPAPlainSchema.TABLE) @@ -61,7 +55,7 @@ public class JPAPlainSchema extends AbstractSchema implements PlainSchema { new TypeReference>() { }; - @OneToOne(fetch = FetchType.EAGER) + @ManyToOne(fetch = FetchType.EAGER) private JPAAnyTypeClass anyTypeClass; @NotNull @@ -80,11 +74,9 @@ public class JPAPlainSchema extends AbstractSchema implements PlainSchema { @Column(nullable = true) private String conversionPattern; + @Convert(converter = String2StringMapConverter.class) @Lob - private String enumValues; - - @Transient - private Map enumValuesMap = new HashMap<>(); + private Map enumValues = new HashMap<>(); @ManyToOne private JPAImplementation dropdownValueProvider; @@ -229,39 +221,6 @@ public void setMimeType(final String mimeType) { @Override public Map getEnumValues() { - return enumValuesMap; - } - - @Override - protected void json2map(final boolean clearFirst) { - super.json2map(clearFirst); - - if (clearFirst) { - getEnumValues().clear(); - } - if (enumValues != null) { - getEnumValues().putAll(POJOHelper.deserialize(enumValues, ENUMVALUES_TYPEREF)); - } - } - - @PostLoad - @Override - public void postLoad() { - json2map(false); - } - - @PostPersist - @PostUpdate - @Override - public void postSave() { - json2map(true); - } - - @PrePersist - @PreUpdate - @Override - public void map2json() { - super.map2json(); - enumValues = POJOHelper.serialize(getEnumValues()); + return enumValues; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java index 1ce36dd8c06..00624f44a25 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java @@ -21,8 +21,8 @@ import jakarta.persistence.Cacheable; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; -import jakarta.persistence.EntityListeners; import jakarta.persistence.FetchType; import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinTable; @@ -30,7 +30,6 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.persistence.UniqueConstraint; import jakarta.validation.constraints.Size; import java.util.ArrayList; @@ -53,6 +52,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; import org.apache.syncope.core.persistence.common.validation.RealmCheck; +import org.apache.syncope.core.persistence.jpa.converters.PlainAttrListConverter; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccessPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAttrReleasePolicy; @@ -63,7 +63,6 @@ @Entity @Table(name = JPARealm.TABLE, uniqueConstraints = @UniqueConstraint(columnNames = { "name", "parent_id" })) -@EntityListeners({ JSONRealmListener.class }) @Cacheable @RealmCheck public class JPARealm extends AbstractAttributable implements Realm { @@ -81,10 +80,8 @@ public class JPARealm extends AbstractAttributable implements Realm { @Column(nullable = false, unique = true) private String fullPath; - private String plainAttrs; - - @Transient - private final List plainAttrsList = new ArrayList<>(); + @Convert(converter = PlainAttrListConverter.class) + private final List plainAttrs = new ArrayList<>(); @ManyToMany @JoinTable(joinColumns = @@ -163,41 +160,26 @@ public void setFullPath(final String fullPath) { this.fullPath = fullPath; } - @Override - public List getPlainAttrsList() { - return plainAttrsList; - } - - @Override - public String getPlainAttrsJSON() { - return plainAttrs; - } - - @Override - public void setPlainAttrsJSON(final String plainAttrs) { - this.plainAttrs = plainAttrs; - } - @Override public boolean add(final PlainAttr attr) { - return plainAttrsList.add(attr); + return plainAttrs.add(attr); } @Override public boolean remove(final PlainAttr attr) { - return plainAttrsList.removeIf(a -> a.getSchema().equals(attr.getSchema())); + return plainAttrs.removeIf(a -> a.getSchema().equals(attr.getSchema())); } @Override public Optional getPlainAttr(final String plainSchema) { - return plainAttrsList.stream(). + return plainAttrs.stream(). filter(attr -> plainSchema.equals(attr.getSchema())). findFirst(); } @Override public List getPlainAttrs() { - return plainAttrsList.stream().toList(); + return plainAttrs; } @Override diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java index 1c5df1f1428..bec49cc8a41 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java @@ -22,8 +22,8 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; +import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; -import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; @@ -43,7 +43,7 @@ public class JPAReport extends AbstractGeneratedKeyEntity implements Report { public static final String TABLE = "Report"; - @OneToOne(optional = false) + @ManyToOne(optional = false) private JPAImplementation jobDelegate; @Column(unique = true, nullable = false) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java index 4217566456e..2bab8d3e0bd 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java @@ -18,21 +18,15 @@ */ package org.apache.syncope.core.persistence.jpa.entity; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.Cacheable; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinTable; import jakarta.persistence.Lob; import jakarta.persistence.ManyToMany; -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.persistence.UniqueConstraint; import jakarta.validation.Valid; import java.util.ArrayList; @@ -42,7 +36,7 @@ import org.apache.syncope.core.persistence.api.entity.DynRealm; import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.entity.Role; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.persistence.jpa.converters.StringSetConverter; @Entity @Table(name = JPARole.TABLE) @@ -53,14 +47,9 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role { public static final String TABLE = "SyncopeRole"; - protected static final TypeReference> TYPEREF = new TypeReference>() { - }; - + @Convert(converter = StringSetConverter.class) @Lob - private String entitlements; - - @Transient - private Set entitlementsSet = new HashSet<>(); + private Set entitlements = new HashSet<>(); private String dynMembershipCond; @@ -89,7 +78,7 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role { @Override public Set getEntitlements() { - return entitlementsSet; + return entitlements; } @Override @@ -133,30 +122,4 @@ public String getAnyLayout() { public void setAnyLayout(final String anyLayout) { this.anyLayout = anyLayout; } - - protected void json2list(final boolean clearFirst) { - if (clearFirst) { - getEntitlements().clear(); - } - if (entitlements != null) { - getEntitlements().addAll(POJOHelper.deserialize(entitlements, TYPEREF)); - } - } - - @PostLoad - public void postLoad() { - json2list(false); - } - - @PostPersist - @PostUpdate - public void postSave() { - json2list(true); - } - - @PrePersist - @PreUpdate - public void list2json() { - entitlements = POJOHelper.serialize(getEntitlements()); - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JSONRealmListener.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JSONRealmListener.java deleted file mode 100644 index 5afda772f44..00000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JSONRealmListener.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.entity; - -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; -import org.apache.syncope.core.persistence.api.entity.Realm; - -public class JSONRealmListener extends JSONEntityListener { - - @PostLoad - public void read(final JPARealm realm) { - super.json2list(realm, false); - } - - @PrePersist - @PreUpdate - public void save(final JPARealm realm) { - realm.list2json(); - } - - @PostPersist - @PostUpdate - public void readAfterSave(final JPARealm realm) { - super.json2list(realm, true); - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java index 59d89b43628..313a3f93f96 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java @@ -18,8 +18,8 @@ */ package org.apache.syncope.core.persistence.jpa.entity.am; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.Column; +import jakarta.persistence.Convert; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.FetchType; @@ -28,7 +28,6 @@ import jakarta.persistence.MappedSuperclass; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import org.apache.syncope.common.lib.Attr; import org.apache.syncope.common.lib.clientapps.UsernameAttributeProviderConf; import org.apache.syncope.common.lib.types.LogoutType; @@ -38,22 +37,20 @@ import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy; import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; +import org.apache.syncope.core.persistence.jpa.converters.AttrListConverter; +import org.apache.syncope.core.persistence.jpa.converters.UsernameAttributeProviderConfConverter; import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity; import org.apache.syncope.core.persistence.jpa.entity.JPARealm; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccessPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAttrReleasePolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAuthPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPATicketExpirationPolicy; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @MappedSuperclass public class AbstractClientApp extends AbstractGeneratedKeyEntity implements ClientApp { private static final long serialVersionUID = 7422422526695279794L; - protected static final TypeReference> ATTR_TYPEREF = new TypeReference>() { - }; - @Column(unique = true, nullable = false) private String name; @@ -66,8 +63,9 @@ public class AbstractClientApp extends AbstractGeneratedKeyEntity implements Cli private String logo; + @Convert(converter = UsernameAttributeProviderConfConverter.class) @Lob - private String usernameAttributeProviderConf; + private UsernameAttributeProviderConf usernameAttributeProviderConf; private String theme; @@ -90,8 +88,9 @@ public class AbstractClientApp extends AbstractGeneratedKeyEntity implements Cli @ManyToOne(fetch = FetchType.EAGER) private JPATicketExpirationPolicy ticketExpirationPolicy; + @Convert(converter = AttrListConverter.class) @Lob - private String properties; + private List properties = new ArrayList<>(); @Enumerated(EnumType.STRING) private LogoutType logoutType; @@ -178,13 +177,12 @@ public void setPrivacyUrl(final String privacyUrl) { @Override public UsernameAttributeProviderConf getUsernameAttributeProviderConf() { - return Optional.ofNullable(usernameAttributeProviderConf). - map(conf -> POJOHelper.deserialize(conf, UsernameAttributeProviderConf.class)).orElse(null); + return usernameAttributeProviderConf; } @Override public void setUsernameAttributeProviderConf(final UsernameAttributeProviderConf conf) { - this.usernameAttributeProviderConf = conf == null ? null : POJOHelper.serialize(conf); + usernameAttributeProviderConf = conf; } @Override @@ -244,14 +242,12 @@ public void setRealm(final Realm realm) { @Override public List getProperties() { - return properties == null - ? new ArrayList<>(0) - : POJOHelper.deserialize(properties, ATTR_TYPEREF); + return properties; } @Override public void setProperties(final List properties) { - this.properties = POJOHelper.serialize(properties); + this.properties = properties; } @Override diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAttrRepo.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAttrRepo.java index b44e8c119fb..42707c334a8 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAttrRepo.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAttrRepo.java @@ -18,29 +18,23 @@ */ package org.apache.syncope.core.persistence.jpa.entity.am; -import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.Lob; -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.attr.AttrRepoConf; import org.apache.syncope.common.lib.to.Item; import org.apache.syncope.common.lib.types.AttrRepoState; import org.apache.syncope.core.persistence.api.entity.am.AttrRepo; +import org.apache.syncope.core.persistence.jpa.converters.AttrRepoConfConverter; +import org.apache.syncope.core.persistence.jpa.converters.ItemListConverter; import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity @Table(name = JPAAttrRepo.TABLE) @@ -50,9 +44,6 @@ public class JPAAttrRepo extends AbstractProvidedKeyEntity implements AttrRepo { public static final String TABLE = "AttrRepo"; - protected static final TypeReference> TYPEREF = new TypeReference>() { - }; - private String description; @Enumerated(EnumType.STRING) @@ -62,14 +53,13 @@ public class JPAAttrRepo extends AbstractProvidedKeyEntity implements AttrRepo { @NotNull private Integer attrRepoOrder = 0; + @Convert(converter = ItemListConverter.class) @Lob - private String items; - - @Transient - private final List itemList = new ArrayList<>(); + private List items = new ArrayList<>(); + @Convert(converter = AttrRepoConfConverter.class) @Lob - private String jsonConf; + private AttrRepoConf jsonConf; @Override public String getDescription() { @@ -103,47 +93,16 @@ public void setOrder(final int order) { @Override public List getItems() { - return itemList; + return items; } @Override public AttrRepoConf getConf() { - AttrRepoConf conf = null; - if (!StringUtils.isBlank(jsonConf)) { - conf = POJOHelper.deserialize(jsonConf, AttrRepoConf.class); - } - - return conf; + return jsonConf; } @Override public void setConf(final AttrRepoConf conf) { - jsonConf = POJOHelper.serialize(conf); - } - - protected void json2list(final boolean clearFirst) { - if (clearFirst) { - getItems().clear(); - } - if (items != null) { - getItems().addAll(POJOHelper.deserialize(items, TYPEREF)); - } - } - - @PostLoad - public void postLoad() { - json2list(false); - } - - @PostPersist - @PostUpdate - public void postSave() { - json2list(true); - } - - @PrePersist - @PreUpdate - public void list2json() { - items = POJOHelper.serialize(getItems()); + jsonConf = conf; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthModule.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthModule.java index bc3ac02516e..3c850b136bd 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthModule.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthModule.java @@ -18,29 +18,23 @@ */ package org.apache.syncope.core.persistence.jpa.entity.am; -import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.Lob; -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.auth.AuthModuleConf; import org.apache.syncope.common.lib.to.Item; import org.apache.syncope.common.lib.types.AuthModuleState; import org.apache.syncope.core.persistence.api.entity.am.AuthModule; +import org.apache.syncope.core.persistence.jpa.converters.AuthModuleConfConverter; +import org.apache.syncope.core.persistence.jpa.converters.ItemListConverter; import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity @Table(name = JPAAuthModule.TABLE) @@ -50,9 +44,6 @@ public class JPAAuthModule extends AbstractProvidedKeyEntity implements AuthModu public static final String TABLE = "AuthModule"; - protected static final TypeReference> TYPEREF = new TypeReference>() { - }; - private String description; @Enumerated(EnumType.STRING) @@ -62,14 +53,13 @@ public class JPAAuthModule extends AbstractProvidedKeyEntity implements AuthModu @NotNull private Integer authModuleOrder = 0; + @Convert(converter = ItemListConverter.class) @Lob - private String items; - - @Transient - private final List itemList = new ArrayList<>(); + private List items = new ArrayList<>(); + @Convert(converter = AuthModuleConfConverter.class) @Lob - private String jsonConf; + private AuthModuleConf jsonConf; @Override public String getDescription() { @@ -103,47 +93,16 @@ public void setOrder(final int order) { @Override public List getItems() { - return itemList; + return items; } @Override public AuthModuleConf getConf() { - AuthModuleConf conf = null; - if (!StringUtils.isBlank(jsonConf)) { - conf = POJOHelper.deserialize(jsonConf, AuthModuleConf.class); - } - - return conf; + return jsonConf; } @Override public void setConf(final AuthModuleConf conf) { - jsonConf = POJOHelper.serialize(conf); - } - - protected void json2list(final boolean clearFirst) { - if (clearFirst) { - getItems().clear(); - } - if (items != null) { - getItems().addAll(POJOHelper.deserialize(items, TYPEREF)); - } - } - - @PostLoad - public void postLoad() { - json2list(false); - } - - @PostPersist - @PostUpdate - public void postSave() { - json2list(true); - } - - @PrePersist - @PreUpdate - public void list2json() { - items = POJOHelper.serialize(getItems()); + jsonConf = conf; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthProfile.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthProfile.java index 303436b9c38..993b485f22e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthProfile.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthProfile.java @@ -18,23 +18,26 @@ */ package org.apache.syncope.core.persistence.jpa.entity.am; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.Column; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.Lob; import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount; import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken; import org.apache.syncope.common.lib.wa.ImpersonationAccount; import org.apache.syncope.common.lib.wa.MfaTrustedDevice; import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential; import org.apache.syncope.core.persistence.api.entity.am.AuthProfile; +import org.apache.syncope.core.persistence.jpa.converters.GoogleMfaAuthAccountListConverter; +import org.apache.syncope.core.persistence.jpa.converters.GoogleMfaAuthTokenListConverter; +import org.apache.syncope.core.persistence.jpa.converters.ImpersonationAccountListConverter; +import org.apache.syncope.core.persistence.jpa.converters.MfaTrustedDeviceListConverter; +import org.apache.syncope.core.persistence.jpa.converters.WebAuthnDeviceCredentialListConverter; import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity @Table(name = JPAAuthProfile.TABLE, uniqueConstraints = @@ -45,43 +48,28 @@ public class JPAAuthProfile extends AbstractGeneratedKeyEntity implements AuthPr public static final String TABLE = "AuthProfile"; - protected static final TypeReference> GOOGLE_MFA_TOKENS_TYPEREF = - new TypeReference>() { - }; - - protected static final TypeReference> GOOGLE_MFA_ACCOUNTS_TYPEREF = - new TypeReference>() { - }; - - protected static final TypeReference> MFA_TRUSTED_DEVICE_TYPEREF = - new TypeReference>() { - }; - - protected static final TypeReference> IMPERSONATION_TYPEREF = - new TypeReference>() { - }; - - protected static final TypeReference> WEBAUTHN_TYPEREF = - new TypeReference>() { - }; - @Column(nullable = false) private String owner; + @Convert(converter = ImpersonationAccountListConverter.class) @Lob - private String impersonationAccounts; + private List impersonationAccounts = new ArrayList<>(); + @Convert(converter = GoogleMfaAuthAccountListConverter.class) @Lob - private String googleMfaAuthAccounts; + private List googleMfaAuthAccounts = new ArrayList<>(); + @Convert(converter = GoogleMfaAuthTokenListConverter.class) @Lob - private String googleMfaAuthTokens; + private List googleMfaAuthTokens = new ArrayList<>(); + @Convert(converter = MfaTrustedDeviceListConverter.class) @Lob - private String mfaTrustedDevices; + private List mfaTrustedDevices = new ArrayList<>(); + @Convert(converter = WebAuthnDeviceCredentialListConverter.class) @Lob - private String webAuthnDeviceCredentials; + private List webAuthnDeviceCredentials = new ArrayList<>(); @Override public String getOwner() { @@ -93,58 +81,58 @@ public void setOwner(final String owner) { this.owner = owner; } + @Override + public boolean add(final GoogleMfaAuthToken googleMfaAuthToken) { + return !googleMfaAuthTokens.contains(googleMfaAuthToken) + && googleMfaAuthTokens.add(googleMfaAuthToken); + } + @Override public List getGoogleMfaAuthTokens() { - return Optional.ofNullable(googleMfaAuthTokens). - map(v -> POJOHelper.deserialize(v, GOOGLE_MFA_TOKENS_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); + return googleMfaAuthTokens; } @Override - public void setGoogleMfaAuthTokens(final List tokens) { - googleMfaAuthTokens = POJOHelper.serialize(tokens); + public boolean add(final GoogleMfaAuthAccount googleMfaAuthAccount) { + return !googleMfaAuthAccounts.contains(googleMfaAuthAccount) + && googleMfaAuthAccounts.add(googleMfaAuthAccount); } @Override public List getGoogleMfaAuthAccounts() { - return Optional.ofNullable(googleMfaAuthAccounts). - map(v -> POJOHelper.deserialize(v, GOOGLE_MFA_ACCOUNTS_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); + return googleMfaAuthAccounts; } @Override - public void setGoogleMfaAuthAccounts(final List accounts) { - googleMfaAuthAccounts = POJOHelper.serialize(accounts); + public boolean add(final MfaTrustedDevice mfaTrustedDevice) { + return !mfaTrustedDevices.contains(mfaTrustedDevice) + && mfaTrustedDevices.add(mfaTrustedDevice); } @Override public List getMfaTrustedDevices() { - return Optional.ofNullable(mfaTrustedDevices). - map(v -> POJOHelper.deserialize(v, MFA_TRUSTED_DEVICE_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); + return mfaTrustedDevices; } @Override - public void setMfaTrustedDevices(final List devices) { - mfaTrustedDevices = POJOHelper.serialize(devices); + public boolean add(final ImpersonationAccount impersonationAccount) { + return !impersonationAccounts.contains(impersonationAccount) + && impersonationAccounts.add(impersonationAccount); } @Override public List getImpersonationAccounts() { - return Optional.ofNullable(impersonationAccounts). - map(v -> POJOHelper.deserialize(v, IMPERSONATION_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); + return impersonationAccounts; } @Override - public void setImpersonationAccounts(final List accounts) { - impersonationAccounts = POJOHelper.serialize(accounts); + public boolean add(final WebAuthnDeviceCredential webAuthnDeviceCredential) { + return !webAuthnDeviceCredentials.contains(webAuthnDeviceCredential) + && webAuthnDeviceCredentials.add(webAuthnDeviceCredential); } @Override public List getWebAuthnDeviceCredentials() { - return Optional.ofNullable(webAuthnDeviceCredentials). - map(v -> POJOHelper.deserialize(v, WEBAUTHN_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); - } - - @Override - public void setWebAuthnDeviceCredentials(final List credentials) { - webAuthnDeviceCredentials = POJOHelper.serialize(credentials); + return webAuthnDeviceCredentials; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCJWKS.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCJWKS.java index c900763c240..ea950bfb86b 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCJWKS.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCJWKS.java @@ -18,10 +18,10 @@ */ package org.apache.syncope.core.persistence.jpa.entity.am; -import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Lob; import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; import org.apache.syncope.core.persistence.api.entity.am.OIDCJWKS; import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity; @@ -33,13 +33,13 @@ public class JPAOIDCJWKS extends AbstractGeneratedKeyEntity implements OIDCJWKS private static final long serialVersionUID = 47352617217394093L; - @Column(nullable = false) + @NotNull @Lob private String json; @Override public String getJson() { - return this.json; + return json; } @Override diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java index 0ced79e62d3..dee3259c1c7 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java @@ -18,19 +18,13 @@ */ package org.apache.syncope.core.persistence.jpa.entity.am; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.Column; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.Lob; -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import java.util.HashSet; import java.util.Set; import org.apache.syncope.common.lib.types.OIDCApplicationType; @@ -42,7 +36,9 @@ import org.apache.syncope.common.lib.types.OIDCTokenEncryptionEncoding; import org.apache.syncope.common.lib.types.OIDCTokenSigningAlg; import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.persistence.jpa.converters.OIDCGrantTypeSetConverter; +import org.apache.syncope.core.persistence.jpa.converters.OIDCResponseTypeSetConverter; +import org.apache.syncope.core.persistence.jpa.converters.StringSetConverter; @Entity @Table(name = JPAOIDCRPClientApp.TABLE) @@ -52,21 +48,6 @@ public class JPAOIDCRPClientApp extends AbstractClientApp implements OIDCRPClien public static final String TABLE = "OIDCRPClientApp"; - protected static final TypeReference> STRING_TYPEREF = new TypeReference>() { - }; - - protected static final TypeReference> GRANT_TYPE_TYPEREF = - new TypeReference>() { - }; - - protected static final TypeReference> RESPONSE_TYPE_TYPEREF = - new TypeReference>() { - }; - - protected static final TypeReference> SCOPE_TYPEREF = - new TypeReference>() { - }; - @Column(unique = true, nullable = false) private String clientId; @@ -108,29 +89,21 @@ public class JPAOIDCRPClientApp extends AbstractClientApp implements OIDCRPClien @Enumerated(EnumType.STRING) private OIDCApplicationType applicationType = OIDCApplicationType.WEB; + @Convert(converter = StringSetConverter.class) @Lob - private String redirectUris; - - @Transient - private Set redirectUrisSet = new HashSet<>(); + private Set redirectUris = new HashSet<>(); + @Convert(converter = OIDCGrantTypeSetConverter.class) @Lob - private String supportedGrantTypes; - - @Transient - private Set supportedGrantTypesSet = new HashSet<>(); + private Set supportedGrantTypes = new HashSet<>(); + @Convert(converter = OIDCResponseTypeSetConverter.class) @Lob - private String supportedResponseTypes; - - @Transient - private Set supportedResponseTypesSet = new HashSet<>(); + private Set supportedResponseTypes = new HashSet<>(); + @Convert(converter = StringSetConverter.class) @Lob - private String scopes; - - @Transient - private Set scopesSet = new HashSet<>(); + private Set scopes = new HashSet<>(); @Lob private String jwks; @@ -144,7 +117,7 @@ public class JPAOIDCRPClientApp extends AbstractClientApp implements OIDCRPClien @Override public Set getRedirectUris() { - return redirectUrisSet; + return redirectUris; } @Override @@ -309,17 +282,17 @@ public void setApplicationType(final OIDCApplicationType applicationType) { @Override public Set getSupportedGrantTypes() { - return supportedGrantTypesSet; + return supportedGrantTypes; } @Override public Set getSupportedResponseTypes() { - return supportedResponseTypesSet; + return supportedResponseTypes; } @Override public Set getScopes() { - return scopesSet; + return scopes; } @Override @@ -363,44 +336,4 @@ public String getLogoutUri() { public void setLogoutUri(final String logoutUri) { this.logoutUri = logoutUri; } - - protected void json2list(final boolean clearFirst) { - if (clearFirst) { - getRedirectUris().clear(); - getSupportedGrantTypes().clear(); - getSupportedResponseTypes().clear(); - } - if (redirectUris != null) { - getRedirectUris().addAll(POJOHelper.deserialize(redirectUris, STRING_TYPEREF)); - } - if (supportedGrantTypes != null) { - getSupportedGrantTypes().addAll(POJOHelper.deserialize(supportedGrantTypes, GRANT_TYPE_TYPEREF)); - } - if (supportedResponseTypes != null) { - getSupportedResponseTypes().addAll(POJOHelper.deserialize(supportedResponseTypes, RESPONSE_TYPE_TYPEREF)); - } - if (scopes != null) { - getScopes().addAll(POJOHelper.deserialize(scopes, SCOPE_TYPEREF)); - } - } - - @PostLoad - public void postLoad() { - json2list(false); - } - - @PostPersist - @PostUpdate - public void postSave() { - json2list(true); - } - - @PrePersist - @PreUpdate - public void list2json() { - redirectUris = POJOHelper.serialize(getRedirectUris()); - supportedGrantTypes = POJOHelper.serialize(getSupportedGrantTypes()); - supportedResponseTypes = POJOHelper.serialize(getSupportedResponseTypes()); - scopes = POJOHelper.serialize(getScopes()); - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAPasswordManagement.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAPasswordManagement.java index 416e16e9703..49737a2f602 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAPasswordManagement.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAPasswordManagement.java @@ -18,16 +18,16 @@ */ package org.apache.syncope.core.persistence.jpa.entity.am; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.Lob; import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; -import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.password.PasswordManagementConf; import org.apache.syncope.core.persistence.api.entity.am.PasswordManagement; import org.apache.syncope.core.persistence.common.validation.PasswordManagementCheck; +import org.apache.syncope.core.persistence.jpa.converters.PasswordManagementConfConverter; import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity @Table(name = JPAPasswordManagement.TABLE) @@ -43,8 +43,9 @@ public class JPAPasswordManagement extends AbstractProvidedKeyEntity implements @NotNull private boolean enabled; + @Convert(converter = PasswordManagementConfConverter.class) @Lob - private String jsonConf; + private PasswordManagementConf jsonConf; @Override public String getDescription() { @@ -69,16 +70,11 @@ public void setEnabled(final boolean enabled) { @Override public PasswordManagementConf getConf() { - PasswordManagementConf conf = null; - if (!StringUtils.isBlank(jsonConf)) { - conf = POJOHelper.deserialize(jsonConf, PasswordManagementConf.class); - } - - return conf; + return jsonConf; } @Override public void setConf(final PasswordManagementConf conf) { - jsonConf = POJOHelper.serialize(conf); + jsonConf = conf; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2SPClientApp.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2SPClientApp.java index af6c17c9375..33347d721c1 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2SPClientApp.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2SPClientApp.java @@ -18,17 +18,11 @@ */ package org.apache.syncope.core.persistence.jpa.entity.am; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.Column; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.Lob; -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -37,7 +31,8 @@ import org.apache.syncope.common.lib.types.SAML2SPNameId; import org.apache.syncope.common.lib.types.XmlSecAlgorithm; import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.persistence.jpa.converters.StringSetConverter; +import org.apache.syncope.core.persistence.jpa.converters.XmlSecAlgorithmListConverter; @Entity @Table(name = JPASAML2SPClientApp.TABLE) @@ -47,13 +42,6 @@ public class JPASAML2SPClientApp extends AbstractClientApp implements SAML2SPCli public static final String TABLE = "SAML2SPClientApp"; - protected static final TypeReference> STRING_TYPEREF = new TypeReference>() { - }; - - protected static final TypeReference> XMLSECAGO_TYPEREF = - new TypeReference>() { - }; - @Column(unique = true, nullable = false) private String entityId; @@ -81,56 +69,42 @@ public class JPASAML2SPClientApp extends AbstractClientApp implements SAML2SPCli private String nameIdQualifier; + @Convert(converter = StringSetConverter.class) @Lob - private String assertionAudiences; - - @Transient - private Set assertionAudiencesSet = new HashSet<>(); + private Set assertionAudiences = new HashSet<>(); @Column(name = "spNameIdQualifier") private String serviceProviderNameIdQualifier; + @Convert(converter = XmlSecAlgorithmListConverter.class) @Column(name = "sigAlgs") @Lob - private String signingSignatureAlgorithms; - - @Transient - private List signingSignatureAlgorithmsList = new ArrayList<>(); + private List signingSignatureAlgorithms = new ArrayList<>(); + @Convert(converter = XmlSecAlgorithmListConverter.class) @Column(name = "sigRefDigestMethod") @Lob - private String signingSignatureReferenceDigestMethods; - - @Transient - private List signingSignatureReferenceDigestMethodsList = new ArrayList<>(); + private List signingSignatureReferenceDigestMethods = new ArrayList<>(); + @Convert(converter = XmlSecAlgorithmListConverter.class) @Column(name = "encDataAlg") @Lob - private String encryptionDataAlgorithms; - - @Transient - private List encryptionDataAlgorithmsList = new ArrayList<>(); + private List encryptionDataAlgorithms = new ArrayList<>(); + @Convert(converter = XmlSecAlgorithmListConverter.class) @Column(name = "encKeyAlg") @Lob - private String encryptionKeyAlgorithms; - - @Transient - private List encryptionKeyAlgorithmsList = new ArrayList<>(); + private List encryptionKeyAlgorithms = new ArrayList<>(); + @Convert(converter = XmlSecAlgorithmListConverter.class) @Column(name = "sigBlAlg") @Lob - private String signingSignatureBlackListedAlgorithms; - - @Transient - private List signingSignatureBlackListedAlgorithmsList = new ArrayList<>(); + private List signingSignatureBlackListedAlgorithms = new ArrayList<>(); + @Convert(converter = XmlSecAlgorithmListConverter.class) @Column(name = "encBlAlg") @Lob - private String encryptionBlackListedAlgorithms; - - @Transient - private List encryptionBlackListedAlgorithmsList = new ArrayList<>(); + private List encryptionBlackListedAlgorithms = new ArrayList<>(); @Override public String getEntityId() { @@ -254,7 +228,7 @@ public void setNameIdQualifier(final String nameIdQualifier) { @Override public Set getAssertionAudiences() { - return assertionAudiencesSet; + return assertionAudiences; } @Override @@ -269,94 +243,31 @@ public void setServiceProviderNameIdQualifier(final String serviceProviderNameId @Override public List getSigningSignatureAlgorithms() { - return signingSignatureAlgorithmsList; + return signingSignatureAlgorithms; } @Override public List getSigningSignatureReferenceDigestMethods() { - return signingSignatureReferenceDigestMethodsList; + return signingSignatureReferenceDigestMethods; } @Override public List getEncryptionDataAlgorithms() { - return encryptionDataAlgorithmsList; + return encryptionDataAlgorithms; } @Override public List getEncryptionKeyAlgorithms() { - return encryptionKeyAlgorithmsList; + return encryptionKeyAlgorithms; } @Override public List getSigningSignatureBlackListedAlgorithms() { - return signingSignatureBlackListedAlgorithmsList; + return signingSignatureBlackListedAlgorithms; } @Override public List getEncryptionBlackListedAlgorithms() { - return encryptionBlackListedAlgorithmsList; - } - - protected void json2list(final boolean clearFirst) { - if (clearFirst) { - getAssertionAudiences().clear(); - getSigningSignatureAlgorithms().clear(); - getSigningSignatureReferenceDigestMethods().clear(); - getEncryptionDataAlgorithms().clear(); - getEncryptionKeyAlgorithms().clear(); - getSigningSignatureBlackListedAlgorithms().clear(); - getEncryptionBlackListedAlgorithms().clear(); - } - if (assertionAudiences != null) { - getAssertionAudiences().addAll( - POJOHelper.deserialize(assertionAudiences, STRING_TYPEREF)); - } - if (signingSignatureAlgorithms != null) { - getSigningSignatureAlgorithms().addAll( - POJOHelper.deserialize(signingSignatureAlgorithms, XMLSECAGO_TYPEREF)); - } - if (signingSignatureReferenceDigestMethods != null) { - getSigningSignatureReferenceDigestMethods().addAll( - POJOHelper.deserialize(signingSignatureReferenceDigestMethods, XMLSECAGO_TYPEREF)); - } - if (encryptionDataAlgorithms != null) { - getEncryptionDataAlgorithms().addAll( - POJOHelper.deserialize(encryptionDataAlgorithms, XMLSECAGO_TYPEREF)); - } - if (encryptionKeyAlgorithms != null) { - getEncryptionKeyAlgorithms().addAll( - POJOHelper.deserialize(encryptionKeyAlgorithms, XMLSECAGO_TYPEREF)); - } - if (signingSignatureBlackListedAlgorithms != null) { - getSigningSignatureBlackListedAlgorithms().addAll( - POJOHelper.deserialize(signingSignatureBlackListedAlgorithms, XMLSECAGO_TYPEREF)); - } - if (encryptionBlackListedAlgorithms != null) { - getEncryptionBlackListedAlgorithms().addAll( - POJOHelper.deserialize(encryptionBlackListedAlgorithms, XMLSECAGO_TYPEREF)); - } - } - - @PostLoad - public void postLoad() { - json2list(false); - } - - @PostPersist - @PostUpdate - public void postSave() { - json2list(true); - } - - @PrePersist - @PreUpdate - public void list2json() { - assertionAudiences = POJOHelper.serialize(getAssertionAudiences()); - signingSignatureAlgorithms = POJOHelper.serialize(getSigningSignatureAlgorithms()); - signingSignatureReferenceDigestMethods = POJOHelper.serialize(getSigningSignatureReferenceDigestMethods()); - encryptionDataAlgorithms = POJOHelper.serialize(getEncryptionDataAlgorithms()); - encryptionKeyAlgorithms = POJOHelper.serialize(getEncryptionKeyAlgorithms()); - signingSignatureBlackListedAlgorithms = POJOHelper.serialize(getSigningSignatureBlackListedAlgorithms()); - encryptionBlackListedAlgorithms = POJOHelper.serialize(getEncryptionBlackListedAlgorithms()); + return encryptionBlackListedAlgorithms; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAWAConfigEntry.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAWAConfigEntry.java index 6ceeca63921..0a053e1dd4b 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAWAConfigEntry.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAWAConfigEntry.java @@ -18,15 +18,15 @@ */ package org.apache.syncope.core.persistence.jpa.entity.am; -import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.Lob; import jakarta.persistence.Table; +import java.util.ArrayList; import java.util.List; -import java.util.Optional; import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry; +import org.apache.syncope.core.persistence.jpa.converters.StringListConverter; import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity @Table(name = JPAWAConfigEntry.TABLE) @@ -36,19 +36,17 @@ public class JPAWAConfigEntry extends AbstractProvidedKeyEntity implements WACon public static final String TABLE = "WAConfigEntry"; - protected static TypeReference> TYPEREF = new TypeReference>() { - }; - + @Convert(converter = StringListConverter.class) @Lob - private String waConfigValues; + private List waConfigValues = new ArrayList<>(); @Override public List getValues() { - return Optional.ofNullable(waConfigValues).map(v -> POJOHelper.deserialize(v, TYPEREF)).orElseGet(List::of); + return waConfigValues; } @Override public void setValues(final List values) { - this.waConfigValues = POJOHelper.serialize(values); + waConfigValues = values; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAMembership.java index ecb0a85344c..046e05ac9f1 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAMembership.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAMembership.java @@ -18,8 +18,8 @@ */ package org.apache.syncope.core.persistence.jpa.entity.anyobject; -import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.PostLoad; import jakarta.persistence.Table; @@ -44,11 +44,11 @@ public class JPAAMembership extends AbstractGeneratedKeyEntity implements AMembe public static final String TABLE = "AMembership"; @ManyToOne - @Column(name = "anyObject_id") + @JoinColumn(name = "anyObject_id") private JPAAnyObject leftEnd; @ManyToOne - @Column(name = "group_id") + @JoinColumn(name = "group_id") private JPAGroup rightEnd; @Transient diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAARelationship.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAARelationship.java index 206f75df93b..cfebb93c6ec 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAARelationship.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAARelationship.java @@ -18,9 +18,9 @@ */ package org.apache.syncope.core.persistence.jpa.entity.anyobject; -import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; @@ -47,11 +47,11 @@ public class JPAARelationship extends AbstractGeneratedKeyEntity implements ARel private JPARelationshipType type; @ManyToOne - @Column(name = "left_anyObject_id") + @JoinColumn(name = "left_anyObject_id") private JPAAnyObject leftEnd; @ManyToOne - @Column(name = "right_anyObject_id") + @JoinColumn(name = "right_anyObject_id") private JPAAnyObject rightEnd; @Override diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java index 21416f43894..bc931c30aa8 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java @@ -20,8 +20,8 @@ import jakarta.persistence.Cacheable; import jakarta.persistence.CascadeType; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; -import jakarta.persistence.EntityListeners; import jakarta.persistence.FetchType; import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinTable; @@ -29,7 +29,6 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.persistence.UniqueConstraint; import jakarta.validation.Valid; import jakarta.validation.constraints.Size; @@ -45,6 +44,7 @@ import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.common.validation.AnyObjectCheck; +import org.apache.syncope.core.persistence.jpa.converters.PlainAttrListConverter; import org.apache.syncope.core.persistence.jpa.entity.AbstractGroupableRelatable; import org.apache.syncope.core.persistence.jpa.entity.JPAAnyType; import org.apache.syncope.core.persistence.jpa.entity.JPAAnyTypeClass; @@ -53,7 +53,6 @@ @Entity @Table(name = JPAAnyObject.TABLE, uniqueConstraints = @UniqueConstraint(columnNames = { "name", "type_id" })) -@EntityListeners({ JSONAnyObjectListener.class }) @Cacheable @AnyObjectCheck public class JPAAnyObject @@ -70,12 +69,10 @@ public class JPAAnyObject @ManyToOne(fetch = FetchType.EAGER, optional = false) private JPAAnyType type; - private String plainAttrs; + @Convert(converter = PlainAttrListConverter.class) + private final List plainAttrs = new ArrayList<>(); - @Transient - private final List plainAttrsList = new ArrayList<>(); - - @ManyToMany(fetch = FetchType.EAGER) + @ManyToMany(fetch = FetchType.LAZY) @JoinTable(joinColumns = @JoinColumn(name = "anyObject_id"), inverseJoinColumns = @@ -134,28 +131,25 @@ public List getResources() { } @Override - public List getPlainAttrsList() { - return plainAttrsList; - } - - @Override - public String getPlainAttrsJSON() { + protected List plainAttrs() { return plainAttrs; } @Override - public void setPlainAttrsJSON(final String plainAttrs) { - this.plainAttrs = plainAttrs; + public List getPlainAttrs() { + return plainAttrs.stream(). + filter(attr -> attr.getMembership() == null && attr.getRelationship() == null). + toList(); } @Override public boolean add(final PlainAttr attr) { - return plainAttrsList.add(attr); + return plainAttrs.add(attr); } @Override public boolean remove(final PlainAttr attr) { - return plainAttrsList.removeIf(a -> a.getSchema().equals(attr.getSchema()) + return plainAttrs.removeIf(a -> a.getSchema().equals(attr.getSchema()) && Objects.equals(a.getMembership(), attr.getMembership()) && Objects.equals(a.getRelationship(), attr.getRelationship())); } @@ -180,7 +174,7 @@ public boolean add(final ARelationship relationship) { @Override public boolean remove(final Relationship relationship) { checkType(relationship, JPAARelationship.class); - plainAttrsList.removeIf(attr -> Objects.equals(attr.getRelationship(), relationship.getKey())); + plainAttrs.removeIf(attr -> Objects.equals(attr.getRelationship(), relationship.getKey())); return relationships.remove((JPAARelationship) relationship); } @@ -198,7 +192,7 @@ public boolean add(final AMembership membership) { @Override public boolean remove(final AMembership membership) { checkType(membership, JPAAMembership.class); - plainAttrsList.removeIf(attr -> Objects.equals(attr.getMembership(), membership.getKey())); + plainAttrs.removeIf(attr -> Objects.equals(attr.getMembership(), membership.getKey())); return memberships.remove((JPAAMembership) membership); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JSONAnyObjectListener.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JSONAnyObjectListener.java deleted file mode 100644 index c35f7b2dc96..00000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JSONAnyObjectListener.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.entity.anyobject; - -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; -import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; -import org.apache.syncope.core.persistence.jpa.entity.JSONEntityListener; - -public class JSONAnyObjectListener extends JSONEntityListener { - - @PostLoad - public void read(final JPAAnyObject anyObject) { - super.json2list(anyObject, false); - } - - @PrePersist - @PreUpdate - public void save(final JPAAnyObject anyObject) { - anyObject.list2json(); - } - - @PostPersist - @PostUpdate - public void readAfterSave(final JPAAnyObject anyObject) { - super.json2list(anyObject, true); - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGRelationship.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGRelationship.java index 4ec8160ceea..0209a49774c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGRelationship.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGRelationship.java @@ -18,9 +18,9 @@ */ package org.apache.syncope.core.persistence.jpa.entity.group; -import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; @@ -49,11 +49,11 @@ public class JPAGRelationship extends AbstractGeneratedKeyEntity implements GRel private JPARelationshipType type; @ManyToOne - @Column(name = "group_id") + @JoinColumn(name = "group_id") private JPAGroup leftEnd; @ManyToOne - @Column(name = "anyObject_id") + @JoinColumn(name = "anyObject_id") private JPAAnyObject rightEnd; @Override diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java index a194df71467..8c4afd07d26 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java @@ -21,17 +21,15 @@ import jakarta.persistence.Cacheable; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; -import jakarta.persistence.EntityListeners; import jakarta.persistence.FetchType; import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinTable; import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; -import jakarta.persistence.OneToOne; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.persistence.UniqueConstraint; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @@ -43,26 +41,24 @@ import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.entity.AnyType; import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.persistence.api.entity.Relationship; -import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; import org.apache.syncope.core.persistence.api.entity.group.GRelationship; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.group.GroupTypeExtension; -import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.common.validation.GroupCheck; +import org.apache.syncope.core.persistence.jpa.converters.PlainAttrListConverter; import org.apache.syncope.core.persistence.jpa.entity.AbstractRelatable; import org.apache.syncope.core.persistence.jpa.entity.JPAAnyTypeClass; +import org.apache.syncope.core.persistence.jpa.entity.JPADynGroupMembership; import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource; -import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership; -import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser; @Entity @Table(name = JPAGroup.TABLE) -@EntityListeners({ JSONGroupListener.class }) @Cacheable @GroupCheck public class JPAGroup @@ -83,10 +79,8 @@ public class JPAGroup @ManyToOne private JPAGroup groupOwner; - private String plainAttrs; - - @Transient - private final List plainAttrsList = new ArrayList<>(); + @Convert(converter = PlainAttrListConverter.class) + private final List plainAttrs = new ArrayList<>(); @ManyToMany(fetch = FetchType.LAZY) @JoinTable(joinColumns = @@ -106,12 +100,8 @@ public class JPAGroup @UniqueConstraint(columnNames = { "group_id", "anyTypeClass_id" })) private List auxClasses = new ArrayList<>(); - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "group") - @Valid - private JPAUDynGroupMembership uDynMembership; - @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "group") - private List aDynMemberships = new ArrayList<>(); + private List dynMemberships = new ArrayList<>(); @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "group") private List typeExtensions = new ArrayList<>(); @@ -174,56 +164,35 @@ public void setGroupOwner(final Group group) { } @Override - public List getPlainAttrsList() { - return plainAttrsList; - } - - @Override - public String getPlainAttrsJSON() { + protected List plainAttrs() { return plainAttrs; } - @Override - public void setPlainAttrsJSON(final String plainAttrs) { - this.plainAttrs = plainAttrs; - } - @Override public boolean add(final PlainAttr attr) { - return plainAttrsList.add(attr); + return plainAttrs.add(attr); } @Override public boolean remove(final PlainAttr attr) { - return plainAttrsList.removeIf(a -> a.getSchema().equals(attr.getSchema()) + return plainAttrs.removeIf(a -> a.getSchema().equals(attr.getSchema()) && Objects.equals(a.getRelationship(), attr.getRelationship())); } @Override public Optional getPlainAttr(final String plainSchema) { - return plainAttrsList.stream(). + return plainAttrs.stream(). filter(attr -> attr.getRelationship() == null && plainSchema.equals(attr.getSchema())). findFirst(); } @Override public List getPlainAttrs() { - return plainAttrsList.stream(). + return plainAttrs.stream(). filter(attr -> attr.getRelationship() == null). toList(); } - @Override - public UDynGroupMembership getUDynMembership() { - return uDynMembership; - } - - @Override - public void setUDynMembership(final UDynGroupMembership uDynMembership) { - checkType(uDynMembership, JPAUDynGroupMembership.class); - this.uDynMembership = (JPAUDynGroupMembership) uDynMembership; - } - @Override public boolean add(final AnyTypeClass auxClass) { checkType(auxClass, JPAAnyTypeClass.class); @@ -236,21 +205,21 @@ public List getAuxClasses() { } @Override - public boolean add(final ADynGroupMembership dynGroupMembership) { - checkType(dynGroupMembership, JPAADynGroupMembership.class); - return aDynMemberships.add((JPAADynGroupMembership) dynGroupMembership); + public boolean add(final DynGroupMembership dynGroupMembership) { + checkType(dynGroupMembership, JPADynGroupMembership.class); + return this.dynMemberships.add((JPADynGroupMembership) dynGroupMembership); } @Override - public Optional getADynMembership(final AnyType anyType) { - return aDynMemberships.stream(). + public Optional getDynMembership(final AnyType anyType) { + return dynMemberships.stream(). filter(dynGroupMembership -> anyType != null && anyType.equals(dynGroupMembership.getAnyType())). findFirst(); } @Override - public List getADynMemberships() { - return aDynMemberships; + public List getDynMemberships() { + return dynMemberships; } @Override @@ -280,7 +249,7 @@ public boolean add(final GRelationship relationship) { @Override public boolean remove(final Relationship relationship) { checkType(relationship, JPAGRelationship.class); - plainAttrsList.removeIf(attr -> Objects.equals(attr.getRelationship(), relationship.getKey())); + plainAttrs.removeIf(attr -> Objects.equals(attr.getRelationship(), relationship.getKey())); return relationships.remove((JPAGRelationship) relationship); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JSONGroupListener.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JSONGroupListener.java deleted file mode 100644 index 3a82b14589b..00000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JSONGroupListener.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.entity.group; - -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; -import org.apache.syncope.core.persistence.api.entity.group.Group; -import org.apache.syncope.core.persistence.jpa.entity.JSONEntityListener; - -public class JSONGroupListener extends JSONEntityListener { - - @PostLoad - public void read(final JPAGroup group) { - super.json2list(group, false); - } - - @PrePersist - @PreUpdate - public void save(final JPAGroup group) { - group.list2json(); - } - - @PostPersist - @PostUpdate - public void readAfterSave(final JPAGroup group) { - super.json2list(group, true); - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPAConfParam.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPAConfParam.java index cb768766d2f..6cf11c68761 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPAConfParam.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPAConfParam.java @@ -1,3 +1,4 @@ + /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,15 +19,15 @@ */ package org.apache.syncope.core.persistence.jpa.entity.keymaster; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; import jakarta.persistence.Entity; import jakarta.persistence.Lob; import jakarta.persistence.Table; -import java.io.IOException; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.core.persistence.api.entity.keymaster.ConfParam; import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; @Entity @Table(name = JPAConfParam.TABLE) @@ -34,7 +35,7 @@ public class JPAConfParam extends AbstractProvidedKeyEntity implements ConfParam private static final long serialVersionUID = 8742750097008236475L; - private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + private static final JsonMapper MAPPER = new SyncopeJsonMapper(); public static final String TABLE = "ConfParam"; @@ -46,7 +47,7 @@ public JsonNode getValue() { JsonNode deserialized = null; try { deserialized = MAPPER.readTree(jsonValue); - } catch (final IOException e) { + } catch (JacksonException e) { LOG.error("Could not deserialize {}", jsonValue, e); } return deserialized; @@ -56,7 +57,7 @@ public JsonNode getValue() { public void setValue(final JsonNode value) { try { this.jsonValue = MAPPER.writeValueAsString(value); - } catch (JsonProcessingException e) { + } catch (JacksonException e) { LOG.error("Could not serialize {}", value, e); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccessPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccessPolicy.java index b34ce5be1da..015d34c6057 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccessPolicy.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccessPolicy.java @@ -18,13 +18,13 @@ */ package org.apache.syncope.core.persistence.jpa.entity.policy; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.Lob; import jakarta.persistence.Table; -import java.util.Optional; import org.apache.syncope.common.lib.policy.AccessPolicyConf; import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.persistence.jpa.converters.AccessPolicyConfConverter; @Entity @Table(name = JPAAccessPolicy.TABLE) @@ -34,16 +34,17 @@ public class JPAAccessPolicy extends AbstractPolicy implements AccessPolicy { public static final String TABLE = "AccessPolicy"; + @Convert(converter = AccessPolicyConfConverter.class) @Lob - private String jsonConf; + private AccessPolicyConf jsonConf; @Override public AccessPolicyConf getConf() { - return Optional.ofNullable(jsonConf).map(c -> POJOHelper.deserialize(c, AccessPolicyConf.class)).orElse(null); + return jsonConf; } @Override public void setConf(final AccessPolicyConf conf) { - jsonConf = Optional.ofNullable(conf).map(POJOHelper::serialize).orElse(null); + jsonConf = conf; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAttrReleasePolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAttrReleasePolicy.java index 515e46f8459..4cb4f7b1f55 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAttrReleasePolicy.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAttrReleasePolicy.java @@ -19,13 +19,14 @@ package org.apache.syncope.core.persistence.jpa.entity.policy; import jakarta.persistence.Basic; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.Lob; import jakarta.persistence.Table; import java.util.Optional; import org.apache.syncope.common.lib.policy.AttrReleasePolicyConf; import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.persistence.jpa.converters.AttrReleasePolicyConfConverter; @Entity @Table(name = JPAAttrReleasePolicy.TABLE) @@ -40,8 +41,9 @@ public class JPAAttrReleasePolicy extends AbstractPolicy implements AttrReleaseP private Boolean status; + @Convert(converter = AttrReleasePolicyConfConverter.class) @Lob - private String jsonConf; + private AttrReleasePolicyConf jsonConf; @Override public int getOrder() { @@ -65,12 +67,11 @@ public void setStatus(final Boolean status) { @Override public AttrReleasePolicyConf getConf() { - return Optional.ofNullable(jsonConf). - map(c -> POJOHelper.deserialize(c, AttrReleasePolicyConf.class)).orElse(null); + return jsonConf; } @Override public void setConf(final AttrReleasePolicyConf conf) { - jsonConf = Optional.ofNullable(conf).map(POJOHelper::serialize).orElse(null); + jsonConf = conf; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAuthPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAuthPolicy.java index 61fcc499c56..ed3c3ba07e2 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAuthPolicy.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAuthPolicy.java @@ -18,13 +18,13 @@ */ package org.apache.syncope.core.persistence.jpa.entity.policy; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.Lob; import jakarta.persistence.Table; -import java.util.Optional; import org.apache.syncope.common.lib.policy.AuthPolicyConf; import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.persistence.jpa.converters.AuthPolicyConfConverter; @Entity @Table(name = JPAAuthPolicy.TABLE) @@ -34,18 +34,17 @@ public class JPAAuthPolicy extends AbstractPolicy implements AuthPolicy { public static final String TABLE = "AuthPolicy"; + @Convert(converter = AuthPolicyConfConverter.class) @Lob - private String jsonConf; + private AuthPolicyConf jsonConf; @Override public AuthPolicyConf getConf() { - return jsonConf == null - ? null - : POJOHelper.deserialize(jsonConf, AuthPolicyConf.class); + return jsonConf; } @Override public void setConf(final AuthPolicyConf conf) { - jsonConf = Optional.ofNullable(conf).map(POJOHelper::serialize).orElse(null); + jsonConf = conf; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPATicketExpirationPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPATicketExpirationPolicy.java index b3c934ebc4c..15cac755915 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPATicketExpirationPolicy.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPATicketExpirationPolicy.java @@ -18,13 +18,13 @@ */ package org.apache.syncope.core.persistence.jpa.entity.policy; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.Lob; import jakarta.persistence.Table; -import java.util.Optional; import org.apache.syncope.common.lib.policy.TicketExpirationPolicyConf; import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.persistence.jpa.converters.TicketExpirationPolicyConfConverter; @Entity @Table(name = JPATicketExpirationPolicy.TABLE) @@ -34,18 +34,17 @@ public class JPATicketExpirationPolicy extends AbstractPolicy implements TicketE public static final String TABLE = "TicketExpirationPolicy"; + @Convert(converter = TicketExpirationPolicyConfConverter.class) @Lob - private String jsonConf; + private TicketExpirationPolicyConf jsonConf; @Override public TicketExpirationPolicyConf getConf() { - return Optional.ofNullable(jsonConf). - map(c -> POJOHelper.deserialize(c, TicketExpirationPolicyConf.class)). - orElse(null); + return jsonConf; } @Override public void setConf(final TicketExpirationPolicyConf conf) { - jsonConf = Optional.ofNullable(conf).map(POJOHelper::serialize).orElse(null); + jsonConf = conf; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java index edcab017acc..3006c4a0510 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java @@ -38,7 +38,7 @@ @MappedSuperclass @ProvisioningTaskCheck public abstract class AbstractProvisioningTask - extends JPASchedTask implements ProvisioningTask { + extends AbstractSchedTask implements ProvisioningTask { private static final long serialVersionUID = -4141057723006682562L; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractSchedTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractSchedTask.java new file mode 100644 index 00000000000..06d3f3e61bf --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractSchedTask.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.entity.task; + +import jakarta.persistence.Column; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation; + +@MappedSuperclass +public abstract class AbstractSchedTask extends AbstractTask implements SchedTask { + + private static final long serialVersionUID = -2205057516126341566L; + + @ManyToOne(optional = false) + private JPAImplementation jobDelegate; + + private String cronExpression; + + @Column(unique = true, nullable = false) + private String name; + + private String description; + + @NotNull + private Boolean active = true; + + @Override + public Implementation getJobDelegate() { + return jobDelegate; + } + + @Override + public void setJobDelegate(final Implementation jobDelegate) { + checkType(jobDelegate, JPAImplementation.class); + checkImplementationType(jobDelegate, IdRepoImplementationType.TASKJOB_DELEGATE); + this.jobDelegate = (JPAImplementation) jobDelegate; + } + + @Override + public String getCronExpression() { + return cronExpression; + } + + @Override + public void setCronExpression(final String cronExpression) { + this.cronExpression = cronExpression; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(final String description) { + this.description = description; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(final String name) { + this.name = name; + } + + @Override + public boolean isActive() { + return active; + } + + @Override + public void setActive(final boolean active) { + this.active = active; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java index 29600aeba11..8af33d47c97 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java @@ -18,19 +18,13 @@ */ package org.apache.syncope.core.persistence.jpa.entity.task; -import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.Lob; import jakarta.persistence.ManyToOne; -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.validation.constraints.NotNull; import java.util.HashMap; import java.util.Locale; @@ -41,8 +35,9 @@ import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef; import org.apache.syncope.core.persistence.api.entity.task.MacroTask; import org.apache.syncope.core.persistence.common.validation.FormPropertyDefCheck; +import org.apache.syncope.core.persistence.jpa.converters.Locale2StringMapConverter; +import org.apache.syncope.core.persistence.jpa.converters.String2StringMapConverter; import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity @Table(name = JPAFormPropertyDef.TABLE) @@ -53,14 +48,6 @@ public class JPAFormPropertyDef extends AbstractGeneratedKeyEntity implements Fo public static final String TABLE = "FormPropertyDef"; - protected static final TypeReference> ENUMVALUES_TYPEREF = - new TypeReference>() { - }; - - protected static final TypeReference> LABEL_TYPEREF = - new TypeReference>() { - }; - private int idx; @ManyToOne(optional = false) @@ -69,11 +56,9 @@ public class JPAFormPropertyDef extends AbstractGeneratedKeyEntity implements Fo @NotNull private String name; + @Convert(converter = Locale2StringMapConverter.class) @Lob - private String labels; - - @Transient - private Map labelMap = new HashMap<>(); + private Map labels = new HashMap<>(); @NotNull @Enumerated(EnumType.STRING) @@ -92,8 +77,9 @@ public class JPAFormPropertyDef extends AbstractGeneratedKeyEntity implements Fo private String datePattern; + @Convert(converter = String2StringMapConverter.class) @Lob - private String enumValues; + private Map enumValues = new HashMap<>(); @NotNull private Boolean dropdownSingleSelection = Boolean.TRUE; @@ -130,12 +116,12 @@ public void setName(final String name) { @Override public Optional getLabel(final Locale locale) { - return Optional.ofNullable(labelMap.get(locale)); + return Optional.ofNullable(labels.get(locale)); } @Override public Map getLabels() { - return labelMap; + return labels; } @Override @@ -200,13 +186,12 @@ public void setDatePattern(final String datePattern) { @Override public Map getEnumValues() { - return Optional.ofNullable(enumValues).map(v -> POJOHelper.deserialize(v, ENUMVALUES_TYPEREF)). - orElseGet(Map::of); + return enumValues; } @Override public void setEnumValues(final Map enumValues) { - this.enumValues = Optional.ofNullable(enumValues).map(POJOHelper::serialize).orElse(null); + this.enumValues = enumValues; } @Override @@ -238,30 +223,4 @@ public String getMimeType() { public void setMimeType(final String mimeType) { this.mimeType = mimeType; } - - protected void json2map(final boolean clearFirst) { - if (clearFirst) { - getLabels().clear(); - } - if (labels != null) { - getLabels().putAll(POJOHelper.deserialize(labels, LABEL_TYPEREF)); - } - } - - @PostLoad - public void postLoad() { - json2map(false); - } - - @PostPersist - @PostUpdate - public void postSave() { - json2map(true); - } - - @PrePersist - @PreUpdate - public void map2json() { - labels = POJOHelper.serialize(getLabels()); - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java index 64725466b5d..31ad23b78b5 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java @@ -37,12 +37,14 @@ import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand; import org.apache.syncope.core.persistence.api.entity.task.SchedTask; import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.common.validation.SchedTaskCheck; import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation; import org.apache.syncope.core.persistence.jpa.entity.JPARealm; @Entity @Table(name = JPAMacroTask.TABLE) -public class JPAMacroTask extends JPASchedTask implements MacroTask { +@SchedTaskCheck +public class JPAMacroTask extends AbstractSchedTask implements MacroTask { private static final long serialVersionUID = 8261850094316787406L; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTaskCommand.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTaskCommand.java index 86ca5037c1d..23579a68d93 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTaskCommand.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTaskCommand.java @@ -21,7 +21,6 @@ import jakarta.persistence.Entity; import jakarta.persistence.Lob; import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import java.util.Optional; import org.apache.syncope.common.lib.command.CommandArgs; @@ -46,7 +45,7 @@ public class JPAMacroTaskCommand extends AbstractGeneratedKeyEntity implements M @ManyToOne(optional = false) private JPAMacroTask macroTask; - @OneToOne(optional = false) + @ManyToOne(optional = false) private JPAImplementation command; @Lob diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTask.java index 758513df111..fef3ced6001 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTask.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTask.java @@ -18,21 +18,15 @@ */ package org.apache.syncope.core.persistence.jpa.entity.task; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.CascadeType; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.Lob; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.HashSet; @@ -43,8 +37,8 @@ import org.apache.syncope.core.persistence.api.entity.Notification; import org.apache.syncope.core.persistence.api.entity.task.NotificationTask; import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.jpa.converters.StringSetConverter; import org.apache.syncope.core.persistence.jpa.entity.JPANotification; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity @Table(name = JPANotificationTask.TABLE) @@ -54,9 +48,6 @@ public class JPANotificationTask extends AbstractTask implemen public static final String TABLE = "NotificationTask"; - protected static final TypeReference> TYPEREF = new TypeReference>() { - }; - @NotNull @ManyToOne private JPANotification notification; @@ -66,11 +57,9 @@ public class JPANotificationTask extends AbstractTask implemen private String entityKey; + @Convert(converter = StringSetConverter.class) @Lob - private String recipients; - - @Transient - private Set recipientsSet = new HashSet<>(); + private Set recipients = new HashSet<>(); @OneToMany(targetEntity = JPANotificationTaskExec.class, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task") @@ -130,7 +119,7 @@ public void setEntityKey(final String entityKey) { @Override public Set getRecipients() { - return recipientsSet; + return recipients; } @Override @@ -202,30 +191,4 @@ protected Class> executionClass() { protected List> executions() { return executions; } - - protected void json2list(final boolean clearFirst) { - if (clearFirst) { - getRecipients().clear(); - } - if (recipients != null) { - getRecipients().addAll(POJOHelper.deserialize(recipients, TYPEREF)); - } - } - - @PostLoad - public void postLoad() { - json2list(false); - } - - @PostPersist - @PostUpdate - public void postSave() { - json2list(true); - } - - @PrePersist - @PreUpdate - public void list2json() { - recipients = POJOHelper.serialize(getRecipients()); - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java index f67c93a87d8..7a2d1a25715 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java @@ -26,8 +26,8 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinTable; import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; -import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; import jakarta.validation.constraints.NotNull; @@ -55,7 +55,7 @@ public class JPAPullTask extends AbstractInboundTask implements PullTa @NotNull private PullMode pullMode; - @OneToOne + @ManyToOne private JPAImplementation reconFilterBuilder; @ManyToMany(fetch = FetchType.EAGER) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java index dd6dc006644..40e46a3368c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java @@ -18,8 +18,8 @@ */ package org.apache.syncope.core.persistence.jpa.entity.task; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.CascadeType; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.JoinColumn; @@ -28,13 +28,7 @@ import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.persistence.UniqueConstraint; import java.util.ArrayList; import java.util.HashMap; @@ -47,9 +41,9 @@ import org.apache.syncope.core.persistence.api.entity.task.PushTask; import org.apache.syncope.core.persistence.api.entity.task.SchedTask; import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.jpa.converters.String2StringMapConverter; import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation; import org.apache.syncope.core.persistence.jpa.entity.JPARealm; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity @Table(name = JPAPushTask.TABLE) @@ -59,18 +53,12 @@ public class JPAPushTask extends AbstractProvisioningTask implements P public static final String TABLE = "PushTask"; - protected static final TypeReference> FILTER_TYPEREF = - new TypeReference>() { - }; - @ManyToOne(fetch = FetchType.EAGER, optional = false) private JPARealm sourceRealm; + @Convert(converter = String2StringMapConverter.class) @Lob - private String filters; - - @Transient - private Map filterMap = new HashMap<>(); + private Map filters = new HashMap<>(); @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "PushTaskAction", @@ -111,12 +99,12 @@ public List getActions() { @Override public Optional getFilter(final String anyType) { - return Optional.ofNullable(filterMap.get(anyType)); + return Optional.ofNullable(filters.get(anyType)); } @Override public Map getFilters() { - return filterMap; + return filters; } @Override @@ -128,30 +116,4 @@ protected Class> executionClass() { protected List> executions() { return executions; } - - protected void json2map(final boolean clearFirst) { - if (clearFirst) { - getFilters().clear(); - } - if (filters != null) { - getFilters().putAll(POJOHelper.deserialize(filters, FILTER_TYPEREF)); - } - } - - @PostLoad - public void postLoad() { - json2map(false); - } - - @PostPersist - @PostUpdate - public void postSave() { - json2map(true); - } - - @PrePersist - @PreUpdate - public void map2json() { - filters = POJOHelper.serialize(getFilters()); - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java index 2dc005cb513..4efd3d9617b 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java @@ -19,102 +19,28 @@ package org.apache.syncope.core.persistence.jpa.entity.task; import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.Inheritance; -import jakarta.persistence.InheritanceType; import jakarta.persistence.OneToMany; -import jakarta.persistence.OneToOne; import jakarta.persistence.Table; -import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; -import org.apache.syncope.common.lib.types.IdRepoImplementationType; -import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.task.SchedTask; import org.apache.syncope.core.persistence.api.entity.task.TaskExec; import org.apache.syncope.core.persistence.common.validation.SchedTaskCheck; -import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation; @Entity @Table(name = JPASchedTask.TABLE) -@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) @SchedTaskCheck -public class JPASchedTask extends AbstractTask implements SchedTask { +public class JPASchedTask extends AbstractSchedTask { private static final long serialVersionUID = 7596236684832602180L; public static final String TABLE = "SchedTask"; - @OneToOne(optional = false) - private JPAImplementation jobDelegate; - - private String cronExpression; - - @Column(unique = true, nullable = false) - private String name; - - private String description; - - @NotNull - private Boolean active = true; - @OneToMany(targetEntity = JPASchedTaskExec.class, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task") private List> executions = new ArrayList<>(); - @Override - public Implementation getJobDelegate() { - return jobDelegate; - } - - @Override - public void setJobDelegate(final Implementation jobDelegate) { - checkType(jobDelegate, JPAImplementation.class); - checkImplementationType(jobDelegate, IdRepoImplementationType.TASKJOB_DELEGATE); - this.jobDelegate = (JPAImplementation) jobDelegate; - } - - @Override - public String getCronExpression() { - return cronExpression; - } - - @Override - public void setCronExpression(final String cronExpression) { - this.cronExpression = cronExpression; - } - - @Override - public String getDescription() { - return description; - } - - @Override - public void setDescription(final String description) { - this.description = description; - } - - @Override - public String getName() { - return name; - } - - @Override - public void setName(final String name) { - this.name = name; - } - - @Override - public boolean isActive() { - return active; - } - - @Override - public void setActive(final boolean active) { - this.active = active; - } - @Override protected Class> executionClass() { return JPASchedTaskExec.class; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPALinkedAccount.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPALinkedAccount.java index 34f9701b188..48da10f37f0 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPALinkedAccount.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPALinkedAccount.java @@ -19,20 +19,18 @@ package org.apache.syncope.core.persistence.jpa.entity.user; import jakarta.persistence.Column; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; -import jakarta.persistence.EntityListeners; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.FetchType; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.persistence.UniqueConstraint; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.ApplicationContextProvider; import org.apache.syncope.core.persistence.api.EncryptorManager; @@ -40,14 +38,13 @@ import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.jpa.converters.PlainAttrListConverter; import org.apache.syncope.core.persistence.jpa.entity.AbstractAttributable; import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource; -import org.apache.syncope.core.spring.security.AuthContextUtils; @Entity @Table(name = JPALinkedAccount.TABLE, uniqueConstraints = @UniqueConstraint(columnNames = { "connObjectKeyValue", "resource_id" })) -@EntityListeners({ JSONLinkedAccountListener.class }) public class JPALinkedAccount extends AbstractAttributable implements LinkedAccount { private static final long serialVersionUID = -5141654998687601522L; @@ -73,10 +70,8 @@ public class JPALinkedAccount extends AbstractAttributable implements LinkedAcco private Boolean suspended = false; - private String plainAttrs; - - @Transient - private final List plainAttrsList = new ArrayList<>(); + @Convert(converter = PlainAttrListConverter.class) + private final List plainAttrs = new ArrayList<>(); @Override public String getConnObjectKeyValue() { @@ -154,12 +149,7 @@ protected String encode(final String value) throws Exception { return ApplicationContextProvider.getApplicationContext().getBean(EncryptorManager.class).getInstance().encode( value, Optional.ofNullable(cipherAlgorithm). - orElseGet(() -> CipherAlgorithm.valueOf( - ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class).get( - AuthContextUtils.getDomain(), - "password.cipher.algorithm", - CipherAlgorithm.AES.name(), - String.class)))); + orElseThrow(() -> new IllegalStateException("No cipherAlgorithm was set"))); } @Override @@ -183,39 +173,24 @@ public Boolean isSuspended() { } @Override - public List getPlainAttrsList() { - return plainAttrsList; - } - - @Override - public String getPlainAttrsJSON() { + public List getPlainAttrs() { return plainAttrs; } - @Override - public void setPlainAttrsJSON(final String plainAttrs) { - this.plainAttrs = plainAttrs; - } - @Override public boolean add(final PlainAttr attr) { - return plainAttrsList.add(attr); + return plainAttrs.add(attr); } @Override public boolean remove(final PlainAttr attr) { - return plainAttrsList.removeIf(jsonAttr -> jsonAttr.getSchema().equals(attr.getSchema())); + return plainAttrs.removeIf(jsonAttr -> jsonAttr.getSchema().equals(attr.getSchema())); } @Override public Optional getPlainAttr(final String plainSchema) { - return plainAttrsList.stream(). + return plainAttrs.stream(). filter(attr -> plainSchema.equals(attr.getSchema())). findFirst(); } - - @Override - public List getPlainAttrs() { - return plainAttrsList.stream().toList(); - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUDynGroupMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUDynGroupMembership.java deleted file mode 100644 index 1a851cf335e..00000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUDynGroupMembership.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.entity.user; - -import jakarta.persistence.Entity; -import jakarta.persistence.OneToOne; -import jakarta.persistence.Table; -import org.apache.syncope.core.persistence.api.entity.group.Group; -import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; -import org.apache.syncope.core.persistence.api.entity.user.User; -import org.apache.syncope.core.persistence.jpa.entity.AbstractDynMembership; -import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup; - -@Entity -@Table(name = JPAUDynGroupMembership.TABLE) -public class JPAUDynGroupMembership extends AbstractDynMembership implements UDynGroupMembership { - - private static final long serialVersionUID = -7336814163949640354L; - - public static final String TABLE = "UDynGroupMembership"; - - @OneToOne - private JPAGroup group; - - @Override - public Group getGroup() { - return group; - } - - @Override - public void setGroup(final Group group) { - checkType(group, JPAGroup.class); - this.group = (JPAGroup) group; - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUMembership.java index d26dbd9d2a8..fbdd858f44a 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUMembership.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUMembership.java @@ -18,8 +18,8 @@ */ package org.apache.syncope.core.persistence.jpa.entity.user; -import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; @@ -41,11 +41,11 @@ public class JPAUMembership extends AbstractGeneratedKeyEntity implements UMembe public static final String TABLE = "UMembership"; @ManyToOne - @Column(name = "user_id") + @JoinColumn(name = "user_id") private JPAUser leftEnd; @ManyToOne - @Column(name = "group_id") + @JoinColumn(name = "group_id") private JPAGroup rightEnd; @Override diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAURelationship.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAURelationship.java index bd6ab2af6be..25443257964 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAURelationship.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAURelationship.java @@ -18,9 +18,9 @@ */ package org.apache.syncope.core.persistence.jpa.entity.user; -import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; @@ -49,11 +49,11 @@ public class JPAURelationship extends AbstractGeneratedKeyEntity implements URel private JPARelationshipType type; @ManyToOne - @Column(name = "user_id") + @JoinColumn(name = "user_id") private JPAUser leftEnd; @ManyToOne - @Column(name = "anyObject_id") + @JoinColumn(name = "anyObject_id") private JPAAnyObject rightEnd; @Override diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java index 7454e5eb9df..14d77213c80 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java @@ -18,12 +18,11 @@ */ package org.apache.syncope.core.persistence.jpa.entity.user; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.Cacheable; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; -import jakarta.persistence.EntityListeners; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.FetchType; @@ -34,7 +33,6 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.persistence.UniqueConstraint; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @@ -43,7 +41,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.ApplicationContextProvider; import org.apache.syncope.core.persistence.api.EncryptorManager; @@ -59,17 +56,16 @@ import org.apache.syncope.core.persistence.api.entity.user.UMembership; import org.apache.syncope.core.persistence.api.entity.user.URelationship; import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.jpa.converters.PlainAttrListConverter; +import org.apache.syncope.core.persistence.jpa.converters.StringListConverter; import org.apache.syncope.core.persistence.jpa.entity.AbstractGroupableRelatable; import org.apache.syncope.core.persistence.jpa.entity.JPAAnyTypeClass; import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource; import org.apache.syncope.core.persistence.jpa.entity.JPARole; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; -import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.security.SecureRandomUtils; @Entity @Table(name = JPAUser.TABLE) -@EntityListeners({ JSONUserListener.class }) @Cacheable public class JPAUser extends AbstractGroupableRelatable @@ -79,9 +75,6 @@ public class JPAUser public static final String TABLE = "SyncopeUser"; - protected static final TypeReference> TYPEREF = new TypeReference>() { - }; - @Column(nullable = true) protected String password; @@ -94,10 +87,8 @@ public class JPAUser @UniqueConstraint(columnNames = { "user_id", "role_id" })) protected List roles = new ArrayList<>(); - private String plainAttrs; - - @Transient - private final List plainAttrsList = new ArrayList<>(); + @Convert(converter = PlainAttrListConverter.class) + private List plainAttrs = new ArrayList<>(); @Lob protected String token; @@ -108,8 +99,9 @@ public class JPAUser @Enumerated(EnumType.STRING) protected CipherAlgorithm cipherAlgorithm; + @Convert(converter = StringListConverter.class) @Lob - protected String passwordHistory; + protected List passwordHistory = new ArrayList<>(); /** * Subsequent failed logins. @@ -141,7 +133,7 @@ public class JPAUser /** * Provisioning external resources. */ - @ManyToMany(fetch = FetchType.EAGER) + @ManyToMany(fetch = FetchType.LAZY) @JoinTable(joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @@ -225,12 +217,7 @@ protected String encode(final String value) throws Exception { return ApplicationContextProvider.getApplicationContext().getBean(EncryptorManager.class).getInstance().encode( value, Optional.ofNullable(cipherAlgorithm). - orElseGet(() -> CipherAlgorithm.valueOf( - ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class).get( - AuthContextUtils.getDomain(), - "password.cipher.algorithm", - CipherAlgorithm.AES.name(), - String.class)))); + orElseThrow(() -> new IllegalStateException("No cipherAlgorithm was set"))); } @Override @@ -264,28 +251,25 @@ public boolean canDecodeSecrets() { } @Override - public List getPlainAttrsList() { - return plainAttrsList; - } - - @Override - public String getPlainAttrsJSON() { + protected List plainAttrs() { return plainAttrs; } @Override - public void setPlainAttrsJSON(final String plainAttrs) { - this.plainAttrs = plainAttrs; + public List getPlainAttrs() { + return plainAttrs.stream(). + filter(attr -> attr.getMembership() == null && attr.getRelationship() == null). + toList(); } @Override public boolean add(final PlainAttr attr) { - return plainAttrsList.add(attr); + return plainAttrs.add(attr); } @Override public boolean remove(final PlainAttr attr) { - return plainAttrsList.removeIf(a -> a.getSchema().equals(attr.getSchema()) + return plainAttrs.removeIf(a -> a.getSchema().equals(attr.getSchema()) && Objects.equals(a.getMembership(), attr.getMembership()) && Objects.equals(a.getRelationship(), attr.getRelationship())); } @@ -328,22 +312,18 @@ public boolean hasTokenExpired() { @Override public void addToPasswordHistory(final String password) { - List ph = getPasswordHistory(); - ph.add(password); - passwordHistory = POJOHelper.serialize(ph); + passwordHistory.add(password); } @Override public void removeOldestEntriesFromPasswordHistory(final int n) { - List ph = getPasswordHistory(); - passwordHistory = POJOHelper.serialize(ph.subList(Math.min(n, ph.size()), ph.size())); + passwordHistory = new ArrayList<>( + passwordHistory.subList(Math.min(n, passwordHistory.size()), passwordHistory.size())); } @Override public List getPasswordHistory() { - return passwordHistory == null - ? new ArrayList<>(0) - : POJOHelper.deserialize(passwordHistory, TYPEREF); + return passwordHistory; } @Override @@ -457,7 +437,7 @@ public boolean add(final URelationship relationship) { @Override public boolean remove(final Relationship relationship) { checkType(relationship, JPAURelationship.class); - plainAttrsList.removeIf(attr -> Objects.equals(attr.getRelationship(), relationship.getKey())); + plainAttrs.removeIf(attr -> Objects.equals(attr.getRelationship(), relationship.getKey())); return relationships.remove((JPAURelationship) relationship); } @@ -475,7 +455,7 @@ public boolean add(final UMembership membership) { @Override public boolean remove(final UMembership membership) { checkType(membership, JPAUMembership.class); - plainAttrsList.removeIf(attr -> Objects.equals(attr.getMembership(), membership.getKey())); + plainAttrs.removeIf(attr -> Objects.equals(attr.getMembership(), membership.getKey())); return memberships.remove((JPAUMembership) membership); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JSONLinkedAccountListener.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JSONLinkedAccountListener.java deleted file mode 100644 index 646b3d74d07..00000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JSONLinkedAccountListener.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.entity.user; - -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import org.apache.syncope.core.persistence.api.entity.user.User; -import org.apache.syncope.core.persistence.jpa.entity.JSONEntityListener; - -public class JSONLinkedAccountListener extends JSONEntityListener { - - @PostLoad - public void read(final JPALinkedAccount linkedAccount) { - super.json2list(linkedAccount, false); - } - - @PostPersist - @PostUpdate - public void readAfterSave(final JPALinkedAccount linkedAccount) { - super.json2list(linkedAccount, true); - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JSONUserListener.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JSONUserListener.java deleted file mode 100644 index 6f9294d9061..00000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JSONUserListener.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.entity.user; - -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; -import org.apache.syncope.core.persistence.api.entity.user.User; -import org.apache.syncope.core.persistence.jpa.entity.JSONEntityListener; - -public class JSONUserListener extends JSONEntityListener { - - @PostLoad - public void read(final JPAUser user) { - super.json2list(user, false); - } - - @PrePersist - @PreUpdate - public void save(final JPAUser user) { - user.list2json(); - } - - @PostPersist - @PostUpdate - public void readAfterSave(final JPAUser user) { - super.json2list(user, true); - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/hibernate/DomainJCacheRegionFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/hibernate/DomainJCacheRegionFactory.java new file mode 100644 index 00000000000..0cdbeaf186e --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/hibernate/DomainJCacheRegionFactory.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.hibernate; + +import org.hibernate.cache.jcache.internal.JCacheRegionFactory; + +public class DomainJCacheRegionFactory extends JCacheRegionFactory { + + private static final long serialVersionUID = -6813667559684397717L; + + @Override + protected void releaseFromUse() { + // nothing to release, there might be other Domains using the cacheManager instance + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/hibernate/Jackson3JsonFormatMapper.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/hibernate/Jackson3JsonFormatMapper.java new file mode 100644 index 00000000000..6d27a0ceb94 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/hibernate/Jackson3JsonFormatMapper.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.hibernate; + +import java.lang.reflect.Type; +import java.util.List; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.format.AbstractJsonFormatMapper; +import org.hibernate.type.format.FormatMapperCreationContext; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.JacksonModule; +import tools.jackson.databind.cfg.MapperBuilder; +import tools.jackson.databind.json.JsonMapper; + +public final class Jackson3JsonFormatMapper extends AbstractJsonFormatMapper { + + public static final String SHORT_NAME = "jackson3"; + + private final JsonMapper jsonMapper; + + public Jackson3JsonFormatMapper() { + this(MapperBuilder.findModules(Jackson3JsonFormatMapper.class.getClassLoader())); + } + + public Jackson3JsonFormatMapper(final FormatMapperCreationContext creationContext) { + this(creationContext. + getBootstrapContext(). + getClassLoaderService(). + >workWithClassLoader(MapperBuilder::findModules)); + } + + private Jackson3JsonFormatMapper(final List modules) { + this(JsonMapper.builderWithJackson2Defaults().addModules(modules).build()); + } + + public Jackson3JsonFormatMapper(final JsonMapper jsonMapper) { + this.jsonMapper = jsonMapper; + } + + @Override + public void writeToTarget( + final T value, + final JavaType javaType, + final Object target, + final WrapperOptions options) + throws JacksonException { + + jsonMapper.writerFor(jsonMapper.constructType(javaType.getJavaType())). + writeValue((JsonGenerator) target, value); + } + + @Override + public T readFromSource( + final JavaType javaType, + final Object source, + final WrapperOptions options) throws JacksonException { + + return jsonMapper.readValue((JsonParser) source, jsonMapper.constructType(javaType.getJavaType())); + } + + @Override + public boolean supportsSourceType(final Class sourceType) { + return JsonParser.class.isAssignableFrom(sourceType); + } + + @Override + public boolean supportsTargetType(final Class targetType) { + return JsonGenerator.class.isAssignableFrom(targetType); + } + + @Override + public T fromString(final CharSequence charSequence, final Type type) { + try { + return jsonMapper.readValue(charSequence.toString(), jsonMapper.constructType(type)); + } catch (JacksonException e) { + throw new IllegalArgumentException("Could not deserialize string to java type: " + type, e); + } + } + + @Override + public String toString(final T value, final Type type) { + try { + return jsonMapper.writerFor(jsonMapper.constructType(type)).writeValueAsString(value); + } catch (JacksonException e) { + throw new IllegalArgumentException("Could not serialize object of java type: " + type, e); + } + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/hibernate/SyncopePostgreSQLDialect.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/hibernate/SyncopePostgreSQLDialect.java new file mode 100644 index 00000000000..dfb4350853b --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/hibernate/SyncopePostgreSQLDialect.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.hibernate; + +import java.sql.Types; +import org.hibernate.boot.model.TypeContributions; +import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.type.descriptor.jdbc.ClobJdbcType; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; + +public class SyncopePostgreSQLDialect extends PostgreSQLDialect { + + @Override + protected String columnType(final int sqlTypeCode) { + return switch (sqlTypeCode) { + case Types.CLOB -> + "text"; + + default -> + super.columnType(sqlTypeCode); + }; + } + + @Override + protected void contributePostgreSQLTypes( + final TypeContributions typeContributions, + final ServiceRegistry serviceRegistry) { + + super.contributePostgreSQLTypes(typeContributions, serviceRegistry); + + JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry(); + jdbcTypeRegistry.addDescriptor(Types.CLOB, ClobJdbcType.STRING_BINDING); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/BooleanValueHandler.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/BooleanValueHandler.java deleted file mode 100644 index 93ce1b12c2e..00000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/BooleanValueHandler.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.openjpa; - -import java.util.Optional; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.openjpa.jdbc.identifier.DBIdentifier; -import org.apache.openjpa.jdbc.kernel.JDBCStore; -import org.apache.openjpa.jdbc.meta.ValueMapping; -import org.apache.openjpa.jdbc.meta.strats.AbstractValueHandler; -import org.apache.openjpa.jdbc.schema.Column; -import org.apache.openjpa.jdbc.schema.ColumnIO; -import org.apache.openjpa.jdbc.sql.DBDictionary; -import org.apache.openjpa.meta.JavaTypes; - -public class BooleanValueHandler extends AbstractValueHandler { - - private static final long serialVersionUID = -6742506201236646294L; - - private static final BooleanValueHandler INSTANCE = new BooleanValueHandler(); - - public static BooleanValueHandler getInstance() { - return INSTANCE; - } - - /** - * @deprecated - */ - @Override - @Deprecated - public Column[] map(final ValueMapping vm, final String name, final ColumnIO io, final boolean adapt) { - DBDictionary dict = vm.getMappingRepository().getDBDictionary(); - DBIdentifier colName = DBIdentifier.newColumn(name, Optional.ofNullable(dict). - filter(DBDictionary::delimitAll).isPresent()); - return map(colName, io, adapt); - } - - public static Column[] map(final DBIdentifier name, final ColumnIO io, final boolean adapt) { - Column col = new Column(); - col.setIdentifier(name); - col.setJavaType(JavaTypes.INT); - return new Column[] { col }; - } - - @Override - public Object toDataStoreValue(final ValueMapping vm, final Object val, final JDBCStore store) { - return Optional.ofNullable(val).map(o -> BooleanUtils.isTrue((Boolean) o) ? 1 : 0).orElse(null); - } - - @Override - public Object toObjectValue(final ValueMapping vm, final Object val) { - return Optional.ofNullable(val).map(o -> BooleanUtils.toBoolean((int) o)).orElse(null); - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/LocaleValueHandler.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/LocaleValueHandler.java deleted file mode 100644 index b39646cfeb1..00000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/LocaleValueHandler.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.openjpa; - -import java.util.Locale; -import java.util.Optional; -import org.apache.commons.lang3.LocaleUtils; -import org.apache.openjpa.jdbc.identifier.DBIdentifier; -import org.apache.openjpa.jdbc.kernel.JDBCStore; -import org.apache.openjpa.jdbc.meta.ValueMapping; -import org.apache.openjpa.jdbc.meta.strats.AbstractValueHandler; -import org.apache.openjpa.jdbc.schema.Column; -import org.apache.openjpa.jdbc.schema.ColumnIO; -import org.apache.openjpa.jdbc.sql.DBDictionary; -import org.apache.openjpa.meta.JavaTypes; - -public class LocaleValueHandler extends AbstractValueHandler { - - private static final long serialVersionUID = 487849441377630981L; - - private static final LocaleValueHandler INSTANCE = new LocaleValueHandler(); - - public static LocaleValueHandler getInstance() { - return INSTANCE; - } - - @Override - @Deprecated - public Column[] map(final ValueMapping vm, final String name, final ColumnIO io, final boolean adapt) { - DBDictionary dict = vm.getMappingRepository().getDBDictionary(); - DBIdentifier colName = DBIdentifier.newColumn( - name, Optional.ofNullable(dict).filter(DBDictionary::delimitAll).isPresent()); - return map(colName); - } - - public static Column[] map(final DBIdentifier name) { - Column col = new Column(); - col.setIdentifier(name); - col.setJavaType(JavaTypes.STRING); - return new Column[] { col }; - } - - @Override - public Object toDataStoreValue(final ValueMapping vm, final Object val, final JDBCStore store) { - return Optional.ofNullable(val).map(o -> ((Locale) o).toString()).orElse(null); - } - - @Override - public Object toObjectValue(final ValueMapping vm, final Object val) { - return Optional.ofNullable(val).map(o -> LocaleUtils.toLocale((String) o)).orElse(null); - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java index d48b8ec334c..1cd004a735a 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java @@ -22,6 +22,7 @@ import java.sql.Connection; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import javax.sql.DataSource; import org.apache.syncope.core.persistence.api.DomainHolder; @@ -98,8 +99,6 @@ public void setPersistenceUnitPostProcessors(final PersistenceUnitPostProcessor. } public void setJpaPropertyMap(final Map jpaProperties) { - if (jpaProperties != null) { - this.jpaPropertyMap.putAll(jpaProperties); - } + Optional.ofNullable(jpaProperties).ifPresent(jpaPropertyMap::putAll); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java index 281e4a17473..7fcc1575b01 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java @@ -20,8 +20,14 @@ import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.spi.PersistenceUnitInfo; -import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; -import org.apache.syncope.core.persistence.jpa.openjpa.ConnectorManagerRemoteCommitListener; +import java.util.Optional; +import javax.cache.Caching; +import javax.cache.configuration.FactoryBuilder; +import javax.cache.configuration.MutableCacheEntryListenerConfiguration; +import org.apache.syncope.core.persistence.jpa.ConnectorManagerRemoteCommitListener; +import org.apache.syncope.core.persistence.jpa.entity.JPAConnInstance; +import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource; +import org.hibernate.cache.spi.support.RegionNameQualifier; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; /** @@ -37,15 +43,13 @@ public class DomainEntityManagerFactoryBean extends LocalContainerEntityManagerF public void setCommonEntityManagerFactoryConf(final CommonEntityManagerFactoryConf commonEMFConf) { super.setJpaPropertyMap(commonEMFConf.getJpaPropertyMap()); - if (commonEMFConf.getPackagesToScan() != null) { - super.setPackagesToScan(commonEMFConf.getPackagesToScan()); - } + Optional.ofNullable(commonEMFConf.getPackagesToScan()). + ifPresent(super::setPackagesToScan); super.setValidationMode(commonEMFConf.getValidationMode()); - if (commonEMFConf.getPersistenceUnitPostProcessors() != null) { - super.setPersistenceUnitPostProcessors(commonEMFConf.getPersistenceUnitPostProcessors()); - } + Optional.ofNullable(commonEMFConf.getPersistenceUnitPostProcessors()). + ifPresent(super::setPersistenceUnitPostProcessors); } public void setConnectorManagerRemoteCommitListener( @@ -58,7 +62,19 @@ public void setConnectorManagerRemoteCommitListener( protected void postProcessEntityManagerFactory(final EntityManagerFactory emf, final PersistenceUnitInfo pui) { super.postProcessEntityManagerFactory(emf, pui); - OpenJPAEntityManagerFactorySPI emfspi = emf.unwrap(OpenJPAEntityManagerFactorySPI.class); - emfspi.getConfiguration().getRemoteCommitEventManager().addListener(connectorManagerRemoteCommitListener); + Optional.ofNullable(Caching.getCachingProvider().getCacheManager(). + getCache(RegionNameQualifier.INSTANCE.qualify( + pui.getPersistenceUnitName(), JPAConnInstance.class.getName()))). + ifPresent(cache -> cache.registerCacheEntryListener( + new MutableCacheEntryListenerConfiguration( + FactoryBuilder.factoryOf(connectorManagerRemoteCommitListener), + null, false, false))); + Optional.ofNullable(Caching.getCachingProvider().getCacheManager(). + getCache(RegionNameQualifier.INSTANCE.qualify( + pui.getPersistenceUnitName(), JPAExternalResource.class.getName()))). + ifPresent(cache -> cache.registerCacheEntryListener( + new MutableCacheEntryListenerConfiguration( + FactoryBuilder.factoryOf(connectorManagerRemoteCommitListener), + null, false, false))); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java index 54fb903b3b7..0483be9ff14 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java @@ -22,9 +22,12 @@ import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.PersistenceUnitTransactionType; import jakarta.persistence.PersistenceUnitUtil; import jakarta.persistence.Query; +import jakarta.persistence.SchemaManager; import jakarta.persistence.SynchronizationType; +import jakarta.persistence.TypedQueryReference; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.metamodel.Metamodel; import java.io.Closeable; @@ -32,17 +35,21 @@ import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.function.Function; import javax.sql.DataSource; import org.apache.syncope.common.keymaster.client.api.model.JPADomain; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.jpa.ConnectorManagerRemoteCommitListener; import org.apache.syncope.core.persistence.jpa.PersistenceProperties; -import org.apache.syncope.core.persistence.jpa.openjpa.ConnectorManagerRemoteCommitListener; import org.apache.syncope.core.provisioning.api.ConnectorManager; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jndi.JndiObjectFactoryBean; +import org.springframework.orm.jpa.JpaVendorAdapter; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; public class DomainRoutingEntityManagerFactory implements EntityManagerFactory, Closeable { @@ -68,25 +75,23 @@ public DomainRoutingEntityManagerFactory( protected void addToJpaPropertyMap( final DomainEntityManagerFactoryBean emf, - final OpenJpaVendorAdapter vendorAdapter, + final JpaVendorAdapter vendorAdapter, final String dbSchema, - final String orm, - final String metadataFactory) { + final String domain) { emf.getJpaPropertyMap().putAll(vendorAdapter.getJpaPropertyMap()); Optional.ofNullable(dbSchema). - ifPresent(s -> emf.getJpaPropertyMap().put("openjpa.jdbc.Schema", s)); + ifPresent(s -> emf.getJpaPropertyMap().put("hibernate.default_schema", s)); - Optional.ofNullable(metadataFactory). - ifPresent(m -> emf.getJpaPropertyMap().put("openjpa.MetaDataFactory", m.replace("##orm##", orm))); + emf.getJpaPropertyMap().put("hibernate.cache.region_prefix", domain); } public void master( final PersistenceProperties props, final JndiObjectFactoryBean dataSource) { - OpenJpaVendorAdapter vendorAdapter = new OpenJpaVendorAdapter(); + HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); vendorAdapter.setShowSql(false); vendorAdapter.setGenerateDdl(true); vendorAdapter.setDatabasePlatform(props.getDomain().getFirst().getDatabasePlatform()); @@ -98,14 +103,13 @@ public void master( emf.setJpaVendorAdapter(vendorAdapter); emf.setCommonEntityManagerFactoryConf(commonEMFConf); emf.setConnectorManagerRemoteCommitListener(new ConnectorManagerRemoteCommitListener( - connectorManager, resourceDAO, SyncopeConstants.MASTER_DOMAIN)); + this, connectorManager, resourceDAO, SyncopeConstants.MASTER_DOMAIN)); addToJpaPropertyMap( emf, vendorAdapter, props.getDomain().getFirst().getDbSchema(), - props.getDomain().getFirst().getOrm(), - props.getMetaDataFactory()); + SyncopeConstants.MASTER_DOMAIN); emf.afterPropertiesSet(); @@ -114,10 +118,9 @@ public void master( public void domain( final JPADomain domain, - final DataSource dataSource, - final String metadataFactory) { + final DataSource dataSource) { - OpenJpaVendorAdapter vendorAdapter = new OpenJpaVendorAdapter(); + HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); vendorAdapter.setShowSql(false); vendorAdapter.setGenerateDdl(true); vendorAdapter.setDatabasePlatform(domain.getDatabasePlatform()); @@ -129,9 +132,9 @@ public void domain( emf.setJpaVendorAdapter(vendorAdapter); emf.setCommonEntityManagerFactoryConf(commonEMFConf); emf.setConnectorManagerRemoteCommitListener(new ConnectorManagerRemoteCommitListener( - connectorManager, resourceDAO, domain.getKey())); + this, connectorManager, resourceDAO, domain.getKey())); - addToJpaPropertyMap(emf, vendorAdapter, domain.getDbSchema(), domain.getOrm(), metadataFactory); + addToJpaPropertyMap(emf, vendorAdapter, domain.getDbSchema(), domain.getKey()); emf.afterPropertiesSet(); @@ -229,4 +232,39 @@ public T unwrap(final Class cls) { public void addNamedEntityGraph(final String graphName, final EntityGraph entityGraph) { delegate().addNamedEntityGraph(graphName, entityGraph); } + + @Override + public String getName() { + return delegate().getName(); + } + + @Override + public PersistenceUnitTransactionType getTransactionType() { + return delegate().getTransactionType(); + } + + @Override + public SchemaManager getSchemaManager() { + return delegate().getSchemaManager(); + } + + @Override + public Map> getNamedQueries(final Class type) { + return delegate().getNamedQueries(type); + } + + @Override + public Map> getNamedEntityGraphs(final Class type) { + return delegate().getNamedEntityGraphs(type); + } + + @Override + public void runInTransaction(final Consumer cnsmr) { + delegate().runInTransaction(cnsmr); + } + + @Override + public R callInTransaction(final Function fnctn) { + return delegate().callInTransaction(fnctn); + } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/OpenJpaDialect.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/OpenJpaDialect.java deleted file mode 100644 index 83d4a5b6f01..00000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/OpenJpaDialect.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2002-2014 the original author or authors. - * - * 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 org.apache.syncope.core.persistence.jpa.spring; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceException; -import java.sql.Connection; -import org.apache.commons.logging.LogFactory; -import org.apache.openjpa.persistence.FetchPlan; -import org.apache.openjpa.persistence.OpenJPAEntityManager; -import org.apache.openjpa.persistence.OpenJPAPersistence; -import org.apache.openjpa.persistence.jdbc.IsolationLevel; -import org.apache.openjpa.persistence.jdbc.JDBCFetchPlan; -import org.springframework.jdbc.datasource.ConnectionHandle; -import org.springframework.jdbc.datasource.ConnectionHolder; -import org.springframework.jdbc.support.JdbcUtils; -import org.springframework.orm.jpa.DefaultJpaDialect; -import org.springframework.transaction.SavepointManager; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionException; - -/** - * {@link org.springframework.orm.jpa.JpaDialect} implementation for Apache OpenJPA. - * Developed and tested against OpenJPA 2.2. - * - * @author Juergen Hoeller - * @author Costin Leau - * @since 2.0 - */ -public class OpenJpaDialect extends DefaultJpaDialect { - - private static final long serialVersionUID = 2099118508988476959L; - - @Override - public Object beginTransaction(final EntityManager entityManager, final TransactionDefinition definition) - throws PersistenceException, TransactionException { - - OpenJPAEntityManager openJpaEntityManager = getOpenJPAEntityManager(entityManager); - - if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { - // Pass custom isolation level on to OpenJPA's JDBCFetchPlan configuration - FetchPlan fetchPlan = openJpaEntityManager.getFetchPlan(); - if (fetchPlan instanceof JDBCFetchPlan jDBCFetchPlan) { - IsolationLevel isolation = IsolationLevel.fromConnectionConstant(definition.getIsolationLevel()); - jDBCFetchPlan.setIsolation(isolation); - } - } - - entityManager.getTransaction().begin(); - - if (!definition.isReadOnly()) { - // Like with EclipseLink, make sure to start the logic transaction early so that other - // participants using the connection (such as JdbcTemplate) run in a transaction. - openJpaEntityManager.beginStore(); - } - - // Custom implementation for OpenJPA savepoint handling - return new OpenJpaTransactionData(openJpaEntityManager); - } - - @Override - public ConnectionHandle getJdbcConnection(final EntityManager entityManager, final boolean readOnly) - throws PersistenceException { - - return new OpenJpaConnectionHandle(getOpenJPAEntityManager(entityManager)); - } - - /** - * Return the OpenJPA-specific variant of {@code EntityManager}. - * - * @param em the generic {@code EntityManager} instance - * @return the OpenJPA-specific variant of {@code EntityManager} - */ - protected static OpenJPAEntityManager getOpenJPAEntityManager(final EntityManager em) { - return OpenJPAPersistence.cast(em); - } - - /** - * Transaction data Object exposed from {@code beginTransaction}, - * implementing the {@link SavepointManager} interface. - */ - private static class OpenJpaTransactionData implements SavepointManager { - - private final OpenJPAEntityManager entityManager; - - private int savepointCounter = 0; - - OpenJpaTransactionData(final OpenJPAEntityManager entityManager) { - this.entityManager = entityManager; - } - - @Override - public Object createSavepoint() throws TransactionException { - this.savepointCounter++; - String savepointName = ConnectionHolder.SAVEPOINT_NAME_PREFIX + this.savepointCounter; - this.entityManager.setSavepoint(savepointName); - return savepointName; - } - - @Override - public void rollbackToSavepoint(final Object savepoint) throws TransactionException { - this.entityManager.rollbackToSavepoint((String) savepoint); - } - - @Override - public void releaseSavepoint(final Object savepoint) throws TransactionException { - try { - this.entityManager.releaseSavepoint((String) savepoint); - } catch (Throwable ex) { - LogFactory.getLog(OpenJpaTransactionData.class).debug( - "Could not explicitly release OpenJPA savepoint", ex); - } - } - } - - /** - * {@link ConnectionHandle} implementation that fetches a new OpenJPA-provided - * Connection for every {@code getConnection} call and closes the Connection on - * {@code releaseConnection}. This is necessary because OpenJPA requires the - * fetched Connection to be closed before continuing EntityManager work. - * - * @see org.apache.openjpa.persistence.OpenJPAEntityManager#getConnection() - */ - private static class OpenJpaConnectionHandle implements ConnectionHandle { - - private final OpenJPAEntityManager entityManager; - - OpenJpaConnectionHandle(final OpenJPAEntityManager entityManager) { - this.entityManager = entityManager; - } - - @Override - public Connection getConnection() { - return (Connection) this.entityManager.getConnection(); - } - - @Override - public void releaseConnection(final Connection con) { - JdbcUtils.closeConnection(con); - } - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/OpenJpaVendorAdapter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/OpenJpaVendorAdapter.java deleted file mode 100644 index deaa696a3b9..00000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/OpenJpaVendorAdapter.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2002-2014 the original author or authors. - * - * 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 org.apache.syncope.core.persistence.jpa.spring; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.spi.PersistenceProvider; -import java.util.HashMap; -import java.util.Map; -import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; -import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI; -import org.apache.openjpa.persistence.PersistenceProviderImpl; -import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; -import org.springframework.orm.jpa.vendor.Database; - -/** - * {@link org.springframework.orm.jpa.JpaVendorAdapter} implementation for Apache OpenJPA. - * Developed and tested against OpenJPA 3.0. - * - * Exposes OpenJPA's persistence provider and EntityManager extension interface, - * and adapts {@link AbstractJpaVendorAdapter}'s common configuration settings. - * No support for the detection of annotated packages (through - * {@link org.springframework.orm.jpa.persistenceunit.SmartPersistenceUnitInfo#getManagedPackages()}) - * since OpenJPA doesn't use package-level metadata. - * - * @author Juergen Hoeller - * @author Costin Leau - * @since 2.0 - * @see OpenJpaDialect - * @see org.apache.openjpa.persistence.PersistenceProviderImpl - * @see org.apache.openjpa.persistence.OpenJPAEntityManager - */ -public class OpenJpaVendorAdapter extends AbstractJpaVendorAdapter { - - private final PersistenceProvider persistenceProvider = new PersistenceProviderImpl(); - - private final OpenJpaDialect jpaDialect = new OpenJpaDialect(); - - @Override - public PersistenceProvider getPersistenceProvider() { - return this.persistenceProvider; - } - - @Override - public String getPersistenceProviderRootPackage() { - return "org.apache.openjpa"; - } - - @Override - public Map getJpaPropertyMap() { - Map jpaProperties = new HashMap<>(); - - if (getDatabasePlatform() != null) { - jpaProperties.put("openjpa.jdbc.DBDictionary", getDatabasePlatform()); - } else if (getDatabase() != null) { - String databaseDictonary = determineDatabaseDictionary(getDatabase()); - if (databaseDictonary != null) { - jpaProperties.put("openjpa.jdbc.DBDictionary", databaseDictonary); - } - } - - if (isGenerateDdl()) { - jpaProperties.put("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true)"); - } - if (isShowSql()) { - // Taken from the OpenJPA 0.9.6 docs ("Standard OpenJPA Log Configuration + All SQL Statements") - jpaProperties.put("openjpa.Log", "DefaultLevel=WARN, Runtime=INFO, Tool=INFO, SQL=TRACE"); - } - - return jpaProperties; - } - - /** - * Determine the OpenJPA database dictionary name for the given database. - * - * @param database the specified database - * @return the OpenJPA database dictionary name, or {@code null} if none found - */ - protected static String determineDatabaseDictionary(final Database database) { - return switch (database) { - case DB2 -> - "db2"; - case DERBY -> - "derby"; - case HSQL -> - "hsql(SimulateLocking=true)"; - case INFORMIX -> - "informix"; - case MYSQL -> - "mysql"; - case ORACLE -> - "oracle"; - case POSTGRESQL -> - "postgres"; - case SQL_SERVER -> - "sqlserver"; - case SYBASE -> - "sybase"; - default -> - null; - }; - } - - @Override - public OpenJpaDialect getJpaDialect() { - return this.jpaDialect; - } - - @Override - public Class getEntityManagerFactoryInterface() { - return OpenJPAEntityManagerFactorySPI.class; - } - - @Override - public Class getEntityManagerInterface() { - return OpenJPAEntityManagerSPI.class; - } -} diff --git a/core/persistence-jpa/src/main/resources/META-INF/indexes.xml b/core/persistence-jpa/src/main/resources/META-INF/indexes.xml index 9940f5d025b..da772b83574 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/indexes.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/indexes.xml @@ -40,10 +40,8 @@ under the License. CREATE INDEX AnyObject_lower_name ON AnyObject(type_id,LOWER(name)) CREATE INDEX AnyObject_plainAttrs_idx ON AnyObject USING gin ((plainAttrs) jsonb_path_ops) - CREATE INDEX UDynGroupMembers_any_id ON UDynGroupMembers(any_id) - CREATE INDEX UDynGroupMembers_group_id ON UDynGroupMembers(group_id) - CREATE INDEX ADynGroupMembers_any_id ON ADynGroupMembers(any_id) - CREATE INDEX ADynGroupMembers_group_id ON ADynGroupMembers(group_id) + CREATE INDEX DynGroupMembers_any_id ON DynGroupMembers(any_id) + CREATE INDEX DynGroupMembers_group_id ON DynGroupMembers(group_id) CREATE INDEX DynRoleMembers_any_id ON DynRoleMembers(any_id) CREATE INDEX DynRoleMembers_role_id ON DynRoleMembers(role_id) diff --git a/core/persistence-jpa/src/main/resources/META-INF/mariadb/indexes.xml b/core/persistence-jpa/src/main/resources/META-INF/mariadb/indexes.xml index 8bc611e0ae7..d08cae03bec 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/mariadb/indexes.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/mariadb/indexes.xml @@ -32,10 +32,8 @@ under the License. CREATE INDEX AnyObject_realm_id ON AnyObject(realm_id) CREATE UNIQUE INDEX AnyObject_name ON AnyObject(type_id,name) - CREATE INDEX UDynGroupMembers_any_id ON UDynGroupMembers(any_id) - CREATE INDEX UDynGroupMembers_group_id ON UDynGroupMembers(group_id) - CREATE INDEX ADynGroupMembers_any_id ON ADynGroupMembers(any_id) - CREATE INDEX ADynGroupMembers_group_id ON ADynGroupMembers(group_id) + CREATE INDEX DynGroupMembers_any_id ON DynGroupMembers(any_id) + CREATE INDEX DynGroupMembers_group_id ON DynGroupMembers(group_id) CREATE INDEX DynRoleMembers_any_id ON DynRoleMembers(any_id) CREATE INDEX DynRoleMembers_role_id ON DynRoleMembers(role_id) diff --git a/core/persistence-jpa/src/main/resources/META-INF/mariadb/spring-orm.xml b/core/persistence-jpa/src/main/resources/META-INF/mariadb/spring-orm.xml index 041e21be8a3..605a8c4fa30 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/mariadb/spring-orm.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/mariadb/spring-orm.xml @@ -17,23 +17,21 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> - - + xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence/orm + https://jakarta.ee/xml/ns/persistence/orm/orm_3_2.xsd" + version="3.2"> + + FIELD - - - - + - + diff --git a/core/persistence-jpa/src/main/resources/META-INF/mariadb/views.xml b/core/persistence-jpa/src/main/resources/META-INF/mariadb/views.xml index 00af8ffba26..32897e29feb 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/mariadb/views.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/mariadb/views.xml @@ -20,14 +20,8 @@ under the License. - - CREATE TABLE UDynGroupMembers( - any_id CHAR(36), - group_id CHAR(36), - UNIQUE(any_id, group_id)) - - - CREATE TABLE ADynGroupMembers( + + CREATE TABLE DynGroupMembers( anyType_id VARCHAR(255), any_id CHAR(36), group_id CHAR(36), diff --git a/core/persistence-jpa/src/main/resources/META-INF/mysql/indexes.xml b/core/persistence-jpa/src/main/resources/META-INF/mysql/indexes.xml index 8bc611e0ae7..d08cae03bec 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/mysql/indexes.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/mysql/indexes.xml @@ -32,10 +32,8 @@ under the License. CREATE INDEX AnyObject_realm_id ON AnyObject(realm_id) CREATE UNIQUE INDEX AnyObject_name ON AnyObject(type_id,name) - CREATE INDEX UDynGroupMembers_any_id ON UDynGroupMembers(any_id) - CREATE INDEX UDynGroupMembers_group_id ON UDynGroupMembers(group_id) - CREATE INDEX ADynGroupMembers_any_id ON ADynGroupMembers(any_id) - CREATE INDEX ADynGroupMembers_group_id ON ADynGroupMembers(group_id) + CREATE INDEX DynGroupMembers_any_id ON DynGroupMembers(any_id) + CREATE INDEX DynGroupMembers_group_id ON DynGroupMembers(group_id) CREATE INDEX DynRoleMembers_any_id ON DynRoleMembers(any_id) CREATE INDEX DynRoleMembers_role_id ON DynRoleMembers(role_id) diff --git a/core/persistence-jpa/src/main/resources/META-INF/mysql/spring-orm.xml b/core/persistence-jpa/src/main/resources/META-INF/mysql/spring-orm.xml index 041e21be8a3..605a8c4fa30 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/mysql/spring-orm.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/mysql/spring-orm.xml @@ -17,23 +17,21 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> - - + xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence/orm + https://jakarta.ee/xml/ns/persistence/orm/orm_3_2.xsd" + version="3.2"> + + FIELD - - - - + - + diff --git a/core/persistence-jpa/src/main/resources/META-INF/mysql/views.xml b/core/persistence-jpa/src/main/resources/META-INF/mysql/views.xml index 00af8ffba26..32897e29feb 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/mysql/views.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/mysql/views.xml @@ -20,14 +20,8 @@ under the License. - - CREATE TABLE UDynGroupMembers( - any_id CHAR(36), - group_id CHAR(36), - UNIQUE(any_id, group_id)) - - - CREATE TABLE ADynGroupMembers( + + CREATE TABLE DynGroupMembers( anyType_id VARCHAR(255), any_id CHAR(36), group_id CHAR(36), diff --git a/core/persistence-jpa/src/main/resources/META-INF/oracle/indexes.xml b/core/persistence-jpa/src/main/resources/META-INF/oracle/indexes.xml index f86e5e81b7e..2fbe901ac81 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/oracle/indexes.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/oracle/indexes.xml @@ -40,10 +40,8 @@ under the License. CREATE SEARCH INDEX SyncopeUser_plainAttrs_Index ON SyncopeUser(plainAttrs) FOR JSON CREATE SEARCH INDEX LinkedAccount_plainAttrs_Index ON LinkedAccount(plainAttrs) FOR JSON - CREATE INDEX UDynGroupMembers_any_id ON UDynGroupMembers(any_id) - CREATE INDEX UDynGroupMembers_group_id ON UDynGroupMembers(group_id) - CREATE INDEX ADynGroupMembers_any_id ON ADynGroupMembers(any_id) - CREATE INDEX ADynGroupMembers_group_id ON ADynGroupMembers(group_id) + CREATE INDEX DynGroupMembers_any_id ON DynGroupMembers(any_id) + CREATE INDEX DynGroupMembers_group_id ON DynGroupMembers(group_id) CREATE INDEX DynRoleMembers_any_id ON DynRoleMembers(any_id) CREATE INDEX DynRoleMembers_role_id ON DynRoleMembers(role_id) diff --git a/core/persistence-jpa/src/main/resources/META-INF/oracle/spring-orm.xml b/core/persistence-jpa/src/main/resources/META-INF/oracle/spring-orm.xml index 6e84c95455f..3cee7ab8f63 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/oracle/spring-orm.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/oracle/spring-orm.xml @@ -17,22 +17,21 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> - - + xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence/orm + https://jakarta.ee/xml/ns/persistence/orm/orm_3_2.xsd" + version="3.2"> + + FIELD - - - - + + diff --git a/core/persistence-jpa/src/main/resources/META-INF/oracle/views.xml b/core/persistence-jpa/src/main/resources/META-INF/oracle/views.xml index 9f0a69149f1..53a0bdc3e81 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/oracle/views.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/oracle/views.xml @@ -20,14 +20,8 @@ under the License. - - CREATE TABLE UDynGroupMembers( - any_id CHAR(36), - group_id CHAR(36), - UNIQUE(any_id, group_id)) - - - CREATE TABLE ADynGroupMembers( + + CREATE TABLE DynGroupMembers( anyType_id VARCHAR(255), any_id CHAR(36), group_id CHAR(36), diff --git a/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml b/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml index 66aec95e14d..bf1d5bc0de7 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml @@ -17,28 +17,25 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> - - + xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence/orm + https://jakarta.ee/xml/ns/persistence/orm/orm_3_2.xsd" + version="3.2"> + + FIELD - - - - + - + - @@ -47,7 +44,6 @@ under the License. - @@ -56,7 +52,6 @@ under the License. - @@ -65,7 +60,6 @@ under the License. - @@ -74,7 +68,6 @@ under the License. - diff --git a/core/persistence-jpa/src/main/resources/META-INF/views.xml b/core/persistence-jpa/src/main/resources/META-INF/views.xml index 9f0a69149f1..53a0bdc3e81 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/views.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/views.xml @@ -20,14 +20,8 @@ under the License. - - CREATE TABLE UDynGroupMembers( - any_id CHAR(36), - group_id CHAR(36), - UNIQUE(any_id, group_id)) - - - CREATE TABLE ADynGroupMembers( + + CREATE TABLE DynGroupMembers( anyType_id VARCHAR(255), any_id CHAR(36), group_id CHAR(36), diff --git a/core/persistence-jpa/src/main/resources/persistence-enhance.xml b/core/persistence-jpa/src/main/resources/persistence-enhance.xml deleted file mode 100644 index 1fbd550ebe1..00000000000 --- a/core/persistence-jpa/src/main/resources/persistence-enhance.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - META-INF/spring-orm.xml - NONE - - - diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/AbstractTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/AbstractTest.java index 3f7cff474bb..8d901c874cd 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/AbstractTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/AbstractTest.java @@ -96,7 +96,7 @@ private static boolean classExists(final String name) { if (classExists("org.postgresql.Driver")) { JDBC_DRIVER = "org.postgresql.Driver"; - DATABASE_PLATFORM = "org.apache.openjpa.jdbc.sql.PostgresDictionary"; + DATABASE_PLATFORM = "org.apache.syncope.core.persistence.jpa.hibernate.SyncopePostgreSQLDialect"; ORM = "META-INF/spring-orm.xml"; INDEXES = "classpath:META-INF/indexes.xml"; VIEWS = "classpath:META-INF/views.xml"; @@ -120,8 +120,7 @@ private static boolean classExists(final String name) { } } else if (classExists("com.mysql.cj.jdbc.Driver")) { JDBC_DRIVER = "com.mysql.cj.jdbc.Driver"; - DATABASE_PLATFORM = "org.apache.openjpa.jdbc.sql.MySQLDictionary(" - + "blobTypeName=LONGBLOB,dateFractionDigits=3,useSetStringForClobs=true)"; + DATABASE_PLATFORM = "org.hibernate.dialect.MySQLDialect"; ORM = "META-INF/mysql/spring-orm.xml"; INDEXES = "classpath:META-INF/mysql/indexes.xml"; VIEWS = "classpath:META-INF/mysql/views.xml"; @@ -143,8 +142,7 @@ private static boolean classExists(final String name) { JDBC2_URL_SUPPLIER = twoDomain::getJdbcUrl; } else if (classExists("org.mariadb.jdbc.Driver")) { JDBC_DRIVER = "org.mariadb.jdbc.Driver"; - DATABASE_PLATFORM = "org.apache.openjpa.jdbc.sql.MariaDBDictionary(" - + "blobTypeName=LONGBLOB,dateFractionDigits=3)"; + DATABASE_PLATFORM = "org.hibernate.dialect.MariaDBDialect"; ORM = "META-INF/mariadb/spring-orm.xml"; INDEXES = "classpath:META-INF/mariadb/indexes.xml"; VIEWS = "classpath:META-INF/mariadb/views.xml"; @@ -166,7 +164,7 @@ private static boolean classExists(final String name) { JDBC2_URL_SUPPLIER = twoDomain::getJdbcUrl; } else if (classExists("oracle.jdbc.OracleDriver")) { JDBC_DRIVER = "oracle.jdbc.OracleDriver"; - DATABASE_PLATFORM = "org.apache.openjpa.jdbc.sql.OracleDictionary"; + DATABASE_PLATFORM = "org.hibernate.dialect.OracleDialect"; ORM = "META-INF/oracle/spring-orm.xml"; INDEXES = "classpath:META-INF/oracle/indexes.xml"; VIEWS = "classpath:META-INF/oracle/views.xml"; diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java index 104d916e534..94777fa33b2 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java @@ -57,22 +57,31 @@ public void beforeEach() { public void googleMfaToken() { String id = SecureRandomUtils.generateRandomUUID().toString(); - createAuthProfileWithToken(id, 123456); + AuthProfile profile = entityFactory.newEntity(AuthProfile.class); + profile.setOwner(id); + GoogleMfaAuthToken token = new GoogleMfaAuthToken.Builder().issueDate(LocalDateTime.now()).token(12345).build(); + profile.add(token); + authProfileDAO.save(profile); - Optional result = authProfileDAO.findByOwner(id); - assertTrue(result.isPresent()); + entityManager.flush(); - assertFalse(authProfileDAO.findAll(Pageable.unpaged()).isEmpty()); + profile = authProfileDAO.findByOwner(id).orElseThrow(); + assertEquals(profile, authProfileDAO.findById(profile.getKey()).orElseThrow()); + assertEquals(1, profile.getGoogleMfaAuthTokens().size()); + assertEquals(token, profile.getGoogleMfaAuthTokens().getFirst()); - AuthProfile authProfile = result.get(); - result = authProfileDAO.findById(authProfile.getKey()); - assertTrue(result.isPresent()); + profile.setOwner("SyncopeCreate-New"); + profile.getGoogleMfaAuthTokens().clear(); + profile.add(new GoogleMfaAuthToken.Builder().issueDate(LocalDateTime.now()).token(54321).build()); + authProfileDAO.save(profile); - authProfile.setOwner("SyncopeCreate-New"); - authProfile.setGoogleMfaAuthTokens(List.of()); - authProfileDAO.save(authProfile); + entityManager.flush(); assertFalse(authProfileDAO.findByOwner(id).isPresent()); + + profile = authProfileDAO.findByOwner("SyncopeCreate-New").orElseThrow(); + assertEquals(1, profile.getGoogleMfaAuthTokens().size()); + assertEquals(54321, profile.getGoogleMfaAuthTokens().getFirst().getOtp()); } @Test @@ -106,7 +115,7 @@ public void webAuthnRegisteredDevice() { assertTrue(result.isPresent()); authProfile.setOwner("newowner"); - authProfile.setWebAuthnDeviceCredentials(List.of()); + authProfile.getWebAuthnDeviceCredentials().clear(); authProfileDAO.save(authProfile); assertFalse(authProfileDAO.findByOwner(id).isPresent()); @@ -118,22 +127,18 @@ public void googleMfaAccount() { createAuthProfileWithAccount(id); - Optional result = authProfileDAO.findByOwner(id); - assertTrue(result.isPresent()); + AuthProfile authProfile = authProfileDAO.findByOwner(id).orElseThrow(); assertFalse(authProfileDAO.findAll(Pageable.unpaged()).isEmpty()); - AuthProfile authProfile = result.get(); - result = authProfileDAO.findById(authProfile.getKey()); - assertTrue(result.isPresent()); + authProfile = authProfileDAO.findById(authProfile.getKey()).orElseThrow(); String secret = SecureRandomUtils.generateRandomUUID().toString(); - List googleMfaAuthAccounts = authProfile.getGoogleMfaAuthAccounts(); - assertFalse(googleMfaAuthAccounts.isEmpty()); - GoogleMfaAuthAccount googleMfaAuthAccount = googleMfaAuthAccounts.getFirst(); + GoogleMfaAuthAccount googleMfaAuthAccount = authProfile.getGoogleMfaAuthAccounts().getFirst(); googleMfaAuthAccount.setSecretKey(secret); - authProfile.setGoogleMfaAuthAccounts(googleMfaAuthAccounts); + authProfile.getGoogleMfaAuthAccounts().clear(); + authProfile.add(googleMfaAuthAccount); authProfile = authProfileDAO.save(authProfile); assertEquals(secret, authProfile.getGoogleMfaAuthAccounts().getFirst().getSecretKey()); } @@ -155,26 +160,19 @@ public void impersonationAccounts() { mapToObj(i -> new ImpersonationAccount.Builder().impersonated("impersonatee" + i).build()). toList(); - authProfile.setImpersonationAccounts(accounts); + authProfile.getImpersonationAccounts().clear(); + accounts.forEach(authProfile::add); authProfile = authProfileDAO.save(authProfile); assertEquals(accounts.size(), authProfile.getImpersonationAccounts().size()); } - private AuthProfile createAuthProfileWithToken(final String owner, final Integer otp) { - AuthProfile profile = entityFactory.newEntity(AuthProfile.class); - profile.setOwner(owner); - GoogleMfaAuthToken token = new GoogleMfaAuthToken.Builder().issueDate(LocalDateTime.now()).token(otp).build(); - profile.setGoogleMfaAuthTokens(List.of(token)); - return authProfileDAO.save(profile); - } - private AuthProfile createAuthProfileWithWebAuthnDevice( final String owner, final List credentials) { AuthProfile profile = entityFactory.newEntity(AuthProfile.class); profile.setOwner(owner); - profile.setWebAuthnDeviceCredentials(credentials); + credentials.forEach(profile::add); return authProfileDAO.save(profile); } @@ -189,7 +187,7 @@ private AuthProfile createAuthProfileWithAccount(final String owner) { .name(SecureRandomUtils.generateRandomUUID().toString()) .username(owner) .build(); - profile.setGoogleMfaAuthAccounts(List.of(account)); + profile.getGoogleMfaAuthAccounts().add(account); return authProfileDAO.save(profile); } } diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConnInstanceTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConnInstanceTest.java index 18476fdf94a..b91c28e52bb 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConnInstanceTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConnInstanceTest.java @@ -24,7 +24,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import org.apache.syncope.common.lib.SyncopeConstants; @@ -32,6 +31,7 @@ import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.syncope.common.lib.types.IdMEntitlement; import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.entity.ConnInstance; import org.apache.syncope.core.persistence.jpa.AbstractTest; import org.apache.syncope.core.spring.security.DelegatedAdministrationException; @@ -50,6 +50,9 @@ public class ConnInstanceTest extends AbstractTest { @Autowired private ConnInstanceDAO connInstanceDAO; + @Autowired + private RealmDAO realmDAO; + @Test public void findAll() { List authorities = IdMEntitlement.values().stream(). @@ -95,6 +98,7 @@ public void save() { connInstance.setBundleName("org.apache.syncope.core.persistence.test.util"); connInstance.setDisplayName("New"); connInstance.setConnRequestTimeout(60); + connInstance.setAdminRealm(realmDAO.getRoot()); // set the connector configuration ConnConfPropSchema endpointSchema = new ConnConfPropSchema(); @@ -113,10 +117,8 @@ public void save() { servicename.setSchema(servicenameSchema); endpoint.getValues().add("Provisioning"); - List conf = new ArrayList<>(); - conf.add(endpoint); - conf.add(servicename); - connInstance.setConf(conf); + connInstance.getConf().add(endpoint); + connInstance.getConf().add(servicename); assertFalse(connInstance.getConf().isEmpty()); // perform save operation diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java index b7523afa089..e354f1f8485 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java @@ -186,11 +186,9 @@ public void checkForDropdownType() { @Test public void saveInvalidSchema() { - assertThrows(InvalidEntityException.class, () -> { - PlainSchema schema = entityFactory.newEntity(PlainSchema.class); - schema.setKey("username"); - plainSchemaDAO.save(schema); - }); + PlainSchema schema = entityFactory.newEntity(PlainSchema.class); + schema.setKey("username"); + assertThrows(InvalidEntityException.class, () -> plainSchemaDAO.save(schema)); } @Test @@ -207,11 +205,7 @@ public void issueSYNCOPE418() { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setKey("http://schemas.examples.org/security/authorization/organizationUnit"); - try { - plainSchemaDAO.save(schema); - fail("This should not happen"); - } catch (InvalidEntityException e) { - assertTrue(e.hasViolation(EntityViolationType.InvalidKey)); - } + InvalidEntityException iee = assertThrows(InvalidEntityException.class, () -> plainSchemaDAO.save(schema)); + assertTrue(iee.hasViolation(EntityViolationType.InvalidKey)); } } diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2IdPEntityTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2IdPEntityTest.java index 7b31ed4fc65..4d6bb9b6d5a 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2IdPEntityTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2IdPEntityTest.java @@ -70,14 +70,15 @@ public void save() { public void update() { SAML2IdPEntity entity = create("SyncopeUpdate"); assertNotNull(entity); - entity.setKey("OtherSyncope"); + + entity.setMetadata("metadata2".getBytes(StandardCharsets.UTF_8)); entity = saml2IdPEntityDAO.save(entity); assertNotNull(entity); - + entityManager.flush(); SAML2IdPEntity found = saml2IdPEntityDAO.findById(entity.getKey()).orElseThrow(); - assertEquals("OtherSyncope", found.getKey()); + assertEquals("metadata2", new String(found.getMetadata(), StandardCharsets.UTF_8)); } } diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceTest.java index a0dd617e1e9..59b85e2e180 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceTest.java @@ -47,14 +47,11 @@ public class ConnInstanceTest extends AbstractTest { public void deleteCascade() { ConnInstance connInstance = connInstanceDAO.findById("fcf9f2b0-f7d6-42c9-84a6-61b28255a42b").orElseThrow(); - List resources = connInstance.getResources(); - assertNotNull(resources); + List resources = resourceDAO.findByConnInstance(connInstance.getKey()); assertFalse(resources.isEmpty()); connInstanceDAO.deleteById(connInstance.getKey()); - entityManager.flush(); - assertTrue(connInstanceDAO.findById("fcf9f2b0-f7d6-42c9-84a6-61b28255a42b").isEmpty()); for (ExternalResource resource : resources) { @@ -67,14 +64,10 @@ public void issue176() { ConnInstance connInstance = connInstanceDAO.findById("fcf9f2b0-f7d6-42c9-84a6-61b28255a42b").orElseThrow(); assertTrue(connInstance.getCapabilities().isEmpty()); - List resources = connInstance.getResources(); - assertNotNull(resources); + List resources = resourceDAO.findByConnInstance(connInstance.getKey()); assertEquals(4, resources.size()); - assertTrue( - "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(0).getKey()) - || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(1).getKey()) - || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(2).getKey()) - || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(3).getKey())); + assertTrue(resources.stream().map(ExternalResource::getKey). + anyMatch("ws-target-resource-nopropagation"::equals)); connInstance.getCapabilities().add(ConnectorCapability.SEARCH); @@ -82,13 +75,9 @@ public void issue176() { assertNotNull(connInstance); assertFalse(connInstance.getCapabilities().isEmpty()); - resources = connInstance.getResources(); - assertNotNull(resources); + resources = resourceDAO.findByConnInstance(connInstance.getKey()); assertEquals(4, resources.size()); - assertTrue( - "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(0).getKey()) - || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(1).getKey()) - || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(2).getKey()) - || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(3).getKey())); + assertTrue(resources.stream().map(ExternalResource::getKey). + anyMatch("ws-target-resource-nopropagation"::equals)); } } diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java index dea60cddf42..ee611ed2e72 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java @@ -45,20 +45,18 @@ import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.persistence.api.entity.RelationshipType; -import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.api.entity.group.GRelationship; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.group.GroupTypeExtension; -import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; import org.apache.syncope.core.persistence.api.entity.user.UMembership; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.jpa.AbstractTest; import org.apache.syncope.core.persistence.jpa.dao.repo.GroupRepoExt; -import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership; -import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership; +import org.apache.syncope.core.persistence.jpa.entity.JPADynGroupMembership; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Pageable; @@ -123,7 +121,7 @@ public void uMemberships() { assertEquals( memberships.stream().map(m -> m.getLeftEnd().getKey()).collect(Collectors.toSet()), - new HashSet<>(groupDAO.findUMembers("37d15e4c-cdc1-460b-a591-8505c8133806"))); + new HashSet<>(groupDAO.findUMembers("37d15e4c-cdc1-460b-a591-8505c8133806"))); assertTrue(groupDAO.existsUMembership( "74cd8ece-715a-44a4-a736-e17b46c4e7e6", "37d15e4c-cdc1-460b-a591-8505c8133806")); @@ -139,19 +137,17 @@ public void uMemberships() { @Test public void saveWithTwoOwners() { - assertThrows(InvalidEntityException.class, () -> { - Group root = groupDAO.findByName("root").orElseThrow(); + Group root = groupDAO.findByName("root").orElseThrow(); - User user = userDAO.findByUsername("rossini").orElseThrow(); + User user = userDAO.findByUsername("rossini").orElseThrow(); - Group group = entityFactory.newEntity(Group.class); - group.setRealm(realmDAO.getRoot()); - group.setName("error"); - group.setUserOwner(user); - group.setGroupOwner(root); + Group group = entityFactory.newEntity(Group.class); + group.setRealm(realmDAO.getRoot()); + group.setName("error"); + group.setUserOwner(user); + group.setGroupOwner(root); - groupDAO.save(group); - }); + assertThrows(InvalidEntityException.class, () -> groupDAO.save(group)); } @Test @@ -202,7 +198,7 @@ public void create() { group = groupDAO.findByName("new").orElseThrow(); assertEquals(1, group.getTypeExtensions().size()); - assertEquals(2, group.getTypeExtension(anyTypeDAO.getUser()).get().getAuxClasses().size()); + assertEquals(2, group.getTypeExtension(anyTypeDAO.getUser()).orElseThrow().getAuxClasses().size()); } @Test @@ -237,7 +233,7 @@ public void delete() { */ private List findDynGroups(final User user) { Query query = entityManager.createNativeQuery( - "SELECT group_id FROM " + GroupRepoExt.UDYNMEMB_TABLE + " WHERE any_id=?"); + "SELECT group_id FROM " + GroupRepoExt.DYNMEMB_TABLE + " WHERE any_id=?"); query.setParameter(1, user.getKey()); @SuppressWarnings("unchecked") @@ -271,11 +267,12 @@ public void udynMembership() { group.setRealm(realmDAO.getRoot()); group.setName("new"); - UDynGroupMembership dynMembership = entityFactory.newEntity(UDynGroupMembership.class); + DynGroupMembership dynMembership = entityFactory.newEntity(DynGroupMembership.class); + dynMembership.setAnyType(anyTypeDAO.getUser()); dynMembership.setFIQLCond("cool==true"); dynMembership.setGroup(group); - group.setUDynMembership(dynMembership); + group.add(dynMembership); Group actual = groupDAO.saveAndRefreshDynMemberships(group); assertNotNull(actual); @@ -284,9 +281,9 @@ public void udynMembership() { // 2. verify that dynamic membership is there actual = groupDAO.findById(actual.getKey()).orElseThrow(); - assertNotNull(actual.getUDynMembership()); - assertNotNull(actual.getUDynMembership().getKey()); - assertEquals(actual, actual.getUDynMembership().getGroup()); + assertNotNull(actual.getDynMembership(anyTypeDAO.getUser()).orElseThrow()); + assertNotNull(actual.getDynMembership(anyTypeDAO.getUser()).orElseThrow().getKey()); + assertEquals(actual, actual.getDynMembership(anyTypeDAO.getUser()).orElseThrow().getGroup()); // 3. verify that expected users have the created group dynamically assigned List members = groupDAO.findUDynMembers(actual); @@ -296,7 +293,8 @@ public void udynMembership() { user = userDAO.findByUsername("bellini").orElseThrow(); List dynGroupMemberships = findDynGroups(user); assertEquals(1, dynGroupMemberships.size()); - assertTrue(dynGroupMemberships.contains(actual.getUDynMembership().getGroup())); + assertTrue(dynGroupMemberships.contains( + actual.getDynMembership(anyTypeDAO.getUser()).orElseThrow().getGroup())); // 4. delete the new user and verify that dynamic membership was updated userDAO.deleteById(newUserKey); @@ -309,13 +307,13 @@ public void udynMembership() { assertEquals("c9b2dec2-00a7-4855-97c0-d854842b4b24", members.getFirst()); // 5. delete group and verify that dynamic membership was also removed - String dynMembershipKey = actual.getUDynMembership().getKey(); + String dynMembershipKey = actual.getDynMembership(anyTypeDAO.getUser()).orElseThrow().getKey(); groupDAO.delete(actual); entityManager.flush(); - assertNull(entityManager.find(JPAUDynGroupMembership.class, dynMembershipKey)); + assertNull(entityManager.find(JPADynGroupMembership.class, dynMembershipKey)); dynGroupMemberships = findDynGroups(user); assertTrue(dynGroupMemberships.isEmpty()); @@ -328,7 +326,7 @@ public void udynMembership() { */ private List findDynGroups(final AnyObject anyObject) { Query query = entityManager.createNativeQuery( - "SELECT group_id FROM " + GroupRepoExt.ADYNMEMB_TABLE + " WHERE any_id=?"); + "SELECT group_id FROM " + GroupRepoExt.DYNMEMB_TABLE + " WHERE any_id=?"); query.setParameter(1, anyObject.getKey()); @SuppressWarnings("unchecked") @@ -362,7 +360,7 @@ public void adynMembership() { group.setRealm(realmDAO.getRoot()); group.setName("new"); - ADynGroupMembership dynMembership = entityFactory.newEntity(ADynGroupMembership.class); + DynGroupMembership dynMembership = entityFactory.newEntity(DynGroupMembership.class); dynMembership.setAnyType(anyTypeDAO.findById("PRINTER").orElseThrow()); dynMembership.setFIQLCond("model==Canon MFC8030"); dynMembership.setGroup(group); @@ -376,9 +374,10 @@ public void adynMembership() { // 2. verify that dynamic membership is there actual = groupDAO.findById(actual.getKey()).orElseThrow(); - assertNotNull(actual.getADynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).get()); - assertNotNull(actual.getADynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).get().getKey()); - assertEquals(actual, actual.getADynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).get().getGroup()); + assertNotNull(actual.getDynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).orElseThrow()); + assertNotNull(actual.getDynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).orElseThrow().getKey()); + assertEquals(actual, actual.getDynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()). + orElseThrow().getGroup()); // 3. verify that expected any objects have the created group dynamically assigned List members = groupDAO.findADynMembers(actual).stream().filter( @@ -392,8 +391,8 @@ public void adynMembership() { anyObject = anyObjectDAO.findById("fc6dbc3a-6c07-4965-8781-921e7401a4a5").orElseThrow(); Collection dynGroupMemberships = findDynGroups(anyObject); assertEquals(1, dynGroupMemberships.size()); - assertTrue(dynGroupMemberships.contains(actual.getADynMembership(anyTypeDAO.findById("PRINTER"). - orElseThrow()).get().getGroup())); + assertTrue(dynGroupMemberships.contains(actual.getDynMembership(anyTypeDAO.findById("PRINTER"). + orElseThrow()).orElseThrow().getGroup())); // 4. delete the new any object and verify that dynamic membership was updated anyObjectDAO.deleteById(newAnyObjectKey); @@ -408,13 +407,14 @@ public void adynMembership() { assertEquals("fc6dbc3a-6c07-4965-8781-921e7401a4a5", members.getFirst()); // 5. delete group and verify that dynamic membership was also removed - String dynMembershipKey = actual.getADynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).get().getKey(); + String dynMembershipKey = actual.getDynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()). + orElseThrow().getKey(); groupDAO.delete(actual); entityManager.flush(); - assertNull(entityManager.find(JPAADynGroupMembership.class, dynMembershipKey)); + assertNull(entityManager.find(JPADynGroupMembership.class, dynMembershipKey)); dynGroupMemberships = findDynGroups(anyObject); assertTrue(dynGroupMemberships.isEmpty()); @@ -446,7 +446,7 @@ public void relationships() { group = groupDAO.findByName("root").orElseThrow(); assertEquals(1, group.getRelationships().size()); assertEquals("8559d14d-58c2-46eb-a2d4-a7d35161e8f8", - group.getRelationships().getFirst().getRightEnd().getKey()); + group.getRelationships().getFirst().getRightEnd().getKey()); } @Test @@ -470,7 +470,8 @@ public void issueSYNCOPE1512() { entityManager.flush(); group = groupDAO.findById(group.getKey()).orElseThrow(); - assertEquals("syncope's group", group.getPlainAttr("title").get().getValuesAsStrings().getFirst()); - assertEquals("syncope's group", group.getPlainAttr("originalName").get().getValuesAsStrings().getFirst()); + assertEquals("syncope's group", group.getPlainAttr("title").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("syncope's group", group.getPlainAttr("originalName").orElseThrow(). + getValuesAsStrings().getFirst()); } } diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java index 7eab4b605cb..59194dd28d8 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java @@ -25,7 +25,9 @@ import static org.junit.jupiter.api.Assertions.fail; import jakarta.persistence.EntityExistsException; +import jakarta.persistence.OptimisticLockException; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.syncope.common.lib.SyncopeConstants; @@ -107,13 +109,15 @@ public void checkIdUniqueness() { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setKey("cn"); schema.setType(AttrSchemaType.String); - plainSchemaDAO.save(schema); try { + plainSchemaDAO.save(schema); entityManager.flush(); fail("This should not happen"); } catch (Exception e) { - assertTrue(e instanceof EntityExistsException || e.getCause() instanceof EntityExistsException); + assertTrue(e instanceof OptimisticLockException + || e instanceof EntityExistsException + || e.getCause() instanceof EntityExistsException); } } @@ -127,17 +131,15 @@ private List getMappingItems(final String intAttrName) { @Test public void deleteFullname() { - // fullname is mapped as ConnObjectKey for ws-target-resource-2, need to swap it otherwise validation errors + // fullname is mapped as ConnObjectKey for various resources, need to swap it otherwise validation errors // will be raised - resourceDAO.findById("ws-target-resource-2").orElseThrow(). - getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping().getItems(). - forEach(item -> { - if ("fullname".equals(item.getIntAttrName())) { - item.setConnObjectKey(false); - } else if ("surname".equals(item.getIntAttrName())) { - item.setConnObjectKey(true); - } - }); + resourceDAO.findAll().stream(). + filter(r -> r.getKey().startsWith("ws-target-resource-")). + map(r -> r.getProvisionByAnyType(AnyTypeKind.USER.name())). + flatMap(Optional::stream). + flatMap(p -> p.getMapping().getItems().stream()). + filter(item -> "fullname".equals(item.getIntAttrName())). + forEach(item -> item.setIntAttrName("surname")); // search for user schema fullname plainSchemaDAO.findById("fullname").orElseThrow(); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ReportTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ReportTest.java index 69f36a87364..ed01ecca9e7 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ReportTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ReportTest.java @@ -24,7 +24,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import jakarta.persistence.EntityExistsException; import jakarta.ws.rs.core.MediaType; import java.time.OffsetDateTime; import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; @@ -33,6 +32,7 @@ import org.apache.syncope.core.persistence.api.entity.Report; import org.apache.syncope.core.persistence.api.entity.ReportExec; import org.apache.syncope.core.persistence.jpa.AbstractTest; +import org.hibernate.exception.ConstraintViolationException; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @@ -60,7 +60,7 @@ public void find() { @Test public void saveWithExistingName() { - assertThrows(EntityExistsException.class, () -> { + assertThrows(ConstraintViolationException.class, () -> { Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); String name = report.getName(); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java index dd50844b6aa..b47cbceaf2c 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java @@ -118,7 +118,7 @@ public void findByPropagationActionsContaining() { assertEquals( Set.of(resourceDAO.findById("resource-testdb2").orElseThrow(), resourceDAO.findById("resource-ldap").orElseThrow()), - new HashSet<>(resourceDAO.findByPropagationActionsContaining(impl))); + new HashSet<>(resourceDAO.findByPropagationActionsContaining(impl))); } @Test @@ -211,7 +211,6 @@ public void save() { // check connector connector = connInstanceDAO.findById("88a7a819-dab5-46b4-9b90-0b9769eabdb8").orElseThrow(); - assertNotNull(connector.getResources()); assertNotNull(resource.getConnector()); assertTrue(resource.getConnector().equals(connector)); @@ -271,8 +270,8 @@ public void delete() { // resource must be not referenced any more from the connector ConnInstance actualConnector = connInstanceDAO.findById(connector.getKey()).orElseThrow(); - actualConnector.getResources(). - forEach(res -> assertFalse(res.getKey().equalsIgnoreCase(resource.getKey()))); + resourceDAO.findByConnInstance(actualConnector.getKey()). + forEach(r -> assertFalse(r.getKey().equalsIgnoreCase(resource.getKey()))); // there must be no tasks propagationTasks.forEach(task -> assertTrue(taskDAO.findById(task.getKey()).isEmpty())); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java index e0b6f427273..0ca3e73584d 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java @@ -141,12 +141,11 @@ public void savePropagationTask() { } @Test - public void addPropagationTaskExecution() { + public void addPropagationTaskExec() { PropagationTask task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); - assertNotNull(task); - int executionNumber = task.getExecs().size(); + int execs = task.getExecs().size(); TaskExec execution = taskUtilsFactory.getInstance(TaskType.PROPAGATION).newTaskExec(); execution.setTask(task); @@ -154,25 +153,21 @@ public void addPropagationTaskExecution() { execution.setStart(OffsetDateTime.now()); execution.setExecutor("admin"); task.add(execution); - execution.setTask(task); taskDAO.save(task); entityManager.flush(); task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); - assertNotNull(task); - - assertEquals(executionNumber + 1, task.getExecs().size()); + assertEquals(execs + 1, task.getExecs().size()); } @Test - public void addPullTaskExecution() { + public void addPullTaskExec() { PullTask task = (PullTask) taskDAO.findById( TaskType.PULL, "c41b9b71-9bfa-4f90-89f2-84787def4c5c").orElseThrow(); - assertNotNull(task); - int executionNumber = task.getExecs().size(); + int execs = task.getExecs().size(); TaskExec execution = taskUtilsFactory.getInstance(TaskType.PULL).newTaskExec(); execution.setStatus("Text-free status"); @@ -181,25 +176,21 @@ public void addPullTaskExecution() { execution.setMessage("A message"); execution.setExecutor("admin"); task.add(execution); - execution.setTask(task); taskDAO.save(task); entityManager.flush(); task = (PullTask) taskDAO.findById( TaskType.PULL, "c41b9b71-9bfa-4f90-89f2-84787def4c5c").orElseThrow(); - assertNotNull(task); - - assertEquals(executionNumber + 1, task.getExecs().size()); + assertEquals(execs + 1, task.getExecs().size()); } @Test - public void addPushTaskExecution() { + public void addPushTaskExec() { PushTask task = (PushTask) taskDAO.findById( TaskType.PUSH, "af558be4-9d2f-4359-bf85-a554e6e90be1").orElseThrow(); - assertNotNull(task); - int executionNumber = task.getExecs().size(); + int execs = task.getExecs().size(); TaskExec execution = taskUtilsFactory.getInstance(TaskType.PUSH).newTaskExec(); execution.setStatus("Text-free status"); @@ -208,15 +199,12 @@ public void addPushTaskExecution() { execution.setMessage("A message"); execution.setExecutor("admin"); task.add(execution); - execution.setTask(task); taskDAO.save(task); entityManager.flush(); task = (PushTask) taskDAO.findById(TaskType.PUSH, "af558be4-9d2f-4359-bf85-a554e6e90be1").orElseThrow(); - assertNotNull(task); - - assertEquals(executionNumber + 1, task.getExecs().size()); + assertEquals(execs + 1, task.getExecs().size()); } @Test @@ -230,11 +218,11 @@ public void deleteTask() { } @Test - public void deleteTaskExecution() { + public void deleteTaskExec() { @SuppressWarnings("unchecked") TaskExec execution = (TaskExec) taskExecDAO.findById( TaskType.PROPAGATION, "e58ca1c7-178a-4012-8a71-8aa14eaf0655").orElseThrow(); - int executionNumber = execution.getTask().getExecs().size(); + int execs = execution.getTask().getExecs().size(); taskExecDAO.delete(TaskType.PROPAGATION, "e58ca1c7-178a-4012-8a71-8aa14eaf0655"); @@ -244,7 +232,7 @@ public void deleteTaskExecution() { PropagationTask task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); - assertEquals(task.getExecs().size(), executionNumber - 1); + assertEquals(task.getExecs().size(), execs - 1); } @Test @@ -391,6 +379,8 @@ public void issueSYNCOPE144() { task.add(inboundActions); task.setMatchingRule(MatchingRule.UPDATE); task.setUnmatchingRule(UnmatchingRule.PROVISION); + task.setJobDelegate(implementationDAO.findById("PullJobDelegate").orElseThrow()); + task.setDestinationRealm(realmDAO.getRoot()); task = taskDAO.save(task); assertNotNull(task); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java index 52a7fecf623..7f1a1104928 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java @@ -18,10 +18,12 @@ */ package org.apache.syncope.core.persistence.jpa.outer; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.OffsetDateTime; @@ -29,13 +31,16 @@ import java.util.Objects; import java.util.UUID; import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.DelegationDAO; import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.dao.RoleDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; @@ -86,6 +91,12 @@ public class UserTest extends AbstractTest { @Autowired private RoleDAO roleDAO; + @Autowired + private RealmDAO realmDAO; + + @Autowired + private AnyTypeClassDAO anyTypeClassDAO; + @Autowired private PlainAttrValidationManager validator; @@ -243,6 +254,30 @@ public void deleteCascadeOnDelegations() { assertTrue(delegationDAO.findById(delegation.getKey()).isEmpty()); } + @Test + public void auxClasses() { + User user = entityFactory.newEntity(User.class); + user.setRealm(realmDAO.getRoot()); + + PlainAttr attr = new PlainAttr(); + attr.setSchema("title"); + attr.add(validator, "value"); + user.add(attr); + + InvalidEntityException iee = assertThrows(InvalidEntityException.class, () -> userDAO.save(user)); + assertTrue(iee.getMessage().contains("propertyPath=username")); + + user.setUsername("username"); + + iee = assertThrows(InvalidEntityException.class, () -> userDAO.save(user)); + assertTrue(iee.getMessage().contains("message=title not allowed for this instance, propertyPath=plainAttrs")); + + user.add(anyTypeClassDAO.findById("minimal group").orElseThrow()); + assertEquals("minimal group", user.getAuxClasses().getFirst().getKey()); + + assertDoesNotThrow(() -> userDAO.save(user)); + } + /** * Search by derived attribute. */ diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml index 83f9e894ab1..b6381b9ea36 100644 --- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml +++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml @@ -65,26 +65,26 @@ under the License. jsonConf='{"_class":"org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf","releaseAttrs":{},"allowedAttrs":["cn","givenName","uid"],"excludedAttrs":[],"includeOnlyAttrs":[],"principalIdAttr":null,"principalAttrRepoConf":{"mergingStrategy":"MULTIVALUED","ignoreResolvedAttributes":false,"expiration":0,"timeUnit":"HOURS","attrRepos":[]}}'/> - - - - - - - - - org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core Persistence Neo4j diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/MasterDomain.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/MasterDomain.java index b89b5f9e5f4..d8744d26464 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/MasterDomain.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/MasterDomain.java @@ -24,7 +24,6 @@ import org.neo4j.driver.Config; import org.neo4j.driver.Driver; import org.neo4j.driver.GraphDatabase; -import org.neo4j.driver.Logging; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -41,11 +40,10 @@ public Driver masterDriver(final PersistenceProperties props) { return GraphDatabase.driver( props.getDomain().getFirst().getUri(), AuthTokens.basic(props.getDomain().getFirst().getUsername(), - props.getDomain().getFirst().getPassword()), + props.getDomain().getFirst().getPassword()), Config.builder(). withMaxConnectionPoolSize(props.getDomain().getFirst().getMaxConnectionPoolSize()). - withDriverMetrics(). - withLogging(Logging.slf4j()).build()); + build()); } @Bean(name = "MasterContentXML") diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java index a485ad24ded..3c998c9f22c 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java @@ -26,7 +26,6 @@ import org.neo4j.driver.Config; import org.neo4j.driver.Driver; import org.neo4j.driver.GraphDatabase; -import org.neo4j.driver.Logging; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ConfigurableApplicationContext; @@ -67,8 +66,7 @@ public void register(final Neo4jDomain domain) { AuthTokens.basic(domain.getUsername(), domain.getPassword()), Config.builder(). withMaxConnectionPoolSize(domain.getMaxConnectionPoolSize()). - withDriverMetrics(). - withLogging(Logging.slf4j()).build()); + build()); registerSingleton(domain.getKey().toLowerCase() + "Driver", driver); domainHolder().getDomains().put(domain.getKey(), driver); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java index 900757b2765..e2f3bdcc456 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java @@ -33,6 +33,7 @@ import org.apache.syncope.core.persistence.api.DomainRegistry; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; @@ -63,6 +64,7 @@ import org.apache.syncope.core.persistence.api.dao.PersistenceInfoDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +import org.apache.syncope.core.persistence.api.dao.RealmChecker; import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; @@ -91,6 +93,7 @@ import org.apache.syncope.core.persistence.common.dao.AnyFinder; import org.apache.syncope.core.persistence.neo4j.content.XMLContentExporter; import org.apache.syncope.core.persistence.neo4j.content.XMLContentLoader; +import org.apache.syncope.core.persistence.neo4j.dao.Neo4jAnyChecker; import org.apache.syncope.core.persistence.neo4j.dao.Neo4jAnyMatchDAO; import org.apache.syncope.core.persistence.neo4j.dao.Neo4jAnySearchDAO; import org.apache.syncope.core.persistence.neo4j.dao.Neo4jAuditEventDAO; @@ -122,6 +125,8 @@ import org.apache.syncope.core.persistence.neo4j.dao.repo.AuthModuleRepoExt; import org.apache.syncope.core.persistence.neo4j.dao.repo.AuthModuleRepoExtImpl; import org.apache.syncope.core.persistence.neo4j.dao.repo.AuthProfileRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AuthProfileRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AuthProfileRepoExtImpl; import org.apache.syncope.core.persistence.neo4j.dao.repo.CASSPClientAppRepo; import org.apache.syncope.core.persistence.neo4j.dao.repo.CASSPClientAppRepoExt; import org.apache.syncope.core.persistence.neo4j.dao.repo.CASSPClientAppRepoExtImpl; @@ -196,6 +201,7 @@ import org.apache.syncope.core.persistence.neo4j.dao.repo.WAConfigRepo; import org.apache.syncope.core.persistence.neo4j.entity.EntityCacheKey; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jConnInstance; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDelegation; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDerSchema; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jEntityFactory; @@ -305,6 +311,7 @@ public CacheCleaningTransactionExecutionListener cacheCleaningTransactionExecuti final Cache anyObjectCache, final Cache delegationCache, final Cache derSchemaCache, + final Cache connInstanceCache, final Cache externalResourceCache, final Cache groupCache, final Cache implementationCache, @@ -318,6 +325,7 @@ public CacheCleaningTransactionExecutionListener cacheCleaningTransactionExecuti anyObjectCache, delegationCache, derSchemaCache, + connInstanceCache, externalResourceCache, groupCache, implementationCache, @@ -440,14 +448,19 @@ public SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory( @ConditionalOnMissingBean @Bean - public AnyFinder anyFinder(final @Lazy PlainSchemaDAO plainSchemaDAO, final @Lazy AnySearchDAO anySearchDAO) { - return new AnyFinder(plainSchemaDAO, anySearchDAO); + public AccessTokenDAO accessTokenDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { + return neo4jRepositoryFactory.getRepository(AccessTokenRepo.class); } @ConditionalOnMissingBean @Bean - public AccessTokenDAO accessTokenDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { - return neo4jRepositoryFactory.getRepository(AccessTokenRepo.class); + public AnyChecker anyChecker( + final @Lazy AnyTypeDAO anyTypeDAO, + final @Lazy AnyTypeClassDAO anyTypeClassDAO, + final @Lazy PlainSchemaDAO plainSchemaDAO, + final @Lazy DerSchemaDAO derSchemaDAO) { + + return new Neo4jAnyChecker(anyTypeDAO, anyTypeClassDAO, plainSchemaDAO, derSchemaDAO); } @ConditionalOnMissingBean @@ -490,12 +503,12 @@ public AnyObjectRepoExt anyObjectRepoExt( final AnyUtilsFactory anyUtilsFactory, final @Lazy AnyTypeDAO anyTypeDAO, final @Lazy AnyTypeClassDAO anyTypeClassDAO, - final @Lazy PlainSchemaDAO plainSchemaDAO, final @Lazy DerSchemaDAO derSchemaDAO, final @Lazy DynRealmDAO dynRealmDAO, final @Lazy UserDAO userDAO, final @Lazy GroupDAO groupDAO, final @Lazy AnyFinder anyFinder, + final AnyChecker anyChecker, final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient, final NodeValidator nodeValidator, @@ -505,11 +518,11 @@ public AnyObjectRepoExt anyObjectRepoExt( anyUtilsFactory, anyTypeDAO, anyTypeClassDAO, - plainSchemaDAO, derSchemaDAO, dynRealmDAO, userDAO, groupDAO, + anyChecker, anyFinder, neo4jTemplate, neo4jClient, @@ -708,8 +721,20 @@ public PasswordManagementDAO passwordManagementDAO( @ConditionalOnMissingBean @Bean - public AuthProfileDAO authProfileDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { - return neo4jRepositoryFactory.getRepository(AuthProfileRepo.class); + public AuthProfileRepoExt authProfileRepoExt( + final Neo4jTemplate neo4jTemplate, + final NodeValidator nodeValidator) { + + return new AuthProfileRepoExtImpl(neo4jTemplate, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public AuthProfileDAO authProfileDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final AuthProfileRepoExt authProfileRepoExt) { + + return neo4jRepositoryFactory.getRepository(AuthProfileRepo.class, authProfileRepoExt); } @ConditionalOnMissingBean @@ -740,14 +765,34 @@ public CASSPClientAppDAO casSPClientAppDAO( return neo4jRepositoryFactory.getRepository(CASSPClientAppRepo.class, casSPClientAppRepoExt); } + @ConditionalOnMissingBean(name = ConnInstanceRepoExt.CACHE) + @Bean(name = ConnInstanceRepoExt.CACHE) + public Cache connInstanceCache(final CacheManager cacheManager) { + return cacheManager.createCache(ConnInstanceRepoExt.CACHE, + new MutableConfiguration(). + setTypes(EntityCacheKey.class, Neo4jConnInstance.class). + setStoreByValue(false). + setReadThrough(true). + setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.ETERNAL))); + } + @ConditionalOnMissingBean @Bean public ConnInstanceRepoExt connInstanceRepoExt( final @Lazy ExternalResourceDAO resourceDAO, + final Cache externalResourceCache, + final Cache connInstanceCache, final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, final NodeValidator nodeValidator) { - return new ConnInstanceRepoExtImpl(resourceDAO, neo4jTemplate, nodeValidator); + return new ConnInstanceRepoExtImpl( + resourceDAO, + externalResourceCache, + connInstanceCache, + neo4jTemplate, + neo4jClient, + nodeValidator); } @ConditionalOnMissingBean @@ -928,7 +973,6 @@ public GroupRepoExt groupRepoExt( final AnyUtilsFactory anyUtilsFactory, final @Lazy AnyTypeDAO anyTypeDAO, final @Lazy AnyTypeClassDAO anyTypeClassDAO, - final @Lazy PlainSchemaDAO plainSchemaDAO, final @Lazy DerSchemaDAO derSchemaDAO, final @Lazy DynRealmDAO dynRealmDAO, final @Lazy RealmDAO realmDAO, @@ -936,6 +980,7 @@ public GroupRepoExt groupRepoExt( final @Lazy UserDAO userDAO, final @Lazy AnyObjectDAO anyObjectDAO, final AnySearchDAO anySearchDAO, + final AnyChecker anyChecker, final @Lazy AnyFinder anyFinder, final SearchCondVisitor searchCondVisitor, final Neo4jTemplate neo4jTemplate, @@ -948,7 +993,6 @@ public GroupRepoExt groupRepoExt( publisher, anyTypeDAO, anyTypeClassDAO, - plainSchemaDAO, derSchemaDAO, dynRealmDAO, realmDAO, @@ -956,6 +1000,7 @@ public GroupRepoExt groupRepoExt( userDAO, anyObjectDAO, anySearchDAO, + anyChecker, anyFinder, searchCondVisitor, neo4jTemplate, @@ -1170,6 +1215,7 @@ public RealmDAO realmDAO( final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient, final NodeValidator nodeValidator, + final RealmChecker realmChecker, final Cache realmCache) { return new Neo4jRealmDAO( @@ -1179,6 +1225,7 @@ public RealmDAO realmDAO( neo4jTemplate, neo4jClient, nodeValidator, + realmChecker, realmCache); } @@ -1434,7 +1481,6 @@ public UserRepoExt userRepoExt( final AnyUtilsFactory anyUtilsFactory, final @Lazy AnyTypeDAO anyTypeDAO, final @Lazy AnyTypeClassDAO anyTypeClassDAO, - final @Lazy PlainSchemaDAO plainSchemaDAO, final @Lazy DerSchemaDAO derSchemaDAO, final @Lazy DynRealmDAO dynRealmDAO, final RoleDAO roleDAO, @@ -1442,6 +1488,7 @@ public UserRepoExt userRepoExt( final @Lazy GroupDAO groupDAO, final DelegationDAO delegationDAO, final FIQLQueryDAO fiqlQueryDAO, + final AnyChecker anyChecker, final @Lazy AnyFinder anyFinder, final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient, @@ -1452,7 +1499,6 @@ public UserRepoExt userRepoExt( anyUtilsFactory, anyTypeDAO, anyTypeClassDAO, - plainSchemaDAO, derSchemaDAO, dynRealmDAO, roleDAO, @@ -1460,6 +1506,7 @@ public UserRepoExt userRepoExt( groupDAO, delegationDAO, fiqlQueryDAO, + anyChecker, anyFinder, securityProperties, neo4jTemplate, diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnyChecker.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnyChecker.java new file mode 100644 index 00000000000..0aed477c5a2 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnyChecker.java @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.neo4j.dao; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; +import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +import org.apache.syncope.core.persistence.api.entity.Schema; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.springframework.transaction.annotation.Transactional; + +public class Neo4jAnyChecker extends AnyChecker { + + protected final AnyTypeDAO anyTypeDAO; + + protected final AnyTypeClassDAO anyTypeClassDAO; + + protected final DerSchemaDAO derSchemaDAO; + + public Neo4jAnyChecker( + final AnyTypeDAO anyTypeDAO, + final AnyTypeClassDAO anyTypeClassDAO, + final PlainSchemaDAO plainSchemaDAO, + final DerSchemaDAO derSchemaDAO) { + + super(plainSchemaDAO); + this.anyTypeDAO = anyTypeDAO; + this.anyTypeClassDAO = anyTypeClassDAO; + this.derSchemaDAO = derSchemaDAO; + } + + @Transactional(readOnly = true) + @SuppressWarnings("unchecked") + @Override + public AllowedSchemas findAllowedSchemas(final Any any, final Class reference) { + AllowedSchemas result = new AllowedSchemas<>(); + + // schemas given by type and aux classes + Set anyTypeOwnClasses = new HashSet<>(); + + anyTypeDAO.findById(any.getType().getKey()).ifPresent(anyType -> { + anyTypeOwnClasses.addAll(anyType.getClasses()); + any.getAuxClasses().forEach(atc -> anyTypeClassDAO.findById(atc.getKey()). + ifPresent(anyTypeOwnClasses::add)); + }); + + anyTypeOwnClasses.forEach(atc -> { + if (reference.equals(PlainSchema.class)) { + atc.getPlainSchemas().stream(). + map(schema -> plainSchemaDAO.findById(schema.getKey())). + flatMap(Optional::stream). + forEach(schema -> result.self().add((S) schema)); + } else if (reference.equals(DerSchema.class)) { + atc.getDerSchemas().stream(). + map(schema -> derSchemaDAO.findById(schema.getKey())). + flatMap(Optional::stream). + forEach(schema -> result.self().add((S) schema)); + } + }); + + // schemas given by group type extensions + Map> gTypeExtensionClasses = new HashMap<>(); + + switch (any) { + case User user -> + user.getMemberships().forEach(memb -> memb.getRightEnd().getTypeExtensions().forEach(typeExt -> { + Set classes = new HashSet<>(); + typeExt.getAuxClasses().forEach(atc -> anyTypeClassDAO.findById(atc.getKey()). + ifPresent(classes::add)); + + gTypeExtensionClasses.put(memb.getRightEnd(), classes); + })); + + case AnyObject anyObject -> + anyObject.getMemberships().forEach(memb -> memb.getRightEnd().getTypeExtensions().stream(). + filter(typeExt -> any.getType().equals(typeExt.getAnyType())).forEach(typeExt -> { + + Set classes = new HashSet<>(); + typeExt.getAuxClasses().forEach(atc -> anyTypeClassDAO.findById(atc.getKey()). + ifPresent(classes::add)); + + gTypeExtensionClasses.put(memb.getRightEnd(), classes); + })); + + default -> { + } + } + + gTypeExtensionClasses.entrySet().stream().peek( + entry -> result.memberships().put(entry.getKey(), new HashSet<>())). + forEach(entry -> entry.getValue().forEach(atc -> { + + if (reference.equals(PlainSchema.class)) { + atc.getPlainSchemas().stream(). + map(schema -> plainSchemaDAO.findById(schema.getKey())). + flatMap(Optional::stream). + forEach(schema -> result.memberships().get(entry.getKey()).add((S) schema)); + } else if (reference.equals(DerSchema.class)) { + atc.getDerSchemas().stream(). + map(schema -> derSchemaDAO.findById(schema.getKey())). + flatMap(Optional::stream). + forEach(schema -> result.memberships().get(entry.getKey()).add((S) schema)); + } + })); + + // schemas given by relationship type extensions + Map> rTypeExtensionClasses = new HashMap<>(); + + switch (any) { + case User user -> + user.getRelationships().forEach(rel -> rel.getType().getTypeExtensions().forEach(typeExt -> { + Set classes = new HashSet<>(); + typeExt.getAuxClasses().forEach(atc -> anyTypeClassDAO.findById(atc.getKey()). + ifPresent(classes::add)); + + rTypeExtensionClasses.put(rel.getType(), classes); + })); + + case AnyObject anyObject -> + anyObject.getRelationships().forEach(rel -> rel.getType().getTypeExtensions().stream(). + filter(typeExt -> any.getType().equals(typeExt.getAnyType())).forEach(typeExt -> { + + Set classes = new HashSet<>(); + typeExt.getAuxClasses().forEach(atc -> anyTypeClassDAO.findById(atc.getKey()). + ifPresent(classes::add)); + + rTypeExtensionClasses.put(rel.getType(), classes); + })); + + default -> { + } + } + + rTypeExtensionClasses.entrySet().stream().peek( + entry -> result.relationshipTypes().put(entry.getKey(), new HashSet<>())). + forEach(entry -> entry.getValue().forEach(atc -> { + + if (reference.equals(PlainSchema.class)) { + atc.getPlainSchemas().stream(). + map(schema -> plainSchemaDAO.findById(schema.getKey())). + flatMap(Optional::stream). + forEach(schema -> result.relationshipTypes().get(entry.getKey()).add((S) schema)); + } else if (reference.equals(DerSchema.class)) { + atc.getDerSchemas().stream(). + map(schema -> derSchemaDAO.findById(schema.getKey())). + flatMap(Optional::stream). + forEach(schema -> result.relationshipTypes().get(entry.getKey()).add((S) schema)); + } + })); + + return result; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java index 85c9cf58ca3..4891aaa709d 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java @@ -297,13 +297,13 @@ protected String getQuery( + "MATCH (n)-[]-(:" + Neo4jUMembership.NODE + ")-[]-" + "(g:" + Neo4jGroup.NODE + ") WHERE g.id IN $" + param + " } " + (not ? "AND NOT" : "OR") + " EXISTS { " - + "MATCH (n)-[:" + GroupRepoExt.DYN_GROUP_USER_MEMBERSHIP_REL + "]-" + + "MATCH (n)-[:" + GroupRepoExt.DYN_GROUP_MEMBERSHIP_REL + "]-" + "(g:" + Neo4jGroup.NODE + ") WHERE g.id IN $" + param + " } " + (not ? "AND NOT" : "OR") + " EXISTS { " + "MATCH (n)-[]-(:" + Neo4jAMembership.NODE + ")-[]-" + "(g:" + Neo4jGroup.NODE + ") WHERE g.id IN $" + param + " } " + (not ? "AND NOT" : "OR") + " EXISTS { " - + "MATCH (n)-[:" + GroupRepoExt.DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-" + + "MATCH (n)-[:" + GroupRepoExt.DYN_GROUP_MEMBERSHIP_REL + "]-" + "(g:" + Neo4jGroup.NODE + ") WHERE g.id IN $" + param + " } "; } @@ -320,13 +320,13 @@ protected String getQuery( + "MATCH (n)-[]-(:" + Neo4jUMembership.NODE + ")-[]-" + "(m:" + Neo4jUser.NODE + ") WHERE m.id IN $" + param + " } " + (not ? "AND NOT" : "OR") + " EXISTS { " - + "MATCH (n)-[:" + GroupRepoExt.DYN_GROUP_USER_MEMBERSHIP_REL + "]-" + + "MATCH (n)-[:" + GroupRepoExt.DYN_GROUP_MEMBERSHIP_REL + "]-" + "(m:" + Neo4jUser.NODE + ") WHERE m.id IN $" + param + " } " + (not ? "AND NOT" : "OR") + " EXISTS { " + "MATCH (n)-[]-(:" + Neo4jAMembership.NODE + ")-[]-" + "(m:" + Neo4jAnyObject.NODE + ") WHERE m.id IN $" + param + " } " + (not ? "AND NOT" : "OR") + " EXISTS { " - + "MATCH (n)-[:" + GroupRepoExt.DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-" + + "MATCH (n)-[:" + GroupRepoExt.DYN_GROUP_MEMBERSHIP_REL + "]-" + "(m:" + Neo4jAnyObject.NODE + ") WHERE m.id IN $" + param + " } "; } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPersistenceInfoDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPersistenceInfoDAO.java index 30518c95e9a..a180165cc99 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPersistenceInfoDAO.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPersistenceInfoDAO.java @@ -18,9 +18,7 @@ */ package org.apache.syncope.core.persistence.neo4j.dao; -import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import org.apache.syncope.core.persistence.api.dao.PersistenceInfoDAO; import org.neo4j.driver.Driver; @@ -51,29 +49,6 @@ public Map info() { result.putAll(session.run(CYPHER).next().asMap()); } - if (driver.isMetricsEnabled()) { - List> metrics = new ArrayList<>(); - driver.metrics().connectionPoolMetrics().forEach(m -> { - Map metric = new LinkedHashMap<>(); - metric.put("id", m.id()); - metric.put("inUse", m.inUse()); - metric.put("idle", m.idle()); - metric.put("created", m.created()); - metric.put("failedToCreate", m.failedToCreate()); - metric.put("closed", m.closed()); - metric.put("acquiring", m.acquiring()); - metric.put("acquired", m.acquired()); - metric.put("timedOutToAcquire", m.timedOutToAcquire()); - metric.put("totalAcquisitionTime", m.totalAcquisitionTime()); - metric.put("totalConnectionTime", m.totalConnectionTime()); - metric.put("totalInUseTime", m.totalInUseTime()); - metric.put("totalInUseCount", m.totalInUseCount()); - metrics.add(metric); - }); - - result.put("metrics", metrics); - } - return result; } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmDAO.java index d09da509497..8c9bfd9d108 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmDAO.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmDAO.java @@ -24,6 +24,7 @@ import javax.cache.Cache; import org.apache.commons.lang3.Strings; import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.core.persistence.api.dao.RealmChecker; import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.RoleDAO; @@ -79,6 +80,8 @@ public class Neo4jRealmDAO extends AbstractDAO implements RealmDAO { protected final NodeValidator nodeValidator; + protected final RealmChecker realmChecker; + protected final Cache cache; public Neo4jRealmDAO( @@ -88,6 +91,7 @@ public Neo4jRealmDAO( final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient, final NodeValidator nodeValidator, + final RealmChecker realmChecker, final Cache cache) { super(neo4jTemplate, neo4jClient); @@ -95,6 +99,7 @@ public Neo4jRealmDAO( this.realmSearchDAO = realmSearchDAO; this.publisher = publisher; this.nodeValidator = nodeValidator; + this.realmChecker = realmChecker; this.cache = cache; } @@ -234,6 +239,8 @@ public S save(final S realm) { ((Neo4jRealm) realm).setFullPath(fullPathAfter); } + realmChecker.checkBeforeSave(realm); + S merged = neo4jTemplate.save(nodeValidator.validate(realm)); if (!fullPathAfter.equals(fullPathBefore)) { @@ -277,4 +284,9 @@ public void delete(final Realm realm) { new EntityLifecycleEvent<>(this, SyncDeltaType.DELETE, toBeDeleted, AuthContextUtils.getDomain())); }); } + + @Override + public void evict(final String key) { + cache.remove(EntityCacheKey.of(key)); + } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskExecDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskExecDAO.java index 41dd50027d2..f3ebbd9998c 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskExecDAO.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskExecDAO.java @@ -240,18 +240,6 @@ public > S save(final S execution) { return neo4jTemplate.save(nodeValidator.validate(execution)); } - @Transactional(rollbackFor = { Throwable.class }) - @Override - public > void saveAndAdd( - final TaskType taskType, final String taskKey, final TaskExec execution) { - - Optional task = taskDAO.findById(taskType, taskKey); - if (task.isPresent()) { - task.get().add(execution); - taskDAO.save(task.get()); - } - } - @Override public void delete(final TaskType taskType, final String key) { findById(taskType, key).ifPresent(this::delete); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractAnyRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractAnyRepoExt.java index 6c87bec623c..a1dcf708088 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractAnyRepoExt.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractAnyRepoExt.java @@ -21,38 +21,24 @@ import java.time.OffsetDateTime; import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.regex.Pattern; import javax.cache.Cache; -import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; -import org.apache.syncope.core.persistence.api.dao.DuplicateException; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.NotFoundException; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.entity.Any; -import org.apache.syncope.core.persistence.api.entity.AnyType; -import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; import org.apache.syncope.core.persistence.api.entity.AnyUtils; -import org.apache.syncope.core.persistence.api.entity.Attributable; -import org.apache.syncope.core.persistence.api.entity.DerSchema; import org.apache.syncope.core.persistence.api.entity.ExternalResource; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.Relationship; -import org.apache.syncope.core.persistence.api.entity.RelationshipType; -import org.apache.syncope.core.persistence.api.entity.Schema; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.api.entity.group.GRelationship; -import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.user.URelationship; -import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.common.dao.AnyFinder; import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO; import org.apache.syncope.core.persistence.neo4j.entity.AbstractAny; @@ -62,10 +48,8 @@ import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jARelationship; import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGRelationship; import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jURelationship; -import org.apache.syncope.core.spring.security.AuthContextUtils; import org.springframework.data.neo4j.core.Neo4jClient; import org.springframework.data.neo4j.core.Neo4jTemplate; -import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; public abstract class AbstractAnyRepoExt @@ -98,12 +82,12 @@ protected static List split(final String attrValue, final List l protected final AnyTypeClassDAO anyTypeClassDAO; - protected final PlainSchemaDAO plainSchemaDAO; - protected final DerSchemaDAO derSchemaDAO; protected final DynRealmDAO dynRealmDAO; + protected final AnyChecker anyChecker; + protected final AnyFinder anyFinder; protected final AnyUtils anyUtils; @@ -111,9 +95,9 @@ protected static List split(final String attrValue, final List l protected AbstractAnyRepoExt( final AnyTypeDAO anyTypeDAO, final AnyTypeClassDAO anyTypeClassDAO, - final PlainSchemaDAO plainSchemaDAO, final DerSchemaDAO derSchemaDAO, final DynRealmDAO dynRealmDAO, + final AnyChecker anyChecker, final AnyFinder anyFinder, final AnyUtils anyUtils, final Neo4jTemplate neo4jTemplate, @@ -122,9 +106,9 @@ protected AbstractAnyRepoExt( super(neo4jTemplate, neo4jClient); this.anyTypeDAO = anyTypeDAO; this.anyTypeClassDAO = anyTypeClassDAO; - this.plainSchemaDAO = plainSchemaDAO; this.derSchemaDAO = derSchemaDAO; this.dynRealmDAO = dynRealmDAO; + this.anyChecker = anyChecker; this.anyFinder = anyFinder; this.anyUtils = anyUtils; } @@ -177,136 +161,6 @@ public List findByDerAttrValue(final String expression, final String value, f return anyFinder.findByDerAttrValue(anyUtils.anyTypeKind(), expression, value, ignoreCaseMatch); } - @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) - @Override - @SuppressWarnings("unchecked") - public AllowedSchemas findAllowedSchemas(final A any, final Class reference) { - AllowedSchemas result = new AllowedSchemas<>(); - - // schemas given by type and aux classes - Set anyTypeOwnClasses = new HashSet<>(); - AnyType anyType = anyTypeDAO.findById(any.getType().getKey()). - orElseThrow(() -> new NotFoundException("AnyType " + any.getType().getKey())); - anyTypeOwnClasses.addAll(anyType.getClasses()); - any.getAuxClasses().forEach(atc -> { - AnyTypeClass anyTypeClass = anyTypeClassDAO.findById(atc.getKey()). - orElseThrow(() -> new NotFoundException("AnyTypeClass " + atc.getKey())); - anyTypeOwnClasses.add(anyTypeClass); - }); - - anyTypeOwnClasses.forEach(atc -> { - if (reference.equals(PlainSchema.class)) { - atc.getPlainSchemas().stream(). - map(schema -> plainSchemaDAO.findById(schema.getKey())). - flatMap(Optional::stream). - forEach(schema -> result.self().add((S) schema)); - } else if (reference.equals(DerSchema.class)) { - atc.getDerSchemas().stream(). - map(schema -> derSchemaDAO.findById(schema.getKey())). - flatMap(Optional::stream). - forEach(schema -> result.self().add((S) schema)); - } - }); - - // schemas given by type extensions - Map> gTypeExtensionClasses = new HashMap<>(); - switch (any) { - case User user -> - user.getMemberships().forEach(memb -> memb.getRightEnd().getTypeExtensions().forEach(typeExt -> { - Set typeExtClasses = new HashSet<>(); - typeExt.getAuxClasses().forEach(atc -> { - AnyTypeClass anyTypeClass = anyTypeClassDAO.findById(atc.getKey()). - orElseThrow(() -> new NotFoundException("AnyTypeClass " + atc.getKey())); - typeExtClasses.add(anyTypeClass); - }); - - gTypeExtensionClasses.put(memb.getRightEnd(), typeExtClasses); - })); - - case AnyObject anyObject -> - anyObject.getMemberships().forEach(memb -> memb.getRightEnd().getTypeExtensions().stream(). - filter(typeExt -> any.getType().equals(typeExt.getAnyType())).forEach(typeExt -> { - - Set typeExtClasses = new HashSet<>(); - typeExt.getAuxClasses().forEach(atc -> { - AnyTypeClass anyTypeClass = anyTypeClassDAO.findById(atc.getKey()). - orElseThrow(() -> new NotFoundException("AnyTypeClass " + atc.getKey())); - typeExtClasses.add(anyTypeClass); - }); - - gTypeExtensionClasses.put(memb.getRightEnd(), typeExtClasses); - })); - - default -> { - } - } - gTypeExtensionClasses.entrySet().stream().peek( - entry -> result.memberships().put(entry.getKey(), new HashSet<>())) - .forEach(entry -> entry.getValue().forEach(atc -> { - if (reference.equals(PlainSchema.class)) { - atc.getPlainSchemas().stream(). - map(schema -> plainSchemaDAO.findById(schema.getKey())). - flatMap(Optional::stream). - forEach(schema -> result.memberships().get(entry.getKey()).add((S) schema)); - } else if (reference.equals(DerSchema.class)) { - atc.getDerSchemas().stream(). - map(schema -> derSchemaDAO.findById(schema.getKey())). - flatMap(Optional::stream). - forEach(schema -> result.memberships().get(entry.getKey()).add((S) schema)); - } - })); - - // schemas given by relationship type extensions - Map> rTypeExtensionClasses = new HashMap<>(); - switch (any) { - case User user -> - user.getRelationships().forEach(rel -> rel.getType().getTypeExtensions().forEach(typeExt -> { - Set typeExtClasses = new HashSet<>(); - typeExt.getAuxClasses().forEach(atc -> { - AnyTypeClass anyTypeClass = anyTypeClassDAO.findById(atc.getKey()). - orElseThrow(() -> new NotFoundException("AnyTypeClass " + atc.getKey())); - typeExtClasses.add(anyTypeClass); - }); - - rTypeExtensionClasses.put(rel.getType(), typeExtClasses); - })); - - case AnyObject anyObject -> - anyObject.getRelationships().forEach(rel -> rel.getType().getTypeExtensions().stream(). - filter(typeExt -> any.getType().equals(typeExt.getAnyType())).forEach(typeExt -> { - - Set typeExtClasses = new HashSet<>(); - typeExt.getAuxClasses().forEach(atc -> { - AnyTypeClass anyTypeClass = anyTypeClassDAO.findById(atc.getKey()). - orElseThrow(() -> new NotFoundException("AnyTypeClass " + atc.getKey())); - typeExtClasses.add(anyTypeClass); - }); - - rTypeExtensionClasses.put(rel.getType(), typeExtClasses); - })); - - default -> { - } - } - rTypeExtensionClasses.entrySet().stream().peek( - entry -> result.relationshipTypes().put(entry.getKey(), new HashSet<>())) - .forEach(entry -> entry.getValue().forEach(atc -> { - if (reference.equals(PlainSchema.class)) { - atc.getPlainSchemas().stream(). - map(schema -> plainSchemaDAO.findById(schema.getKey())). - flatMap(Optional::stream). - forEach(schema -> result.relationshipTypes().get(entry.getKey()).add((S) schema)); - } else if (reference.equals(DerSchema.class)) { - atc.getDerSchemas().stream(). - map(schema -> derSchemaDAO.findById(schema.getKey())). - flatMap(Optional::stream). - forEach(schema -> result.relationshipTypes().get(entry.getKey()).add((S) schema)); - } - })); - - return result; - } - @Transactional(readOnly = true) @Override public List findDynRealms(final String key) { @@ -339,36 +193,13 @@ public void deleteRelationship(final Relationship relati neo4jTemplate.deleteById(relationship.getKey(), domainType); } - protected void checkBeforeSave(final T attributable) { - // check UNIQUE constraints - attributable.getPlainAttrs().stream().filter(attr -> attr.getUniqueValue() != null).forEach(attr -> { - if (plainSchemaDAO.existsPlainAttrUniqueValue( - anyUtils, - attributable.getKey(), - plainSchemaDAO.findById(attr.getSchema()). - orElseThrow(() -> new NotFoundException("PlainSchema " + attr.getSchema())), - attr.getUniqueValue())) { - - throw new DuplicateException("Duplicate value found for " - + attr.getSchema() + "=" + attr.getUniqueValue().getValueAsString()); - } else { - LOG.debug("No duplicate value found for {}={}", - attr.getSchema(), attr.getUniqueValue().getValueAsString()); - } - }); - - // update sysInfo - if (attributable instanceof Any any) { - OffsetDateTime now = OffsetDateTime.now(); - String who = AuthContextUtils.getWho(); - LOG.debug("Set last change date '{}' and modifier '{}' for '{}'", now, who, any); - any.setLastModifier(who); - any.setLastChangeDate(now); - } - } - @Override public void deleteById(final String key) { findById(key).ifPresent(this::delete); } + + @Override + public void evict(final Class entityClass, final String key) { + cache().remove(EntityCacheKey.of(key)); + } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExtImpl.java index 7a6c71d41be..872dde525cf 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExtImpl.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExtImpl.java @@ -32,12 +32,12 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.types.AnyEntitlement; import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; @@ -82,11 +82,11 @@ public AnyObjectRepoExtImpl( final AnyUtilsFactory anyUtilsFactory, final AnyTypeDAO anyTypeDAO, final AnyTypeClassDAO anyTypeClassDAO, - final PlainSchemaDAO plainSchemaDAO, final DerSchemaDAO derSchemaDAO, final DynRealmDAO dynRealmDAO, final UserDAO userDAO, final GroupDAO groupDAO, + final AnyChecker anyChecker, final AnyFinder anyFinder, final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient, @@ -96,9 +96,9 @@ public AnyObjectRepoExtImpl( super( anyTypeDAO, anyTypeClassDAO, - plainSchemaDAO, derSchemaDAO, dynRealmDAO, + anyChecker, anyFinder, anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT), neo4jTemplate, @@ -228,7 +228,7 @@ public List> findAllRelationships(final AnyObject a } protected Pair doSave(final S anyObject) { - checkBeforeSave(anyObject); + anyChecker.checkBeforeSave(anyObject, anyUtils); // unlink any resource or aux class that was unlinked from anyObject // delete any membership or relationship that was removed from anyObject @@ -304,7 +304,7 @@ protected List findURelationships(final AnyObject anyObject) { public List findDynGroups(final String key) { return toList(neo4jClient.query( "MATCH (n:" + Neo4jAnyObject.NODE + " {id: $id})-" - + "[:" + GroupRepoExt.DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-" + + "[:" + GroupRepoExt.DYN_GROUP_MEMBERSHIP_REL + "]-" + "(p:" + Neo4jGroup.NODE + ") " + "RETURN p.id").bindAll(Map.of("id", key)).fetch().all(), "p.id", diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyRepoExt.java index 1131579be34..c4b27497c4d 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyRepoExt.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyRepoExt.java @@ -23,11 +23,9 @@ import java.util.List; import java.util.Optional; import org.apache.syncope.common.lib.types.AnyTypeKind; -import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Relationship; -import org.apache.syncope.core.persistence.api.entity.Schema; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAMembership; import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; @@ -79,8 +77,6 @@ static String membNode(final AnyTypeKind anyTypeKind) { List findByDerAttrValue(String expression, String value, boolean ignoreCaseMatch); - AllowedSchemas findAllowedSchemas(A any, Class reference); - List findDynRealms(String key); Collection findAllResourceKeys(String key); @@ -94,4 +90,6 @@ static String membNode(final AnyTypeKind anyTypeKind) { void deleteById(String key); void delete(A any); + + void evict(Class entityClass, String key); } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepo.java index 86ccb54bede..e70ec8ebcea 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepo.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepo.java @@ -23,6 +23,6 @@ import org.springframework.data.repository.PagingAndSortingRepository; public interface AuthProfileRepo - extends PagingAndSortingRepository, AuthProfileDAO { + extends PagingAndSortingRepository, AuthProfileRepoExt, AuthProfileDAO { } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDynGroupMembership.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepoExt.java similarity index 79% rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDynGroupMembership.java rename to core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepoExt.java index efec3f17dc6..31273ef5135 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDynGroupMembership.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepoExt.java @@ -16,10 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.entity.user; +package org.apache.syncope.core.persistence.neo4j.dao.repo; -import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; +import org.apache.syncope.core.persistence.api.entity.am.AuthProfile; -public interface UDynGroupMembership extends DynGroupMembership { +public interface AuthProfileRepoExt { + AuthProfile save(AuthProfile authProfileI); } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepoExtImpl.java new file mode 100644 index 00000000000..7a527f66f21 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepoExtImpl.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.entity.am.AuthProfile; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jAuthProfile; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class AuthProfileRepoExtImpl implements AuthProfileRepoExt { + + protected final Neo4jTemplate neo4jTemplate; + + protected final NodeValidator nodeValidator; + + public AuthProfileRepoExtImpl(final Neo4jTemplate neo4jTemplate, final NodeValidator nodeValidator) { + this.neo4jTemplate = neo4jTemplate; + this.nodeValidator = nodeValidator; + } + + @Override + public AuthProfile save(final AuthProfile connector) { + ((Neo4jAuthProfile) connector).list2json(); + AuthProfile saved = neo4jTemplate.save(nodeValidator.validate(connector)); + ((Neo4jAuthProfile) saved).postSave(); + return saved; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExt.java index b0c091022cd..9f17afba7ef 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExt.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExt.java @@ -19,10 +19,15 @@ package org.apache.syncope.core.persistence.neo4j.dao.repo; import java.util.List; +import java.util.Optional; import org.apache.syncope.core.persistence.api.entity.ConnInstance; public interface ConnInstanceRepoExt { + String CACHE = "connInstanceCache"; + + Optional findById(String key); + ConnInstance authFind(String key); List findAll(); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExtImpl.java index 80784d2d8ec..71b8a4a9181 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExtImpl.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExtImpl.java @@ -19,34 +19,54 @@ package org.apache.syncope.core.persistence.neo4j.dao.repo; import java.util.List; +import java.util.Optional; import java.util.Set; +import javax.cache.Cache; import org.apache.syncope.common.lib.types.IdMEntitlement; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; import org.apache.syncope.core.persistence.api.entity.ConnInstance; import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO; +import org.apache.syncope.core.persistence.neo4j.entity.EntityCacheKey; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jConnInstance; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.security.DelegatedAdministrationException; +import org.springframework.data.neo4j.core.Neo4jClient; import org.springframework.data.neo4j.core.Neo4jTemplate; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; -public class ConnInstanceRepoExtImpl implements ConnInstanceRepoExt { +public class ConnInstanceRepoExtImpl extends AbstractDAO implements ConnInstanceRepoExt { protected final ExternalResourceDAO resourceDAO; - protected final Neo4jTemplate neo4jTemplate; + protected final Cache resourceCache; + + protected final Cache cache; protected final NodeValidator nodeValidator; public ConnInstanceRepoExtImpl( final ExternalResourceDAO resourceDAO, + final Cache resourceCache, + final Cache connInstanceCache, final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, final NodeValidator nodeValidator) { + super(neo4jTemplate, neo4jClient); this.resourceDAO = resourceDAO; - this.neo4jTemplate = neo4jTemplate; this.nodeValidator = nodeValidator; + this.resourceCache = resourceCache; + this.cache = connInstanceCache; + } + + @Transactional(readOnly = true) + @Override + public Optional findById(final String key) { + return findById(key, Neo4jConnInstance.class, cache); } @Transactional(readOnly = true) @@ -73,12 +93,17 @@ public ConnInstance authFind(final String key) { @Override public List findAll() { - final Set authRealms = AuthContextUtils.getAuthorizations().get(IdMEntitlement.CONNECTOR_LIST); - if (authRealms == null || authRealms.isEmpty()) { + Set authRealms = AuthContextUtils.getAuthorizations().get(IdMEntitlement.CONNECTOR_LIST); + if (CollectionUtils.isEmpty(authRealms)) { return List.of(); } - return neo4jTemplate.findAll(Neo4jConnInstance.class).stream().filter(connInstance -> authRealms.stream(). + List all = toList(neo4jClient.query( + "MATCH (n:" + Neo4jConnInstance.NODE + ") RETURN n.id").fetch().all(), + "n.id", + Neo4jConnInstance.class, + cache); + return all.stream().filter(connInstance -> authRealms.stream(). anyMatch(realm -> connInstance.getAdminRealm().getFullPath().startsWith(realm))). toList(); } @@ -88,14 +113,22 @@ public ConnInstance save(final ConnInstance connector) { ((Neo4jConnInstance) connector).list2json(); ConnInstance saved = neo4jTemplate.save(nodeValidator.validate(connector)); ((Neo4jConnInstance) saved).postSave(); + + resourceDAO.findByConnInstance(saved.getKey()). + forEach(resource -> resourceCache.remove(EntityCacheKey.of(resource.getKey()))); + + cache.put(EntityCacheKey.of(saved.getKey()), (Neo4jConnInstance) saved); + return saved; } @Override public void deleteById(final String key) { neo4jTemplate.findById(key, Neo4jConnInstance.class).ifPresent(connInstance -> { - connInstance.getResources().stream(). - map(ExternalResource::getKey).toList().forEach(resourceDAO::deleteById); + resourceDAO.findByConnInstance(connInstance.getKey()).stream(). + map(ExternalResource::getKey).toList().forEach(resourceDAO::deleteById); + + cache.remove(EntityCacheKey.of(key)); neo4jTemplate.deleteById(key, Neo4jConnInstance.class); }); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExtImpl.java index a0115498e8a..2d0a069e813 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExtImpl.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExtImpl.java @@ -270,7 +270,12 @@ public ExternalResource save(final ExternalResource resource) { @Override public void deleteMapping(final String schemaKey) { - findAll().forEach(resource -> { + List resources = toList(neo4jClient.query( + "MATCH (n:" + Neo4jExternalResource.NODE + ") RETURN n.id").fetch().all(), + "n.id", + Neo4jExternalResource.class, + cache); + resources.forEach(resource -> { Mutable removed = new MutableObject<>(false); resource.getProvisions().forEach(provision -> removed.setValue( @@ -279,10 +284,10 @@ public void deleteMapping(final String schemaKey) { && provision.getMapping().getItems().removeIf(item -> schemaKey.equals(item.getIntAttrName()))))); if (removed.get()) { - ((Neo4jExternalResource) resource).list2json(); - ExternalResource saved = neo4jTemplate.save(resource); - ((Neo4jExternalResource) saved).postSave(); - cache.put(EntityCacheKey.of(resource.getKey()), (Neo4jExternalResource) saved); + resource.list2json(); + Neo4jExternalResource saved = neo4jTemplate.save(resource); + saved.postSave(); + cache.put(EntityCacheKey.of(resource.getKey()), saved); } }); } @@ -311,12 +316,6 @@ public void deleteById(final String key) { groupDAO.findByResourcesContaining(resource). forEach(group -> group.getResources().remove(resource)); - if (resource.getConnector() != null - && resource.getConnector().getResources() != null - && !resource.getConnector().getResources().isEmpty()) { - - resource.getConnector().getResources().remove(resource); - } resource.setConnector(null); cache.remove(EntityCacheKey.of(key)); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExt.java index 059cfb25fa4..a1b885a23a6 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExt.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExt.java @@ -35,9 +35,7 @@ public interface GroupRepoExt extends AnyRepoExt { String CACHE = "groupCache"; - String DYN_GROUP_USER_MEMBERSHIP_REL = "DYN_GROUP_USER_MEMBERSHIP"; - - String DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL = "DYN_GROUP_ANY_OBJECT_MEMBERSHIP"; + String DYN_GROUP_MEMBERSHIP_REL = "DYN_GROUP_MEMBERSHIP_REL"; void securityChecks(Set authRealms, String key, String realm); @@ -63,9 +61,7 @@ public interface GroupRepoExt extends AnyRepoExt { List findUDynMembers(Group group); - void clearADynMembers(Group group); - - void clearUDynMembers(Group group); + void clearDynMembers(Group group); GroupDAO.DynMembershipInfo refreshDynMemberships(AnyObject anyObject); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java index 57a3e312b9b..c1c5247eadb 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java @@ -32,6 +32,7 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.AnyDAO; import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; @@ -41,7 +42,6 @@ import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; @@ -49,13 +49,12 @@ import org.apache.syncope.core.persistence.api.entity.AnyType; import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; import org.apache.syncope.core.persistence.api.entity.ExternalResource; -import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.group.GroupTypeExtension; -import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; import org.apache.syncope.core.persistence.api.entity.user.UMembership; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.api.search.SearchCondConverter; @@ -65,14 +64,13 @@ import org.apache.syncope.core.persistence.neo4j.entity.EntityCacheKey; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDynGroupMembership; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; -import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jADynGroupMembership; import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAMembership; import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroupTypeExtension; -import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUDynGroupMembership; import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUMembership; import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; @@ -112,7 +110,6 @@ public GroupRepoExtImpl( final ApplicationEventPublisher publisher, final AnyTypeDAO anyTypeDAO, final AnyTypeClassDAO anyTypeClassDAO, - final PlainSchemaDAO plainSchemaDAO, final DerSchemaDAO derSchemaDAO, final DynRealmDAO dynRealmDAO, final RealmDAO realmDAO, @@ -120,6 +117,7 @@ public GroupRepoExtImpl( final UserDAO userDAO, final AnyObjectDAO anyObjectDAO, final AnySearchDAO anySearchDAO, + final AnyChecker anyChecker, final AnyFinder anyFinder, final SearchCondVisitor searchCondVisitor, final Neo4jTemplate neo4jTemplate, @@ -130,9 +128,9 @@ public GroupRepoExtImpl( super( anyTypeDAO, anyTypeClassDAO, - plainSchemaDAO, derSchemaDAO, dynRealmDAO, + anyChecker, anyFinder, anyUtilsFactory.getInstance(AnyTypeKind.GROUP), neo4jTemplate, @@ -285,7 +283,7 @@ public List findUMemberships(final Group group, final Pageable page @Override public S save(final S group) { - checkBeforeSave(group); + anyChecker.checkBeforeSave(group, anyUtils); // unlink any resource, aux class, user or group owner that was unlinked from group // delete any dynamic membership or type extension that was removed from group @@ -321,13 +319,10 @@ public S save(final S group) { Neo4jGroup.GROUP_OWNER_REL); } - if (before.getUDynMembership() != null && group.getUDynMembership() == null) { - neo4jTemplate.deleteById(before.getUDynMembership().getKey(), Neo4jUDynGroupMembership.class); - } - Set beforeADynMembs = before.getADynMemberships().stream().map(ADynGroupMembership::getKey). + Set beforeDynMembs = before.getDynMemberships().stream().map(DynGroupMembership::getKey). collect(Collectors.toSet()); - beforeADynMembs.removeAll(group.getADynMemberships().stream().map(ADynGroupMembership::getKey).toList()); - beforeADynMembs.forEach(m -> neo4jTemplate.deleteById(m, Neo4jADynGroupMembership.class)); + beforeDynMembs.removeAll(group.getDynMemberships().stream().map(DynGroupMembership::getKey).toList()); + beforeDynMembs.forEach(m -> neo4jTemplate.deleteById(m, Neo4jDynGroupMembership.class)); Set beforeTypeExts = before.getTypeExtensions().stream().map(GroupTypeExtension::getKey). collect(Collectors.toSet()); @@ -347,49 +342,26 @@ public Group saveAndRefreshDynMemberships(final Group group) { Group merged = save(group); // refresh dynamic memberships - clearUDynMembers(merged); - if (merged.getUDynMembership() != null) { - SearchCond cond = SearchCondConverter.convert(searchCondVisitor, merged.getUDynMembership().getFIQLCond()); - long count = anySearchDAO.count( - realmDAO.getRoot(), true, Set.of(SyncopeConstants.ROOT_REALM), cond, AnyTypeKind.USER); - for (int page = 0; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE); page++) { - List matching = anySearchDAO.search( - realmDAO.getRoot(), - true, - Set.of(SyncopeConstants.ROOT_REALM), - cond, - PageRequest.of(page, AnyDAO.DEFAULT_PAGE_SIZE), - AnyTypeKind.USER); - - matching.forEach(user -> { - neo4jClient.query( - "MATCH (a:" + Neo4jUser.NODE + " {id: $aid}), (b:" + Neo4jGroup.NODE + "{id: $gid}) " - + "CREATE (a)-[:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]->(b)"). - bindAll(Map.of("aid", user.getKey(), "gid", merged.getKey())).run(); - - publisher.publishEvent( - new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, user, AuthContextUtils.getDomain())); - }); - } - } - clearADynMembers(merged); - merged.getADynMemberships().forEach(memb -> { + clearDynMembers(merged); + merged.getDynMemberships().forEach(memb -> { SearchCond cond = SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond()); long count = anySearchDAO.count( - realmDAO.getRoot(), true, Set.of(SyncopeConstants.ROOT_REALM), cond, AnyTypeKind.ANY_OBJECT); + realmDAO.getRoot(), true, Set.of(SyncopeConstants.ROOT_REALM), cond, memb.getAnyType().getKind()); for (int page = 0; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE); page++) { - List matching = anySearchDAO.search( + List matching = anySearchDAO.search( realmDAO.getRoot(), true, Set.of(SyncopeConstants.ROOT_REALM), cond, PageRequest.of(page, AnyDAO.DEFAULT_PAGE_SIZE), - AnyTypeKind.ANY_OBJECT); + memb.getAnyType().getKind()); matching.forEach(any -> { neo4jClient.query( - "MATCH (a:" + Neo4jAnyObject.NODE + " {id: $aid}), (b:" + Neo4jGroup.NODE + "{id: $gid}) " - + "CREATE (a)-[:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]->(b)"). + "MATCH (a:" + (memb.getAnyType().getKind() == AnyTypeKind.USER + ? Neo4jUser.NODE : Neo4jAnyObject.NODE) + + " {id: $aid}), (b:" + Neo4jGroup.NODE + "{id: $gid}) " + + "CREATE (a)-[:" + DYN_GROUP_MEMBERSHIP_REL + "]->(b)"). bindAll(Map.of("aid", any.getKey(), "gid", merged.getKey())).run(); publisher.publishEvent( @@ -431,16 +403,11 @@ public void delete(final Group group) { new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, leftEnd, AuthContextUtils.getDomain())); }); - clearUDynMembers(group); - clearADynMembers(group); - - Optional.ofNullable(group.getUDynMembership()). - ifPresent(r -> neo4jTemplate.deleteById(r.getKey(), Neo4jUDynGroupMembership.class)); + clearDynMembers(group); groupCache.remove(EntityCacheKey.of(group.getKey())); - cascadeDelete( - Neo4jADynGroupMembership.NODE, + cascadeDelete(Neo4jDynGroupMembership.NODE, Neo4jGroup.NODE, group.getKey()); @@ -465,7 +432,7 @@ public List findTypeExtensions(final AnyTypeClass anyTypeCla @Override public long countADynMembers(final Group group) { return neo4jTemplate.count( - "MATCH (n)-[:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-" + "MATCH (n:" + Neo4jAnyObject.NODE + ")-[:" + DYN_GROUP_MEMBERSHIP_REL + "]-" + "(p:" + Neo4jGroup.NODE + " {id: $id}) " + "RETURN COUNT(DISTINCT n.id)", Map.of("id", group.getKey())); } @@ -473,7 +440,7 @@ public long countADynMembers(final Group group) { @Override public long countUDynMembers(final Group group) { return neo4jTemplate.count( - "MATCH (n)-[:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]-" + "MATCH (n:" + Neo4jUser.NODE + ")-[:" + DYN_GROUP_MEMBERSHIP_REL + "]-" + "(p:" + Neo4jGroup.NODE + " {id: $id}) " + "RETURN COUNT(DISTINCT n.id)", Map.of("id", group.getKey())); } @@ -481,7 +448,7 @@ public long countUDynMembers(final Group group) { @Override public List findADynMembers(final Group group) { return neo4jClient.query( - "MATCH (n)-[:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-" + "MATCH (n:" + Neo4jAnyObject.NODE + ")-[:" + DYN_GROUP_MEMBERSHIP_REL + "]-" + "(p:" + Neo4jGroup.NODE + " {id: $id}) " + "RETURN DISTINCT n.id").bindAll(Map.of("id", group.getKey())).fetch().all().stream(). map(found -> found.get("n.id").toString()).toList(); @@ -490,34 +457,25 @@ public List findADynMembers(final Group group) { @Override public List findUDynMembers(final Group group) { return neo4jClient.query( - "MATCH (n)-[:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]-" + "MATCH (n:" + Neo4jUser.NODE + ")-[:" + DYN_GROUP_MEMBERSHIP_REL + "]-" + "(p:" + Neo4jGroup.NODE + " {id: $id}) " + "RETURN DISTINCT n.id").bindAll(Map.of("id", group.getKey())).fetch().all().stream(). map(found -> found.get("n.id").toString()).toList(); } @Override - public void clearADynMembers(final Group group) { + public void clearDynMembers(final Group group) { neo4jClient.query( - "MATCH (n)-[r:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-(p:" + Neo4jGroup.NODE + " {id: $id})" + "MATCH (n)-[r:" + DYN_GROUP_MEMBERSHIP_REL + "]-(p:" + Neo4jGroup.NODE + " {id: $id})" + "DETACH DELETE r"). bindAll(Map.of("id", group.getKey())).run(); } - @Override - public void clearUDynMembers(final Group group) { - neo4jClient.query( - "MATCH (n)-[r:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]-(p:" + Neo4jGroup.NODE + " {id: $id}) " - + "DETACH DELETE r"). - bindAll(Map.of("id", group.getKey())).run(); - } - - protected List findWithADynMemberships(final AnyType anyType) { - return findByRelationship( - Neo4jADynGroupMembership.NODE, + protected List findWithDynMemberships(final AnyType anyType) { + return findByRelationship(Neo4jDynGroupMembership.NODE, Neo4jAnyType.NODE, anyType.getKey(), - Neo4jADynGroupMembership.class, + Neo4jDynGroupMembership.class, null); } @@ -526,7 +484,7 @@ protected List findWithADynMemberships(final AnyType anyTyp public GroupDAO.DynMembershipInfo refreshDynMemberships(final AnyObject anyObject) { Set before = new HashSet<>(); Set after = new HashSet<>(); - findWithADynMemberships(anyObject.getType()).forEach(memb -> { + findWithDynMemberships(anyObject.getType()).forEach(memb -> { boolean matches = anyMatchDAO.matches( anyObject, SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond())); if (matches) { @@ -535,7 +493,7 @@ public GroupDAO.DynMembershipInfo refreshDynMemberships(final AnyObject anyObjec boolean existing = neo4jTemplate.count( "MATCH (n:" + Neo4jAnyObject.NODE + " {id: $aid})-" - + "[r:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-" + + "[r:" + DYN_GROUP_MEMBERSHIP_REL + "]-" + "(p:" + Neo4jGroup.NODE + "{id: $gid}) " + "RETURN COUNT(n)", Map.of("aid", anyObject.getKey(), "gid", memb.getGroup().getKey())) > 0; @@ -546,12 +504,12 @@ public GroupDAO.DynMembershipInfo refreshDynMemberships(final AnyObject anyObjec if (matches && !existing) { neo4jClient.query( "MATCH (a:" + Neo4jAnyObject.NODE + " {id: $aid}), (b:" + Neo4jGroup.NODE + "{id: $gid}) " - + "CREATE (a)-[:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]->(b)"). + + "CREATE (a)-[:" + DYN_GROUP_MEMBERSHIP_REL + "]->(b)"). bindAll(Map.of("aid", anyObject.getKey(), "gid", memb.getGroup().getKey())).run(); } else if (!matches && existing) { neo4jClient.query( "MATCH (n {id: $aid})-" - + "[r:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-" + + "[r:" + DYN_GROUP_MEMBERSHIP_REL + "]-" + "(p:" + Neo4jGroup.NODE + " {id: $gid}) " + "DETACH DELETE r"). bindAll(Map.of("aid", anyObject.getKey(), "gid", memb.getGroup().getKey())).run(); @@ -569,7 +527,7 @@ public Set removeDynMemberships(final AnyObject anyObject) { List dynGroups = anyObjectDAO.findDynGroups(anyObject.getKey()); neo4jClient.query( - "MATCH (n {id: $id})-[r:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-(p:" + Neo4jGroup.NODE + ") " + "MATCH (n {id: $id})-[r:" + DYN_GROUP_MEMBERSHIP_REL + "]-(p:" + Neo4jGroup.NODE + ") " + "DETACH DELETE r").bindAll(Map.of("id", anyObject.getKey())).run(); Set before = new HashSet<>(); @@ -583,17 +541,12 @@ public Set removeDynMemberships(final AnyObject anyObject) { return before; } - protected List findWithUDynMemberships() { - return neo4jTemplate.findAll(Neo4jUDynGroupMembership.class).stream(). - map(UDynGroupMembership.class::cast).toList(); - } - @Transactional @Override public GroupDAO.DynMembershipInfo refreshDynMemberships(final User user) { Set before = new HashSet<>(); Set after = new HashSet<>(); - findWithUDynMemberships().forEach(memb -> { + findWithDynMemberships(user.getType()).forEach(memb -> { boolean matches = anyMatchDAO.matches( user, SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond())); if (matches) { @@ -602,7 +555,7 @@ public GroupDAO.DynMembershipInfo refreshDynMemberships(final User user) { boolean existing = neo4jTemplate.count( "MATCH (n:" + Neo4jUser.NODE + " {id: $aid})-" - + "[r:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]-" + + "[r:" + DYN_GROUP_MEMBERSHIP_REL + "]-" + "(p:" + Neo4jGroup.NODE + "{id: $gid}) " + "RETURN COUNT(n)", Map.of("aid", user.getKey(), "gid", memb.getGroup().getKey())) > 0; @@ -613,12 +566,12 @@ public GroupDAO.DynMembershipInfo refreshDynMemberships(final User user) { if (matches && !existing) { neo4jClient.query( "MATCH (a:" + Neo4jUser.NODE + " {id: $aid}), (b:" + Neo4jGroup.NODE + "{id: $gid}) " - + "CREATE (a)-[:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]->(b)"). + + "CREATE (a)-[:" + DYN_GROUP_MEMBERSHIP_REL + "]->(b)"). bindAll(Map.of("aid", user.getKey(), "gid", memb.getGroup().getKey())).run(); } else if (!matches && existing) { neo4jClient.query( "MATCH (n {id: $aid})-" - + "[r:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]-" + + "[r:" + DYN_GROUP_MEMBERSHIP_REL + "]-" + "(p:" + Neo4jGroup.NODE + " {id: $gid}) " + "DETACH DELETE r"). bindAll(Map.of("aid", user.getKey(), "gid", memb.getGroup().getKey())).run(); @@ -636,7 +589,7 @@ public Set removeDynMemberships(final User user) { List dynGroups = userDAO.findDynGroups(user.getKey()); neo4jClient.query( - "MATCH (n {id: $id})-[r:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]-(p:" + Neo4jGroup.NODE + ") " + "MATCH (n {id: $id})-[r:" + DYN_GROUP_MEMBERSHIP_REL + "]-(p:" + Neo4jGroup.NODE + ") " + "DETACH DELETE r").bindAll(Map.of("id", user.getKey())).run(); Set before = new HashSet<>(); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExt.java index 44bfa78da6f..8a544c786b4 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExt.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExt.java @@ -36,7 +36,7 @@ public interface UserRepoExt extends AnyRepoExt { String CACHE = "userCache"; - Optional findByToken(String token); + Optional findByToken(String token); List findBySecurityQuestion(SecurityQuestion securityQuestion); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java index 373781ac3a2..cd325793e7f 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java @@ -32,6 +32,7 @@ import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.DelegationDAO; @@ -39,10 +40,8 @@ import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.RoleDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; -import org.apache.syncope.core.persistence.api.entity.Attributable; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Role; import org.apache.syncope.core.persistence.api.entity.group.Group; @@ -95,7 +94,6 @@ public UserRepoExtImpl( final AnyUtilsFactory anyUtilsFactory, final AnyTypeDAO anyTypeDAO, final AnyTypeClassDAO anyTypeClassDAO, - final PlainSchemaDAO plainSchemaDAO, final DerSchemaDAO derSchemaDAO, final DynRealmDAO dynRealmDAO, final RoleDAO roleDAO, @@ -103,6 +101,7 @@ public UserRepoExtImpl( final GroupDAO groupDAO, final DelegationDAO delegationDAO, final FIQLQueryDAO fiqlQueryDAO, + final AnyChecker anyChecker, final AnyFinder anyFinder, final SecurityProperties securityProperties, final Neo4jTemplate neo4jTemplate, @@ -113,9 +112,9 @@ public UserRepoExtImpl( super( anyTypeDAO, anyTypeClassDAO, - plainSchemaDAO, derSchemaDAO, dynRealmDAO, + anyChecker, anyFinder, anyUtilsFactory.getInstance(AnyTypeKind.USER), neo4jTemplate, @@ -136,11 +135,11 @@ protected Cache cache() { } @Override - public Optional findByToken(final String token) { + public Optional findByToken(final String token) { return neo4jClient.query( "MATCH (n:" + Neo4jUser.NODE + ") WHERE n.token = $token RETURN n.id"). bindAll(Map.of("token", token)).fetch().one(). - flatMap(toOptional("n.id", Neo4jUser.class, userCache)); + map(found -> found.get("n.id").toString()); } @Override @@ -226,14 +225,9 @@ public void deleteMembership(final UMembership membership) { neo4jTemplate.deleteById(membership.getKey(), Neo4jUMembership.class); } - @Override - protected void checkBeforeSave(final T user) { - super.checkBeforeSave(user); - ((User) user).getLinkedAccounts().forEach(super::checkBeforeSave); - } - protected Pair doSave(final User user) { - checkBeforeSave(user); + anyChecker.checkBeforeSave(user, anyUtils); + user.getLinkedAccounts().forEach(account -> anyChecker.checkBeforeSave(account, anyUtils)); // unlink any role, resource, aux class or security question that was unlinked from user // delete any membership, relationship or linked account that was removed from user @@ -260,8 +254,7 @@ protected Pair doSave(final User user) { auxClass.getKey(), Neo4jUser.USER_AUX_CLASSES_REL)); if (before.getSecurityQuestion() != null && user.getSecurityQuestion() == null) { - deleteRelationship( - Neo4jUser.NODE, + deleteRelationship(Neo4jUser.NODE, Neo4jSecurityQuestion.NODE, user.getKey(), before.getSecurityQuestion().getKey(), @@ -367,7 +360,7 @@ public List findDynRoles(final String key) { public List findDynGroups(final String key) { return toList(neo4jClient.query( "MATCH (n:" + Neo4jUser.NODE + " {id: $id})-" - + "[:" + GroupRepoExt.DYN_GROUP_USER_MEMBERSHIP_REL + "]-" + + "[:" + GroupRepoExt.DYN_GROUP_MEMBERSHIP_REL + "]-" + "(p:" + Neo4jGroup.NODE + ") " + "RETURN p.id").bindAll(Map.of("id", key)).fetch().all(), "p.id", diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractAny.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractAny.java index d45097c03c9..b4c1606428f 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractAny.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractAny.java @@ -22,10 +22,8 @@ import java.time.OffsetDateTime; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.Realm; -import org.apache.syncope.core.persistence.common.validation.AnyCheck; import org.springframework.data.neo4j.core.schema.Relationship; -@AnyCheck public abstract class AbstractAny extends AbstractAttributable implements Any { private static final long serialVersionUID = -2666540708092702810L; diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAuditEvent.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAuditEvent.java index 3cbf852cb71..546b1595401 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAuditEvent.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAuditEvent.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.time.OffsetDateTime; import java.util.List; @@ -26,6 +25,7 @@ import org.apache.syncope.core.persistence.api.entity.AuditEvent; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.springframework.data.neo4j.core.schema.Node; +import tools.jackson.core.type.TypeReference; @Node(Neo4jAuditEvent.NODE) public class Neo4jAuditEvent extends AbstractGeneratedKeyNode implements AuditEvent { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jConnInstance.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jConnInstance.java index da1dfb8b273..3661ada1e93 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jConnInstance.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jConnInstance.java @@ -18,19 +18,16 @@ */ package org.apache.syncope.core.persistence.neo4j.entity; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; -import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.syncope.common.lib.types.ConnPoolConf; import org.apache.syncope.common.lib.types.ConnectorCapability; import org.apache.syncope.core.persistence.api.entity.ConnInstance; -import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.common.validation.ConnInstanceCheck; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @@ -38,6 +35,7 @@ import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.PostLoad; import org.springframework.data.neo4j.core.schema.Relationship; +import tools.jackson.core.type.TypeReference; @Node(Neo4jConnInstance.NODE) @ConnInstanceCheck @@ -92,6 +90,9 @@ public class Neo4jConnInstance extends AbstractGeneratedKeyNode implements ConnI */ private String jsonConf; + @Transient + private List conf = new ArrayList<>(); + private String displayName; /** @@ -106,12 +107,6 @@ public class Neo4jConnInstance extends AbstractGeneratedKeyNode implements ConnI @Relationship(direction = Relationship.Direction.OUTGOING, cascadeUpdates = false) private Neo4jRealm adminRealm; - /** - * External resources associated to the connector. - */ - @Relationship(type = Neo4jExternalResource.RESOURCE_CONNECTOR_REL, direction = Relationship.Direction.INCOMING) - private List resources = new ArrayList<>(); - @Override public Realm getAdminRealm() { return adminRealm; @@ -165,14 +160,7 @@ public void setVersion(final String version) { @Override public List getConf() { - return StringUtils.isNotBlank(jsonConf) - ? POJOHelper.deserialize(jsonConf, CONN_CONF_PROPS_TYPEREF) - : new ArrayList<>(); - } - - @Override - public void setConf(final List conf) { - jsonConf = POJOHelper.serialize(conf); + return conf; } @Override @@ -185,17 +173,6 @@ public void setDisplayName(final String displayName) { this.displayName = displayName; } - @Override - public List getResources() { - return resources; - } - - @Override - public boolean add(final ExternalResource resource) { - checkType(resource, Neo4jExternalResource.class); - return resources.contains((Neo4jExternalResource) resource) || resources.add((Neo4jExternalResource) resource); - } - @Override public Set getCapabilities() { return capabilitiesSet; @@ -224,10 +201,12 @@ public void setPoolConf(final ConnPoolConf poolConf) { protected void json2list(final boolean clearFirst) { if (clearFirst) { getCapabilities().clear(); + getConf().clear(); } - if (capabilities != null) { - getCapabilities().addAll(POJOHelper.deserialize(capabilities, CONNECTOR_CAPABILITY_TYPEREF)); - } + Optional.ofNullable(capabilities). + ifPresent(v -> getCapabilities().addAll(POJOHelper.deserialize(v, CONNECTOR_CAPABILITY_TYPEREF))); + Optional.ofNullable(jsonConf). + ifPresent(v -> getConf().addAll(POJOHelper.deserialize(v, CONN_CONF_PROPS_TYPEREF))); } @PostLoad @@ -241,5 +220,6 @@ public void postSave() { public void list2json() { capabilities = POJOHelper.serialize(getCapabilities()); + jsonConf = POJOHelper.serialize(getConf()); } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jADynGroupMembership.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDynGroupMembership.java similarity index 76% rename from core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jADynGroupMembership.java rename to core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDynGroupMembership.java index 218ed2e2535..6537884ad80 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jADynGroupMembership.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDynGroupMembership.java @@ -16,24 +16,22 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.neo4j.entity.anyobject; +package org.apache.syncope.core.persistence.neo4j.entity; +import jakarta.validation.constraints.NotNull; import org.apache.syncope.core.persistence.api.entity.AnyType; -import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; -import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; import org.apache.syncope.core.persistence.api.entity.group.Group; -import org.apache.syncope.core.persistence.neo4j.entity.AbstractDynMembership; -import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.Relationship; -@Node(Neo4jADynGroupMembership.NODE) -public class Neo4jADynGroupMembership extends AbstractDynMembership implements ADynGroupMembership { +@Node(Neo4jDynGroupMembership.NODE) +public class Neo4jDynGroupMembership extends AbstractGeneratedKeyNode implements DynGroupMembership { private static final long serialVersionUID = -7336814163949640354L; - public static final String NODE = "ADynGroupMembership"; + public static final String NODE = "DynGroupMembership"; @Relationship(direction = Relationship.Direction.OUTGOING, cascadeUpdates = false) private Neo4jGroup group; @@ -41,6 +39,9 @@ public class Neo4jADynGroupMembership extends AbstractDynMembership i @Relationship(direction = Relationship.Direction.OUTGOING, cascadeUpdates = false) private Neo4jAnyType anyType; + @NotNull + private String fiql; + @Override public Group getGroup() { return group; @@ -62,4 +63,14 @@ public void setAnyType(final AnyType anyType) { checkType(anyType, Neo4jAnyType.class); this.anyType = (Neo4jAnyType) anyType; } + + @Override + public String getFIQLCond() { + return fiql; + } + + @Override + public void setFIQLCond(final String fiql) { + this.fiql = fiql; + } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java index d1f6a72e2ec..aecd4c773a7 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java @@ -30,6 +30,7 @@ import org.apache.syncope.core.persistence.api.entity.ConnInstance; import org.apache.syncope.core.persistence.api.entity.Delegation; import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; import org.apache.syncope.core.persistence.api.entity.DynRealm; import org.apache.syncope.core.persistence.api.entity.DynRealmMembership; import org.apache.syncope.core.persistence.api.entity.Entity; @@ -59,7 +60,6 @@ import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity; import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp; import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry; -import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; @@ -92,7 +92,6 @@ import org.apache.syncope.core.persistence.api.entity.task.SchedTask; import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion; -import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; import org.apache.syncope.core.persistence.api.entity.user.UMembership; import org.apache.syncope.core.persistence.api.entity.user.URelationship; import org.apache.syncope.core.persistence.api.entity.user.User; @@ -106,7 +105,6 @@ import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jSAML2IdPEntity; import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jSAML2SPClientApp; import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jWAConfigEntry; -import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jADynGroupMembership; import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAMembership; import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jARelationship; import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; @@ -139,7 +137,6 @@ import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jSchedTask; import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jLinkedAccount; import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jSecurityQuestion; -import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUDynGroupMembership; import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUMembership; import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jURelationship; import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; @@ -246,10 +243,8 @@ public E newEntity(final Class reference) { result = (E) new Neo4jSecurityQuestion(); } else if (reference.equals(AuditConf.class)) { result = (E) new Neo4jAuditConf(); - } else if (reference.equals(ADynGroupMembership.class)) { - result = (E) new Neo4jADynGroupMembership(); - } else if (reference.equals(UDynGroupMembership.class)) { - result = (E) new Neo4jUDynGroupMembership(); + } else if (reference.equals(DynGroupMembership.class)) { + result = (E) new Neo4jDynGroupMembership(); } else if (reference.equals(AccessToken.class)) { result = (E) new Neo4jAccessToken(); } else if (reference.equals(Implementation.class)) { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java index 65cd7dedb5d..dca3a66ac39 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; @@ -52,6 +51,7 @@ import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.PostLoad; import org.springframework.data.neo4j.core.schema.Relationship; +import tools.jackson.core.type.TypeReference; @Node(Neo4jExternalResource.NODE) @ExternalResourceCheck @@ -132,31 +132,31 @@ public class Neo4jExternalResource extends AbstractProvidedKeyNode implements Ex @Relationship(type = RESOURCE_CONNECTOR_REL, direction = Relationship.Direction.OUTGOING, cascadeUpdates = false) private Neo4jConnInstance connector; - @Relationship(type = RESOURCE_PASSWORD_POLICY_REL, - direction = Relationship.Direction.OUTGOING, cascadeUpdates = false) + @Relationship(type = RESOURCE_PASSWORD_POLICY_REL, direction = Relationship.Direction.OUTGOING, cascadeUpdates = + false) private Neo4jPasswordPolicy passwordPolicy; - @Relationship(type = RESOURCE_ACCOUNT_POLICY_REL, - direction = Relationship.Direction.OUTGOING, cascadeUpdates = false) + @Relationship(type = RESOURCE_ACCOUNT_POLICY_REL, direction = Relationship.Direction.OUTGOING, cascadeUpdates = + false) private Neo4jAccountPolicy accountPolicy; - @Relationship(type = RESOURCE_PROPAGATION_POLICY_REL, - direction = Relationship.Direction.OUTGOING, cascadeUpdates = false) + @Relationship(type = RESOURCE_PROPAGATION_POLICY_REL, direction = Relationship.Direction.OUTGOING, cascadeUpdates = + false) private Neo4jPropagationPolicy propagationPolicy; - @Relationship(type = RESOURCE_INBOUND_POLICY_REL, - direction = Relationship.Direction.OUTGOING, cascadeUpdates = false) + @Relationship(type = RESOURCE_INBOUND_POLICY_REL, direction = Relationship.Direction.OUTGOING, cascadeUpdates = + false) private Neo4jInboundPolicy inboundPolicy; @Relationship(type = RESOURCE_PUSH_POLICY_REL, direction = Relationship.Direction.OUTGOING, cascadeUpdates = false) private Neo4jPushPolicy pushPolicy; - @Relationship(type = RESOURCE_PROVISION_SORTER_REL, - direction = Relationship.Direction.OUTGOING, cascadeUpdates = false) + @Relationship(type = RESOURCE_PROVISION_SORTER_REL, direction = Relationship.Direction.OUTGOING, cascadeUpdates = + false) private Neo4jImplementation provisionSorter; - @Relationship(type = RESOURCE_PROPAGATION_ACTIONS_REL, - direction = Relationship.Direction.OUTGOING, cascadeUpdates = false) + @Relationship(type = RESOURCE_PROPAGATION_ACTIONS_REL, direction = Relationship.Direction.OUTGOING, cascadeUpdates = + false) private SortedSet propagationActions = new TreeSet<>(); @Transient diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jNotification.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jNotification.java index 3a0be5046fc..f6c78f382b5 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jNotification.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jNotification.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; @@ -35,6 +34,7 @@ import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.PostLoad; import org.springframework.data.neo4j.core.schema.Relationship; +import tools.jackson.core.type.TypeReference; @Node(Neo4jNotification.NODE) public class Neo4jNotification extends AbstractGeneratedKeyNode implements Notification { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainSchema.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainSchema.java index c152c63b6d6..8c717988eee 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainSchema.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainSchema.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.HashMap; import java.util.Map; @@ -34,6 +33,7 @@ import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.PostLoad; import org.springframework.data.neo4j.core.schema.Relationship; +import tools.jackson.core.type.TypeReference; @Node(Neo4jPlainSchema.NODE) @PlainSchemaCheck diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRole.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRole.java index 05a1a01f4aa..36208b3bda3 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRole.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRole.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity; -import com.fasterxml.jackson.core.type.TypeReference; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -32,6 +31,7 @@ import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.PostLoad; import org.springframework.data.neo4j.core.schema.Relationship; +import tools.jackson.core.type.TypeReference; @Node(Neo4jRole.NODE) @RoleCheck diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSchema.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSchema.java index 649ee1b398b..400a8d0b3c2 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSchema.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSchema.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity; -import com.fasterxml.jackson.core.type.TypeReference; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -29,6 +28,7 @@ import org.springframework.data.annotation.Transient; import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.PostLoad; +import tools.jackson.core.type.TypeReference; @Node(Neo4jSchema.NODE) @SchemaKeyCheck diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/AbstractClientApp.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/AbstractClientApp.java index 8097840ca33..58499750f03 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/AbstractClientApp.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/AbstractClientApp.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity.am; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; @@ -40,6 +39,7 @@ import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jTicketExpirationPolicy; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.springframework.data.neo4j.core.schema.Relationship; +import tools.jackson.core.type.TypeReference; public abstract class AbstractClientApp extends AbstractGeneratedKeyNode implements ClientApp { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAttrRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAttrRepo.java index bd8eef827c6..2281ed1d6cb 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAttrRepo.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAttrRepo.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity.am; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; @@ -33,6 +32,7 @@ import org.springframework.data.annotation.Transient; import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.PostLoad; +import tools.jackson.core.type.TypeReference; @Node(Neo4jAttrRepo.NODE) public class Neo4jAttrRepo extends AbstractProvidedKeyNode implements AttrRepo { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthModule.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthModule.java index c01c9bac5eb..be5d5ace26a 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthModule.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthModule.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity.am; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; @@ -33,6 +32,7 @@ import org.springframework.data.annotation.Transient; import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.PostLoad; +import tools.jackson.core.type.TypeReference; @Node(Neo4jAuthModule.NODE) public class Neo4jAuthModule extends AbstractProvidedKeyNode implements AuthModule { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthProfile.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthProfile.java index 5927b1b82b5..8e857837284 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthProfile.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthProfile.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity.am; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; @@ -31,7 +30,10 @@ import org.apache.syncope.core.persistence.api.entity.am.AuthProfile; import org.apache.syncope.core.persistence.neo4j.entity.AbstractGeneratedKeyNode; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.annotation.Transient; import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; +import tools.jackson.core.type.TypeReference; @Node(Neo4jAuthProfile.NODE) public class Neo4jAuthProfile extends AbstractGeneratedKeyNode implements AuthProfile { @@ -63,16 +65,31 @@ public class Neo4jAuthProfile extends AbstractGeneratedKeyNode implements AuthPr @NotNull private String owner; - private String impersonationAccounts; - private String googleMfaAuthAccounts; + @Transient + private List googleMfaAuthAccountsList = new ArrayList<>(); + private String googleMfaAuthTokens; + @Transient + private List googleMfaAuthTokensList = new ArrayList<>(); + private String mfaTrustedDevices; + @Transient + private List mfaTrustedDevicesList = new ArrayList<>(); + + private String impersonationAccounts; + + @Transient + private List impersonationAccountsList = new ArrayList<>(); + private String webAuthnDeviceCredentials; + @Transient + private List webAuthnDeviceCredentialsList = new ArrayList<>(); + @Override public String getOwner() { return owner; @@ -83,58 +100,90 @@ public void setOwner(final String owner) { this.owner = owner; } + @Override + public boolean add(final GoogleMfaAuthToken googleMfaAuthToken) { + return googleMfaAuthTokensList.add(googleMfaAuthToken); + } + @Override public List getGoogleMfaAuthTokens() { - return Optional.ofNullable(googleMfaAuthTokens). - map(v -> POJOHelper.deserialize(v, GOOGLE_MFA_TOKENS_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); + return googleMfaAuthTokensList; } @Override - public void setGoogleMfaAuthTokens(final List tokens) { - googleMfaAuthTokens = POJOHelper.serialize(tokens); + public boolean add(final GoogleMfaAuthAccount googleMfaAuthAccount) { + return googleMfaAuthAccountsList.add(googleMfaAuthAccount); } @Override public List getGoogleMfaAuthAccounts() { - return Optional.ofNullable(googleMfaAuthAccounts). - map(v -> POJOHelper.deserialize(v, GOOGLE_MFA_ACCOUNTS_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); + return googleMfaAuthAccountsList; } @Override - public void setGoogleMfaAuthAccounts(final List accounts) { - googleMfaAuthAccounts = POJOHelper.serialize(accounts); + public boolean add(final MfaTrustedDevice mfaTrustedDevice) { + return mfaTrustedDevicesList.add(mfaTrustedDevice); } @Override public List getMfaTrustedDevices() { - return Optional.ofNullable(mfaTrustedDevices). - map(v -> POJOHelper.deserialize(v, MFA_TRUSTED_DEVICE_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); + return mfaTrustedDevicesList; } @Override - public void setMfaTrustedDevices(final List devices) { - mfaTrustedDevices = POJOHelper.serialize(devices); + public boolean add(final ImpersonationAccount impersonationAccount) { + return impersonationAccountsList.add(impersonationAccount); } @Override public List getImpersonationAccounts() { - return Optional.ofNullable(impersonationAccounts). - map(v -> POJOHelper.deserialize(v, IMPERSONATION_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); + return impersonationAccountsList; } @Override - public void setImpersonationAccounts(final List accounts) { - impersonationAccounts = POJOHelper.serialize(accounts); + public boolean add(final WebAuthnDeviceCredential webAuthnDeviceCredential) { + return webAuthnDeviceCredentialsList.add(webAuthnDeviceCredential); } @Override public List getWebAuthnDeviceCredentials() { - return Optional.ofNullable(webAuthnDeviceCredentials). - map(v -> POJOHelper.deserialize(v, WEBAUTHN_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); + return webAuthnDeviceCredentialsList; } - @Override - public void setWebAuthnDeviceCredentials(final List credentials) { - webAuthnDeviceCredentials = POJOHelper.serialize(credentials); + protected void json2list(final boolean clearFirst) { + if (clearFirst) { + getGoogleMfaAuthTokens().clear(); + getGoogleMfaAuthAccounts().clear(); + getMfaTrustedDevices().clear(); + getImpersonationAccounts().clear(); + getWebAuthnDeviceCredentials().clear(); + } + Optional.ofNullable(googleMfaAuthTokens).ifPresent(v -> getGoogleMfaAuthTokens(). + addAll(POJOHelper.deserialize(v, GOOGLE_MFA_TOKENS_TYPEREF))); + Optional.ofNullable(googleMfaAuthAccounts).ifPresent(v -> getGoogleMfaAuthAccounts(). + addAll(POJOHelper.deserialize(v, GOOGLE_MFA_ACCOUNTS_TYPEREF))); + Optional.ofNullable(mfaTrustedDevices).ifPresent(v -> getMfaTrustedDevices(). + addAll(POJOHelper.deserialize(v, MFA_TRUSTED_DEVICE_TYPEREF))); + Optional.ofNullable(impersonationAccounts).ifPresent(v -> getImpersonationAccounts(). + addAll(POJOHelper.deserialize(v, IMPERSONATION_TYPEREF))); + Optional.ofNullable(webAuthnDeviceCredentials).ifPresent(v -> getWebAuthnDeviceCredentials(). + addAll(POJOHelper.deserialize(v, WEBAUTHN_TYPEREF))); + } + + @PostLoad + public void postLoad() { + json2list(false); + } + + public void postSave() { + json2list(true); + } + + public void list2json() { + googleMfaAuthTokens = POJOHelper.serialize(getGoogleMfaAuthTokens()); + googleMfaAuthAccounts = POJOHelper.serialize(getGoogleMfaAuthAccounts()); + mfaTrustedDevices = POJOHelper.serialize(getMfaTrustedDevices()); + impersonationAccounts = POJOHelper.serialize(getImpersonationAccounts()); + webAuthnDeviceCredentials = POJOHelper.serialize(getWebAuthnDeviceCredentials()); } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java index 2e6d41b6ceb..061e0ccb6d1 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity.am; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.HashSet; import java.util.Set; @@ -35,6 +34,7 @@ import org.springframework.data.annotation.Transient; import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.PostLoad; +import tools.jackson.core.type.TypeReference; @Node(Neo4jOIDCRPClientApp.NODE) public class Neo4jOIDCRPClientApp extends AbstractClientApp implements OIDCRPClientApp { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2SPClientApp.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2SPClientApp.java index 3c6902bfd7e..4be1542e75a 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2SPClientApp.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2SPClientApp.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity.am; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.HashSet; @@ -32,6 +31,7 @@ import org.springframework.data.annotation.Transient; import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.PostLoad; +import tools.jackson.core.type.TypeReference; @Node(Neo4jSAML2SPClientApp.NODE) public class Neo4jSAML2SPClientApp extends AbstractClientApp implements SAML2SPClientApp { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jWAConfigEntry.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jWAConfigEntry.java index 665b1408bf6..de072bf1aba 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jWAConfigEntry.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jWAConfigEntry.java @@ -18,13 +18,13 @@ */ package org.apache.syncope.core.persistence.neo4j.entity.am; -import com.fasterxml.jackson.core.type.TypeReference; import java.util.List; import java.util.Optional; import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry; import org.apache.syncope.core.persistence.neo4j.entity.AbstractProvidedKeyNode; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.springframework.data.neo4j.core.schema.Node; +import tools.jackson.core.type.TypeReference; @Node(Neo4jWAConfigEntry.NODE) public class Neo4jWAConfigEntry extends AbstractProvidedKeyNode implements WAConfigEntry { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGroup.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGroup.java index 07882a2f8e3..2a828926a6e 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGroup.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGroup.java @@ -28,23 +28,21 @@ import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.entity.AnyType; import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.PlainAttr; -import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.api.entity.group.GRelationship; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.group.GroupTypeExtension; -import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.common.validation.AttributableCheck; import org.apache.syncope.core.persistence.common.validation.GroupCheck; import org.apache.syncope.core.persistence.neo4j.entity.AbstractRelatable; import org.apache.syncope.core.persistence.neo4j.entity.AbstractRelationship; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDynGroupMembership; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; -import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jADynGroupMembership; -import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUDynGroupMembership; import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; import org.springframework.data.neo4j.core.schema.CompositeProperty; import org.springframework.data.neo4j.core.schema.Node; @@ -93,10 +91,7 @@ public class Neo4jGroup protected List auxClasses = new ArrayList<>(); @Relationship(direction = Relationship.Direction.INCOMING) - private Neo4jUDynGroupMembership uDynMembership; - - @Relationship(direction = Relationship.Direction.INCOMING) - private List aDynMemberships = new ArrayList<>(); + private List dynMemberships = new ArrayList<>(); @Relationship(type = GROUP_TYPE_EXTENSION_REL, direction = Relationship.Direction.INCOMING) private List typeExtensions = new ArrayList<>(); @@ -162,17 +157,6 @@ public void setGroupOwner(final Group group) { this.groupOwner = (Neo4jGroup) group; } - @Override - public UDynGroupMembership getUDynMembership() { - return uDynMembership; - } - - @Override - public void setUDynMembership(final UDynGroupMembership uDynMembership) { - checkType(uDynMembership, Neo4jUDynGroupMembership.class); - this.uDynMembership = (Neo4jUDynGroupMembership) uDynMembership; - } - @Override public boolean add(final AnyTypeClass auxClass) { checkType(auxClass, Neo4jAnyTypeClass.class); @@ -185,21 +169,21 @@ public List getAuxClasses() { } @Override - public boolean add(final ADynGroupMembership dynGroupMembership) { - checkType(dynGroupMembership, Neo4jADynGroupMembership.class); - return aDynMemberships.add((Neo4jADynGroupMembership) dynGroupMembership); + public boolean add(final DynGroupMembership dynGroupMembership) { + checkType(dynGroupMembership, Neo4jDynGroupMembership.class); + return dynMemberships.add((Neo4jDynGroupMembership) dynGroupMembership); } @Override - public Optional getADynMembership(final AnyType anyType) { - return aDynMemberships.stream(). + public Optional getDynMembership(final AnyType anyType) { + return dynMemberships.stream(). filter(dynGroupMembership -> anyType != null && anyType.equals(dynGroupMembership.getAnyType())). findFirst(); } @Override - public List getADynMemberships() { - return aDynMemberships; + public List getDynMemberships() { + return dynMemberships; } @Override diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jConfParam.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jConfParam.java index 41ef24c3f04..0e9591ae87a 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jConfParam.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jConfParam.java @@ -18,13 +18,13 @@ */ package org.apache.syncope.core.persistence.neo4j.entity.keymaster; -import com.fasterxml.jackson.core.JacksonException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.core.persistence.api.entity.keymaster.ConfParam; import org.apache.syncope.core.persistence.neo4j.entity.AbstractProvidedKeyNode; import org.springframework.data.neo4j.core.schema.Node; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; @Node(Neo4jConfParam.NODE) public class Neo4jConfParam extends AbstractProvidedKeyNode implements ConfParam { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java index 65d21af0bc3..2082dfc665c 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity.task; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.HashMap; import java.util.Locale; @@ -35,6 +34,7 @@ import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.PostLoad; import org.springframework.data.neo4j.core.schema.Relationship; +import tools.jackson.core.type.TypeReference; @Node(Neo4jFormPropertyDef.NODE) @FormPropertyDefCheck diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jNotificationTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jNotificationTask.java index 59a95fb1970..aff757c7834 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jNotificationTask.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jNotificationTask.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity.task; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.HashSet; @@ -35,6 +34,7 @@ import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.PostLoad; import org.springframework.data.neo4j.core.schema.Relationship; +import tools.jackson.core.type.TypeReference; @Node(Neo4jNotificationTask.NODE) public class Neo4jNotificationTask extends AbstractTask implements NotificationTask { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTask.java index 24367744034..b789cae765a 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTask.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTask.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity.task; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.HashMap; @@ -42,6 +41,7 @@ import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.PostLoad; import org.springframework.data.neo4j.core.schema.Relationship; +import tools.jackson.core.type.TypeReference; @Node(Neo4jPushTask.NODE) public class Neo4jPushTask extends Neo4jProvisioningTask implements PushTask { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUDynGroupMembership.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUDynGroupMembership.java deleted file mode 100644 index 1b0aa528294..00000000000 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUDynGroupMembership.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.neo4j.entity.user; - -import org.apache.syncope.core.persistence.api.entity.group.Group; -import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; -import org.apache.syncope.core.persistence.api.entity.user.User; -import org.apache.syncope.core.persistence.neo4j.entity.AbstractDynMembership; -import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; -import org.springframework.data.neo4j.core.schema.Node; - -@Node(Neo4jUDynGroupMembership.NODE) -public class Neo4jUDynGroupMembership extends AbstractDynMembership implements UDynGroupMembership { - - private static final long serialVersionUID = -7336814163949640354L; - - public static final String NODE = "UDynGroupMembership"; - - private Neo4jGroup group; - - @Override - public Group getGroup() { - return group; - } - - @Override - public void setGroup(final Group group) { - checkType(group, Neo4jGroup.class); - this.group = (Neo4jGroup) group; - } -} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java index 3e07573523d..6191d350f6f 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.neo4j.entity.user; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import java.time.OffsetDateTime; @@ -27,7 +26,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.ApplicationContextProvider; import org.apache.syncope.core.persistence.api.EncryptorManager; @@ -50,11 +48,11 @@ import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRole; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; -import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.security.SecureRandomUtils; import org.springframework.data.neo4j.core.schema.CompositeProperty; import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.Relationship; +import tools.jackson.core.type.TypeReference; @Node(Neo4jUser.NODE) @AttributableCheck @@ -197,12 +195,7 @@ protected String encode(final String value) throws Exception { return ApplicationContextProvider.getApplicationContext().getBean(EncryptorManager.class).getInstance().encode( value, Optional.ofNullable(cipherAlgorithm). - orElseGet(() -> CipherAlgorithm.valueOf( - ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class).get( - AuthContextUtils.getDomain(), - "password.cipher.algorithm", - CipherAlgorithm.AES.name(), - String.class)))); + orElseThrow(() -> new IllegalStateException("No cipherAlgorithm was set"))); } @Override diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/CacheCleaningTransactionExecutionListener.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/CacheCleaningTransactionExecutionListener.java index 930e43aafb9..a4e316a73b1 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/CacheCleaningTransactionExecutionListener.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/CacheCleaningTransactionExecutionListener.java @@ -21,6 +21,7 @@ import javax.cache.Cache; import org.apache.syncope.core.persistence.neo4j.entity.EntityCacheKey; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jConnInstance; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDelegation; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDerSchema; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; @@ -44,6 +45,8 @@ public class CacheCleaningTransactionExecutionListener implements TransactionExe protected final Cache derSchemaCache; + protected final Cache connInstanceCache; + protected final Cache externalResourceCache; protected final Cache groupCache; @@ -63,6 +66,7 @@ public CacheCleaningTransactionExecutionListener( final Cache anyObjectCache, final Cache delegationCache, final Cache derSchemaCache, + final Cache connInstanceCache, final Cache externalResourceCache, final Cache groupCache, final Cache implementationCache, @@ -75,6 +79,7 @@ public CacheCleaningTransactionExecutionListener( this.anyObjectCache = anyObjectCache; this.delegationCache = delegationCache; this.derSchemaCache = derSchemaCache; + this.connInstanceCache = connInstanceCache; this.externalResourceCache = externalResourceCache; this.groupCache = groupCache; this.implementationCache = implementationCache; diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/DomainRoutingDriver.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/DomainRoutingDriver.java index 2a86dfe3d02..027e1707d34 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/DomainRoutingDriver.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/DomainRoutingDriver.java @@ -26,9 +26,7 @@ import org.neo4j.driver.BookmarkManager; import org.neo4j.driver.Driver; import org.neo4j.driver.ExecutableQuery; -import org.neo4j.driver.Metrics; import org.neo4j.driver.SessionConfig; -import org.neo4j.driver.types.TypeSystem; public class DomainRoutingDriver implements Driver { @@ -78,22 +76,6 @@ public CompletionStage closeAsync() { return delegate().closeAsync(); } - @Override - public Metrics metrics() { - return delegate().metrics(); - } - - @Override - public boolean isMetricsEnabled() { - return delegate().isMetricsEnabled(); - } - - @SuppressWarnings("deprecation") - @Override - public TypeSystem defaultTypeSystem() { - return delegate().defaultTypeSystem(); - } - @Override public void verifyConnectivity() { delegate().verifyConnectivity(); diff --git a/core/persistence-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SyncopeNeo4jRepositoryFactory.java b/core/persistence-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SyncopeNeo4jRepositoryFactory.java index 109c1ed5271..d2b83627b49 100644 --- a/core/persistence-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SyncopeNeo4jRepositoryFactory.java +++ b/core/persistence-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SyncopeNeo4jRepositoryFactory.java @@ -29,7 +29,6 @@ import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.ValueExpressionDelegate; -import org.springframework.lang.Nullable; public class SyncopeNeo4jRepositoryFactory extends RepositoryFactorySupport { @@ -43,8 +42,8 @@ public SyncopeNeo4jRepositoryFactory( } @Override - public EntityInformation getEntityInformation(final Class domainClass) { - return delegate.getEntityInformation(domainClass); + public EntityInformation getEntityInformation(final RepositoryMetadata metadata) { + return delegate.getEntityInformation(metadata); } @Override @@ -64,7 +63,7 @@ protected Class getRepositoryBaseClass(final RepositoryMetadata metadata) { @Override protected Optional getQueryLookupStrategy( - final @Nullable QueryLookupStrategy.Key key, final ValueExpressionDelegate valueExpressionDelegate) { + final QueryLookupStrategy.Key key, final ValueExpressionDelegate valueExpressionDelegate) { return delegate.getQueryLookupStrategy(key, valueExpressionDelegate); } diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthProfileTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthProfileTest.java index 659a6cd81ec..148b06a73e3 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthProfileTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthProfileTest.java @@ -39,7 +39,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.data.neo4j.core.Neo4jTemplate; import org.springframework.transaction.annotation.Transactional; @@ -61,22 +61,27 @@ public void beforeEach() { public void googleMfaToken() { String id = SecureRandomUtils.generateRandomUUID().toString(); - createAuthProfileWithToken(id, 123456); - - Optional result = authProfileDAO.findByOwner(id); - assertTrue(result.isPresent()); - - assertFalse(authProfileDAO.findAll(PageRequest.of(0, 100)).isEmpty()); + AuthProfile profile = entityFactory.newEntity(AuthProfile.class); + profile.setOwner(id); + GoogleMfaAuthToken token = new GoogleMfaAuthToken.Builder().issueDate(LocalDateTime.now()).token(12345).build(); + profile.add(token); + authProfileDAO.save(profile); - AuthProfile authProfile = result.get(); - result = authProfileDAO.findById(authProfile.getKey()); - assertTrue(result.isPresent()); + profile = authProfileDAO.findByOwner(id).orElseThrow(); + assertEquals(profile, authProfileDAO.findById(profile.getKey()).orElseThrow()); + assertEquals(1, profile.getGoogleMfaAuthTokens().size()); + assertEquals(token, profile.getGoogleMfaAuthTokens().getFirst()); - authProfile.setOwner("SyncopeCreate-New"); - authProfile.setGoogleMfaAuthTokens(List.of()); - authProfileDAO.save(authProfile); + profile.setOwner("SyncopeCreate-New"); + profile.getGoogleMfaAuthTokens().clear(); + profile.add(new GoogleMfaAuthToken.Builder().issueDate(LocalDateTime.now()).token(54321).build()); + authProfileDAO.save(profile); assertFalse(authProfileDAO.findByOwner(id).isPresent()); + + profile = authProfileDAO.findByOwner("SyncopeCreate-New").orElseThrow(); + assertEquals(1, profile.getGoogleMfaAuthTokens().size()); + assertEquals(54321, profile.getGoogleMfaAuthTokens().getFirst().getOtp()); } @Test @@ -103,14 +108,14 @@ public void webAuthnRegisteredDevice() { Optional result = authProfileDAO.findByOwner(id); assertTrue(result.isPresent()); - assertFalse(authProfileDAO.findAll(PageRequest.of(0, 100)).isEmpty()); + assertFalse(authProfileDAO.findAll(Pageable.unpaged()).isEmpty()); AuthProfile authProfile = result.get(); result = authProfileDAO.findById(authProfile.getKey()); assertTrue(result.isPresent()); authProfile.setOwner("newowner"); - authProfile.setWebAuthnDeviceCredentials(List.of()); + authProfile.getWebAuthnDeviceCredentials().clear(); authProfileDAO.save(authProfile); assertFalse(authProfileDAO.findByOwner(id).isPresent()); @@ -122,22 +127,18 @@ public void googleMfaAccount() { createAuthProfileWithAccount(id); - Optional result = authProfileDAO.findByOwner(id); - assertTrue(result.isPresent()); + AuthProfile authProfile = authProfileDAO.findByOwner(id).orElseThrow(); - assertFalse(authProfileDAO.findAll(PageRequest.of(0, 100)).isEmpty()); + assertFalse(authProfileDAO.findAll(Pageable.unpaged()).isEmpty()); - AuthProfile authProfile = result.get(); - result = authProfileDAO.findById(authProfile.getKey()); - assertTrue(result.isPresent()); + authProfile = authProfileDAO.findById(authProfile.getKey()).orElseThrow(); String secret = SecureRandomUtils.generateRandomUUID().toString(); - List googleMfaAuthAccounts = authProfile.getGoogleMfaAuthAccounts(); - assertFalse(googleMfaAuthAccounts.isEmpty()); - GoogleMfaAuthAccount googleMfaAuthAccount = googleMfaAuthAccounts.getFirst(); + GoogleMfaAuthAccount googleMfaAuthAccount = authProfile.getGoogleMfaAuthAccounts().getFirst(); googleMfaAuthAccount.setSecretKey(secret); - authProfile.setGoogleMfaAuthAccounts(googleMfaAuthAccounts); + authProfile.getGoogleMfaAuthAccounts().clear(); + authProfile.add(googleMfaAuthAccount); authProfile = authProfileDAO.save(authProfile); assertEquals(secret, authProfile.getGoogleMfaAuthAccounts().getFirst().getSecretKey()); } @@ -159,26 +160,19 @@ public void impersonationAccounts() { mapToObj(i -> new ImpersonationAccount.Builder().impersonated("impersonatee" + i).build()). toList(); - authProfile.setImpersonationAccounts(accounts); + authProfile.getImpersonationAccounts().clear(); + accounts.forEach(authProfile::add); authProfile = authProfileDAO.save(authProfile); assertEquals(accounts.size(), authProfile.getImpersonationAccounts().size()); } - private AuthProfile createAuthProfileWithToken(final String owner, final Integer otp) { - AuthProfile profile = entityFactory.newEntity(AuthProfile.class); - profile.setOwner(owner); - GoogleMfaAuthToken token = new GoogleMfaAuthToken.Builder().issueDate(LocalDateTime.now()).token(otp).build(); - profile.setGoogleMfaAuthTokens(List.of(token)); - return authProfileDAO.save(profile); - } - private AuthProfile createAuthProfileWithWebAuthnDevice( final String owner, final List credentials) { AuthProfile profile = entityFactory.newEntity(AuthProfile.class); profile.setOwner(owner); - profile.setWebAuthnDeviceCredentials(credentials); + credentials.forEach(profile::add); return authProfileDAO.save(profile); } @@ -193,7 +187,7 @@ private AuthProfile createAuthProfileWithAccount(final String owner) { .name(SecureRandomUtils.generateRandomUUID().toString()) .username(owner) .build(); - profile.setGoogleMfaAuthAccounts(List.of(account)); + profile.getGoogleMfaAuthAccounts().add(account); return authProfileDAO.save(profile); } } diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ConnInstanceTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ConnInstanceTest.java index d9f5414726f..2f7a226c753 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ConnInstanceTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ConnInstanceTest.java @@ -24,7 +24,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import org.apache.syncope.common.lib.SyncopeConstants; @@ -32,6 +31,7 @@ import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.syncope.common.lib.types.IdMEntitlement; import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.entity.ConnInstance; import org.apache.syncope.core.persistence.neo4j.AbstractTest; import org.apache.syncope.core.spring.security.DelegatedAdministrationException; @@ -50,6 +50,9 @@ public class ConnInstanceTest extends AbstractTest { @Autowired private ConnInstanceDAO connInstanceDAO; + @Autowired + private RealmDAO realmDAO; + @Test public void findAll() { List authorities = IdMEntitlement.values().stream(). @@ -95,6 +98,7 @@ public void save() { connInstance.setBundleName("org.apache.syncope.core.persistence.test.util"); connInstance.setDisplayName("New"); connInstance.setConnRequestTimeout(60); + connInstance.setAdminRealm(realmDAO.getRoot()); // set the connector configuration ConnConfPropSchema endpointSchema = new ConnConfPropSchema(); @@ -113,10 +117,8 @@ public void save() { servicename.setSchema(servicenameSchema); endpoint.getValues().add("Provisioning"); - List conf = new ArrayList<>(); - conf.add(endpoint); - conf.add(servicename); - connInstance.setConf(conf); + connInstance.getConf().add(endpoint); + connInstance.getConf().add(servicename); assertFalse(connInstance.getConf().isEmpty()); // perform save operation diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainSchemaTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainSchemaTest.java index ae6036fb2ff..ead9afc1c48 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainSchemaTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainSchemaTest.java @@ -189,11 +189,9 @@ public void checkForDropdownType() { @Test public void saveInvalidSchema() { - assertThrows(InvalidEntityException.class, () -> { - PlainSchema schema = entityFactory.newEntity(PlainSchema.class); - schema.setKey("username"); - plainSchemaDAO.save(schema); - }); + PlainSchema schema = entityFactory.newEntity(PlainSchema.class); + schema.setKey("username"); + assertThrows(InvalidEntityException.class, () -> plainSchemaDAO.save(schema)); } @Test @@ -210,11 +208,7 @@ public void issueSYNCOPE418() { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setKey("http://schemas.examples.org/security/authorization/organizationUnit"); - try { - plainSchemaDAO.save(schema); - fail("This should not happen"); - } catch (InvalidEntityException e) { - assertTrue(e.hasViolation(EntityViolationType.InvalidKey)); - } + InvalidEntityException iee = assertThrows(InvalidEntityException.class, () -> plainSchemaDAO.save(schema)); + assertTrue(iee.hasViolation(EntityViolationType.InvalidKey)); } } diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2IdPEntityTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2IdPEntityTest.java index 8571be7c036..1160db18908 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2IdPEntityTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2IdPEntityTest.java @@ -70,12 +70,13 @@ public void save() { public void update() { SAML2IdPEntity entity = create("SyncopeUpdate"); assertNotNull(entity); - entity.setKey("OtherSyncope"); + + entity.setMetadata("metadata2".getBytes(StandardCharsets.UTF_8)); entity = saml2IdPEntityDAO.save(entity); assertNotNull(entity); SAML2IdPEntity found = saml2IdPEntityDAO.findById(entity.getKey()).orElseThrow(); - assertEquals("OtherSyncope", found.getKey()); + assertEquals("metadata2", new String(found.getMetadata(), StandardCharsets.UTF_8)); } } diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ConnInstanceTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ConnInstanceTest.java index ac619c3589f..1c14e6c729d 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ConnInstanceTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ConnInstanceTest.java @@ -47,8 +47,7 @@ public class ConnInstanceTest extends AbstractTest { public void deleteCascade() { ConnInstance connInstance = connInstanceDAO.findById("fcf9f2b0-f7d6-42c9-84a6-61b28255a42b").orElseThrow(); - List resources = connInstance.getResources(); - assertNotNull(resources); + List resources = resourceDAO.findByConnInstance(connInstance.getKey()); assertFalse(resources.isEmpty()); connInstanceDAO.deleteById(connInstance.getKey()); @@ -65,14 +64,10 @@ public void issue176() { ConnInstance connInstance = connInstanceDAO.findById("fcf9f2b0-f7d6-42c9-84a6-61b28255a42b").orElseThrow(); assertTrue(connInstance.getCapabilities().isEmpty()); - List resources = connInstance.getResources(); - assertNotNull(resources); + List resources = resourceDAO.findByConnInstance(connInstance.getKey()); assertEquals(4, resources.size()); - assertTrue( - "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(0).getKey()) - || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(1).getKey()) - || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(2).getKey()) - || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(3).getKey())); + assertTrue(resources.stream().map(ExternalResource::getKey). + anyMatch("ws-target-resource-nopropagation"::equals)); connInstance.getCapabilities().add(ConnectorCapability.SEARCH); @@ -80,13 +75,9 @@ public void issue176() { assertNotNull(connInstance); assertFalse(connInstance.getCapabilities().isEmpty()); - resources = connInstance.getResources(); - assertNotNull(resources); + resources = resourceDAO.findByConnInstance(connInstance.getKey()); assertEquals(4, resources.size()); - assertTrue( - "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(0).getKey()) - || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(1).getKey()) - || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(2).getKey()) - || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(3).getKey())); + assertTrue(resources.stream().map(ExternalResource::getKey). + anyMatch("ws-target-resource-nopropagation"::equals)); } } diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/GroupTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/GroupTest.java index aab29f0cc2d..54d7bc420b1 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/GroupTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/GroupTest.java @@ -42,19 +42,17 @@ import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.persistence.api.entity.RelationshipType; -import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.api.entity.group.GRelationship; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.group.GroupTypeExtension; -import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; import org.apache.syncope.core.persistence.api.entity.user.UMembership; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.neo4j.AbstractTest; -import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jADynGroupMembership; -import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUDynGroupMembership; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDynGroupMembership; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Pageable; @@ -121,7 +119,7 @@ public void uMemberships() { assertEquals( memberships.stream().map(m -> m.getLeftEnd().getKey()).collect(Collectors.toSet()), - new HashSet<>(groupDAO.findUMembers("37d15e4c-cdc1-460b-a591-8505c8133806"))); + new HashSet<>(groupDAO.findUMembers("37d15e4c-cdc1-460b-a591-8505c8133806"))); assertTrue(groupDAO.existsUMembership( "74cd8ece-715a-44a4-a736-e17b46c4e7e6", "37d15e4c-cdc1-460b-a591-8505c8133806")); @@ -244,20 +242,21 @@ public void udynMembership() { group.setRealm(realmDAO.getRoot()); group.setName("new"); - UDynGroupMembership dynMembership = entityFactory.newEntity(UDynGroupMembership.class); + DynGroupMembership dynMembership = entityFactory.newEntity(DynGroupMembership.class); + dynMembership.setAnyType(anyTypeDAO.getUser()); dynMembership.setFIQLCond("cool==true"); dynMembership.setGroup(group); - group.setUDynMembership(dynMembership); + group.add(dynMembership); Group actual = groupDAO.saveAndRefreshDynMemberships(group); assertNotNull(actual); // 2. verify that dynamic membership is there actual = groupDAO.findById(actual.getKey()).orElseThrow(); - assertNotNull(actual.getUDynMembership()); - assertNotNull(actual.getUDynMembership().getKey()); - assertEquals(actual, actual.getUDynMembership().getGroup()); + assertNotNull(actual.getDynMembership(anyTypeDAO.getUser()).orElseThrow()); + assertNotNull(actual.getDynMembership(anyTypeDAO.getUser()).orElseThrow().getKey()); + assertEquals(actual, actual.getDynMembership(anyTypeDAO.getUser()).orElseThrow().getGroup()); // 3. verify that expected users have the created group dynamically assigned List members = groupDAO.findUDynMembers(actual); @@ -267,7 +266,8 @@ public void udynMembership() { user = userDAO.findByUsername("bellini").orElseThrow(); List dynGroupMemberships = userDAO.findDynGroups(user.getKey()); assertEquals(1, dynGroupMemberships.size()); - assertTrue(dynGroupMemberships.contains(actual.getUDynMembership().getGroup())); + assertTrue(dynGroupMemberships.contains( + actual.getDynMembership(anyTypeDAO.getUser()).orElseThrow().getGroup())); // 4. delete the new user and verify that dynamic membership was updated userDAO.deleteById(newUserKey); @@ -278,11 +278,11 @@ public void udynMembership() { assertEquals("c9b2dec2-00a7-4855-97c0-d854842b4b24", members.getFirst()); // 5. delete group and verify that dynamic membership was also removed - String dynMembershipKey = actual.getUDynMembership().getKey(); + String dynMembershipKey = actual.getDynMembership(anyTypeDAO.getUser()).orElseThrow().getKey(); groupDAO.delete(actual); - assertTrue(neo4jTemplate.findById(dynMembershipKey, Neo4jUDynGroupMembership.class).isEmpty()); + assertTrue(neo4jTemplate.findById(dynMembershipKey, Neo4jDynGroupMembership.class).isEmpty()); dynGroupMemberships = userDAO.findDynGroups(user.getKey()); assertTrue(dynGroupMemberships.isEmpty()); @@ -310,7 +310,7 @@ public void adynMembership() { group.setRealm(realmDAO.getRoot()); group.setName("new"); - ADynGroupMembership dynMembership = entityFactory.newEntity(ADynGroupMembership.class); + DynGroupMembership dynMembership = entityFactory.newEntity(DynGroupMembership.class); dynMembership.setAnyType(anyTypeDAO.findById("PRINTER").orElseThrow()); dynMembership.setFIQLCond("model==Canon MFC8030"); dynMembership.setGroup(group); @@ -322,9 +322,10 @@ public void adynMembership() { // 2. verify that dynamic membership is there actual = groupDAO.findById(actual.getKey()).orElseThrow(); - assertNotNull(actual.getADynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).get()); - assertNotNull(actual.getADynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).get().getKey()); - assertEquals(actual, actual.getADynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).get().getGroup()); + assertNotNull(actual.getDynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).orElseThrow()); + assertNotNull(actual.getDynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).orElseThrow().getKey()); + assertEquals(actual, actual.getDynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()). + orElseThrow().getGroup()); // 3. verify that expected any objects have the created group dynamically assigned List members = groupDAO.findADynMembers(actual).stream().filter( @@ -338,8 +339,8 @@ public void adynMembership() { anyObject = anyObjectDAO.findById("fc6dbc3a-6c07-4965-8781-921e7401a4a5").orElseThrow(); Collection dynGroupMemberships = anyObjectDAO.findDynGroups(anyObject.getKey()); assertEquals(1, dynGroupMemberships.size()); - assertTrue(dynGroupMemberships.contains(actual.getADynMembership(anyTypeDAO.findById("PRINTER"). - orElseThrow()).get().getGroup())); + assertTrue(dynGroupMemberships.contains(actual.getDynMembership(anyTypeDAO.findById("PRINTER"). + orElseThrow()).orElseThrow().getGroup())); // 4. delete the new any object and verify that dynamic membership was updated anyObjectDAO.deleteById(newAnyObjectKey); @@ -352,11 +353,12 @@ public void adynMembership() { assertEquals("fc6dbc3a-6c07-4965-8781-921e7401a4a5", members.getFirst()); // 5. delete group and verify that dynamic membership was also removed - String dynMembershipKey = actual.getADynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).get().getKey(); + String dynMembershipKey = actual.getDynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()). + orElseThrow().getKey(); groupDAO.delete(actual); - assertTrue(neo4jTemplate.findById(dynMembershipKey, Neo4jADynGroupMembership.class).isEmpty()); + assertTrue(neo4jTemplate.findById(dynMembershipKey, Neo4jDynGroupMembership.class).isEmpty()); dynGroupMemberships = anyObjectDAO.findDynGroups(anyObject.getKey()); assertTrue(dynGroupMemberships.isEmpty()); @@ -384,7 +386,7 @@ public void relationships() { group = groupDAO.findByName("root").orElseThrow(); assertEquals(1, group.getRelationships().size()); assertEquals("8559d14d-58c2-46eb-a2d4-a7d35161e8f8", - group.getRelationships().getFirst().getRightEnd().getKey()); + group.getRelationships().getFirst().getRightEnd().getKey()); } @Test diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PlainSchemaTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PlainSchemaTest.java index 141c3829ce4..766bc795a56 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PlainSchemaTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PlainSchemaTest.java @@ -25,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.fail; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.syncope.common.lib.SyncopeConstants; @@ -126,17 +127,15 @@ private List getMappingItems(final String intAttrName) { @Test public void deleteFullname() { - // fullname is mapped as ConnObjectKey for ws-target-resource-2, need to swap it otherwise validation errors + // fullname is mapped as ConnObjectKey for various resources, need to swap it otherwise validation errors // will be raised - resourceDAO.findById("ws-target-resource-2").orElseThrow(). - getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping().getItems(). - forEach(item -> { - if ("fullname".equals(item.getIntAttrName())) { - item.setConnObjectKey(false); - } else if ("surname".equals(item.getIntAttrName())) { - item.setConnObjectKey(true); - } - }); + resourceDAO.findAll().stream(). + filter(r -> r.getKey().startsWith("ws-target-resource-")). + map(r -> r.getProvisionByAnyType(AnyTypeKind.USER.name())). + flatMap(Optional::stream). + flatMap(p -> p.getMapping().getItems().stream()). + filter(item -> "fullname".equals(item.getIntAttrName())). + forEach(item -> item.setIntAttrName("surname")); // search for user schema fullname plainSchemaDAO.findById("fullname").orElseThrow(); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ResourceTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ResourceTest.java index abdb14527d3..cb21cfaec92 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ResourceTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ResourceTest.java @@ -115,7 +115,7 @@ public void findByPropagationActionsContaining() { assertEquals( Set.of(resourceDAO.findById("resource-testdb2").orElseThrow(), resourceDAO.findById("resource-ldap").orElseThrow()), - new HashSet<>(resourceDAO.findByPropagationActionsContaining(impl))); + new HashSet<>(resourceDAO.findByPropagationActionsContaining(impl))); } @Test @@ -200,7 +200,6 @@ public void save() { // check connector connector = connInstanceDAO.findById("88a7a819-dab5-46b4-9b90-0b9769eabdb8").orElseThrow(); - assertNotNull(connector.getResources()); assertNotNull(resource.getConnector()); assertTrue(resource.getConnector().equals(connector)); @@ -257,8 +256,8 @@ public void delete() { // resource must be not referenced any more from the connector ConnInstance actualConnector = connInstanceDAO.findById(connector.getKey()).orElseThrow(); - actualConnector.getResources(). - forEach(res -> assertFalse(res.getKey().equalsIgnoreCase(resource.getKey()))); + resourceDAO.findByConnInstance(actualConnector.getKey()). + forEach(r -> assertFalse(r.getKey().equalsIgnoreCase(resource.getKey()))); // there must be no tasks propagationTasks.forEach(task -> assertTrue(taskDAO.findById(task.getKey()).isEmpty())); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java index d550632ff1b..0af1f3f1989 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java @@ -153,12 +153,11 @@ public void savePropagationTask() { } @Test - public void addPropagationTaskExecution() { + public void addPropagationTaskExec() { PropagationTask task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); - assertNotNull(task); - int executionNumber = task.getExecs().size(); + int execs = task.getExecs().size(); TaskExec execution = taskUtilsFactory.getInstance(TaskType.PROPAGATION).newTaskExec(); execution.setTask(task); @@ -166,24 +165,20 @@ public void addPropagationTaskExecution() { execution.setStart(OffsetDateTime.now()); execution.setExecutor("admin"); task.add(execution); - execution.setTask(task); taskDAO.save(task); task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); - assertNotNull(task); - - assertEquals(executionNumber + 1, task.getExecs().size()); + assertEquals(execs + 1, task.getExecs().size()); } @Test - public void addPullTaskExecution() { + public void addPullTaskExec() { PullTask task = (PullTask) taskDAO.findById( TaskType.PULL, "c41b9b71-9bfa-4f90-89f2-84787def4c5c").orElseThrow(); - assertNotNull(task); - int executionNumber = task.getExecs().size(); + int execs = task.getExecs().size(); TaskExec execution = taskUtilsFactory.getInstance(TaskType.PULL).newTaskExec(); execution.setStatus("Text-free status"); @@ -192,24 +187,19 @@ public void addPullTaskExecution() { execution.setMessage("A message"); execution.setExecutor("admin"); task.add(execution); - execution.setTask(task); taskDAO.save(task); - task = (PullTask) taskDAO.findById( - TaskType.PULL, "c41b9b71-9bfa-4f90-89f2-84787def4c5c").orElseThrow(); - assertNotNull(task); - - assertEquals(executionNumber + 1, task.getExecs().size()); + task = (PullTask) taskDAO.findById(TaskType.PULL, "c41b9b71-9bfa-4f90-89f2-84787def4c5c").orElseThrow(); + assertEquals(execs + 1, task.getExecs().size()); } @Test - public void addPushTaskExecution() { + public void addPushTaskExec() { PushTask task = (PushTask) taskDAO.findById( TaskType.PUSH, "af558be4-9d2f-4359-bf85-a554e6e90be1").orElseThrow(); - assertNotNull(task); - int executionNumber = task.getExecs().size(); + int execs = task.getExecs().size(); TaskExec execution = taskUtilsFactory.getInstance(TaskType.PUSH).newTaskExec(); execution.setStatus("Text-free status"); @@ -218,14 +208,11 @@ public void addPushTaskExecution() { execution.setMessage("A message"); execution.setExecutor("admin"); task.add(execution); - execution.setTask(task); taskDAO.save(task); task = (PushTask) taskDAO.findById(TaskType.PUSH, "af558be4-9d2f-4359-bf85-a554e6e90be1").orElseThrow(); - assertNotNull(task); - - assertEquals(executionNumber + 1, task.getExecs().size()); + assertEquals(execs + 1, task.getExecs().size()); } @Test @@ -237,11 +224,11 @@ public void deleteTask() { } @Test - public void deleteTaskExecution() { + public void deleteTaskExec() { @SuppressWarnings("unchecked") TaskExec execution = (TaskExec) taskExecDAO.findById( TaskType.PROPAGATION, "e58ca1c7-178a-4012-8a71-8aa14eaf0655").orElseThrow(); - int executionNumber = execution.getTask().getExecs().size(); + int execs = execution.getTask().getExecs().size(); taskExecDAO.delete(TaskType.PROPAGATION, "e58ca1c7-178a-4012-8a71-8aa14eaf0655"); @@ -249,7 +236,7 @@ public void deleteTaskExecution() { PropagationTask task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); - assertEquals(task.getExecs().size(), executionNumber - 1); + assertEquals(task.getExecs().size(), execs - 1); } @Test diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/UserTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/UserTest.java index 78eb585fe8f..1de42470810 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/UserTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/UserTest.java @@ -18,22 +18,27 @@ */ package org.apache.syncope.core.persistence.neo4j.outer; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.OffsetDateTime; import java.util.List; import java.util.UUID; import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.DelegationDAO; import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.dao.RoleDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; @@ -83,6 +88,12 @@ public class UserTest extends AbstractTest { @Autowired private RoleDAO roleDAO; + @Autowired + private RealmDAO realmDAO; + + @Autowired + private AnyTypeClassDAO anyTypeClassDAO; + @Autowired private PlainAttrValidationManager validator; @@ -225,6 +236,29 @@ public void deleteCascadeOnDelegations() { assertTrue(delegationDAO.findById(delegation.getKey()).isEmpty()); } + @Test + public void auxClasses() { + User user = entityFactory.newEntity(User.class); + user.setRealm(realmDAO.getRoot()); + + PlainAttr attr = new PlainAttr(); + attr.setSchema("title"); + attr.add(validator, "value"); + user.add(attr); + + InvalidEntityException iee = assertThrows(InvalidEntityException.class, () -> userDAO.save(user)); + assertTrue(iee.getMessage().contains("message=title not allowed for this instance, propertyPath=plainAttrs")); + + user.add(anyTypeClassDAO.findById("minimal group").orElseThrow()); + assertEquals("minimal group", user.getAuxClasses().getFirst().getKey()); + + iee = assertThrows(InvalidEntityException.class, () -> userDAO.save(user)); + assertTrue(iee.getMessage().contains("propertyPath=username")); + + user.setUsername("username"); + assertDoesNotThrow(() -> userDAO.save(user)); + } + /** * Search by derived attribute. */ diff --git a/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml b/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml index 15b1ec50db1..d5f5ffaef00 100644 --- a/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml +++ b/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml @@ -65,26 +65,26 @@ under the License. jsonConf='{"_class":"org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf","releaseAttrs":{},"allowedAttrs":["cn","givenName","uid"],"excludedAttrs":[],"includeOnlyAttrs":[],"principalIdAttr":null,"principalAttrRepoConf":{"mergingStrategy":"MULTIVALUED","ignoreResolvedAttributes":false,"expiration":0,"timeUnit":"HOURS","attrRepos":[]}}'/> - - - - - - - - - org.apache.syncope syncope - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core diff --git a/core/provisioning-api/pom.xml b/core/provisioning-api/pom.xml index b18ca4eb238..71b8e7f4982 100644 --- a/core/provisioning-api/pom.xml +++ b/core/provisioning-api/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core Provisioning API diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/UserProvisioningManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/UserProvisioningManager.java index f3a838fe050..4c8f3bdd352 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/UserProvisioningManager.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/UserProvisioningManager.java @@ -29,6 +29,8 @@ public interface UserProvisioningManager extends ProvisioningManager { + boolean checkSecurityAnswer(String key, String value); + ProvisioningResult create( UserCR userCR, boolean disablePwdPolicyCheck, @@ -59,7 +61,7 @@ ProvisioningResult update( ProvisioningResult suspend(StatusR statusR, boolean nullPriorityAsync, String updater, String context); - void internalSuspend(String key, String updater, String context); + void suspendOnAuthFailures(String key, String updater, String context); void requestPasswordReset(String key, String updater, String context); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AnyTypeClassDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AnyTypeClassDataBinder.java index 18187be7a23..501db83bac6 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AnyTypeClassDataBinder.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AnyTypeClassDataBinder.java @@ -25,7 +25,7 @@ public interface AnyTypeClassDataBinder { AnyTypeClass create(AnyTypeClassTO anyTypeClassTO); - void update(AnyTypeClass anyTypeClass, AnyTypeClassTO anyTypeClassTO); + AnyTypeClass update(AnyTypeClass anyTypeClass, AnyTypeClassTO anyTypeClassTO); AnyTypeClassTO getAnyTypeClassTO(AnyTypeClass anyTypeClass); } diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RelationshipTypeDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RelationshipTypeDataBinder.java index 1a2d6ea3bb1..418ce12bcbc 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RelationshipTypeDataBinder.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RelationshipTypeDataBinder.java @@ -27,7 +27,7 @@ public interface RelationshipTypeDataBinder { RelationshipType create(RelationshipTypeTO relationshipTypeTO); - void update(RelationshipType relationshipType, RelationshipTypeTO relationshipTypeTO); + RelationshipType update(RelationshipType relationshipType, RelationshipTypeTO relationshipTypeTO); TypeExtensionTO getTypeExtensionTO(RelationshipTypeExtension typeExt); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlContextBuilder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlContextBuilder.java index bed2130497e..85b63d47d9a 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlContextBuilder.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlContextBuilder.java @@ -40,11 +40,9 @@ import org.apache.syncope.common.lib.to.AnyTO; import org.apache.syncope.common.lib.to.RealmTO; import org.apache.syncope.core.persistence.api.entity.Any; -import org.apache.syncope.core.persistence.api.entity.Attributable; import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.utils.FormatUtils; -import org.apache.syncope.core.provisioning.api.DerAttrHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.ReflectionUtils; @@ -185,13 +183,7 @@ public JexlContextBuilder plainAttrs(final Collection attrs) { return this; } - public JexlContextBuilder derAttrs(final Attributable attributable, final DerAttrHandler derAttrHandler) { - Map derAttrs = attributable instanceof Realm realm - ? derAttrHandler.getValues(realm) - : attributable instanceof Any any - ? derAttrHandler.getValues(any) - : Map.of(); - + public JexlContextBuilder derAttrs(final Map derAttrs) { derAttrs.forEach((schema, value) -> jexlContext.set(schema, value)); return this; diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlTools.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlTools.java index e34df5b5bae..7f622c168f7 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlTools.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlTools.java @@ -19,6 +19,7 @@ package org.apache.syncope.core.provisioning.api.jexl; import java.io.StringWriter; +import java.util.Map; import java.util.Optional; import org.apache.commons.jexl3.JexlContext; import org.apache.commons.jexl3.JexlEngine; @@ -26,10 +27,13 @@ import org.apache.commons.jexl3.JexlExpression; import org.apache.commons.jexl3.JxltEngine; import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.Attributable; +import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.provisioning.api.DerAttrHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.transaction.annotation.Transactional; /** * JEXL reference is available. @@ -101,6 +105,16 @@ public String evaluateTemplate(final String template, final JexlContext jexlCont return Optional.ofNullable(result).orElse(template); } + @Transactional(readOnly = true) + public Map derAttrs(final Attributable attributable, final DerAttrHandler derAttrHandler) { + return attributable instanceof Realm realm + ? derAttrHandler.getValues(realm) + : attributable instanceof Any any + ? derAttrHandler.getValues(any) + : Map.of(); + } + + @Transactional(readOnly = true) public boolean evaluateMandatoryCondition( final String mandatoryCondition, final Attributable attributable, @@ -108,7 +122,7 @@ public boolean evaluateMandatoryCondition( JexlContext jexlContext = new JexlContextBuilder(). plainAttrs(attributable.getPlainAttrs()). - derAttrs(attributable, derAttrHandler). + derAttrs(derAttrs(attributable, derAttrHandler)). build(); return Boolean.parseBoolean(evaluateExpression(mandatoryCondition, jexlContext).toString()); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/TemplateUtils.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/TemplateUtils.java index d49c06951fc..feb5c3de162 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/TemplateUtils.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/TemplateUtils.java @@ -275,24 +275,15 @@ public void apply(final RealmMember realmMember, final AnyTO template) { } }); - Optional.ofNullable(groupTO.getUDynMembershipCond()).ifPresent(udynMembershipCond -> { - switch (realmMember) { - case GroupTO grm -> - grm.setUDynMembershipCond(udynMembershipCond); - case GroupCR grm -> - grm.setUDynMembershipCond(udynMembershipCond); - default -> { - } - } - }); + groupTO.getDynMembershipConds().forEach((anyType, cond) -> { + if (realmMember instanceof GroupTO grm + && grm.getDynMembershipConds().containsKey(anyType)) { - groupTO.getADynMembershipConds().forEach((anyType, cond) -> { - if (realmMember instanceof GroupTO grm && grm.getADynMembershipConds().containsKey(anyType)) { - grm.getADynMembershipConds().put(anyType, cond); + grm.getDynMembershipConds().put(anyType, cond); } else if (realmMember instanceof GroupCR grm - && !grm.getADynMembershipConds().containsKey(anyType)) { + && !grm.getDynMembershipConds().containsKey(anyType)) { - grm.getADynMembershipConds().put(anyType, cond); + grm.getDynMembershipConds().put(anyType, cond); } }); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java index 36cd3c7ce6c..045e8d915fa 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java @@ -90,8 +90,6 @@ List createTasks( Object output, Object... input); - long getMaxRetries(); - /** * Set execution state of NotificationTask with provided id. * diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AbstractValueDeserializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AbstractValueDeserializer.java index 6fd69f9e86b..dd4f1b35ad6 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AbstractValueDeserializer.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AbstractValueDeserializer.java @@ -18,27 +18,26 @@ */ package org.apache.syncope.core.provisioning.api.serialization; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.io.IOException; import java.util.ArrayList; import java.util.Base64; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.identityconnectors.common.security.GuardedString; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ValueDeserializer; -public abstract class AbstractValueDeserializer extends JsonDeserializer { +public abstract class AbstractValueDeserializer extends ValueDeserializer { - protected List doDeserialize(final JsonNode value, final JsonParser jp) throws IOException { + protected List doDeserialize(final JsonNode value, final JsonParser jp) throws JacksonException { List values = new ArrayList<>(); for (JsonNode node : value) { if (node.isNull()) { values.add(null); } else if (node.isObject()) { - values.add(((ObjectNode) node).traverse(jp.getCodec()).readValueAs(GuardedString.class)); + values.add(node.traverse(jp.objectReadContext()).readValueAs(GuardedString.class)); } else if (node.isBoolean()) { values.add(node.asBoolean()); } else if (node.isDouble()) { @@ -48,7 +47,7 @@ protected List doDeserialize(final JsonNode value, final JsonParser jp) } else if (node.isInt()) { values.add(node.asInt()); } else { - String text = node.asText(); + String text = node.asString(); if (text.startsWith(AbstractValueSerializer.BYTE_ARRAY_PREFIX) && text.endsWith(AbstractValueSerializer.BYTE_ARRAY_SUFFIX)) { diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AbstractValueSerializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AbstractValueSerializer.java index 04b3784461a..335bc214454 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AbstractValueSerializer.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AbstractValueSerializer.java @@ -18,20 +18,20 @@ */ package org.apache.syncope.core.provisioning.api.serialization; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import java.io.IOException; import java.util.Base64; import java.util.List; import org.identityconnectors.common.security.GuardedString; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.ValueSerializer; -public abstract class AbstractValueSerializer extends JsonSerializer { +public abstract class AbstractValueSerializer extends ValueSerializer { public static final String BYTE_ARRAY_PREFIX = ""; public static final String BYTE_ARRAY_SUFFIX = ""; - protected void doSerialize(final List value, final JsonGenerator jgen) throws IOException { + protected void doSerialize(final List value, final JsonGenerator jgen) throws JacksonException { if (value == null) { jgen.writeNull(); } else { @@ -40,7 +40,7 @@ protected void doSerialize(final List value, final JsonGenerator jgen) t if (v == null) { jgen.writeNull(); } else if (v instanceof GuardedString) { - jgen.writeObject(v); + jgen.writePOJO(v); } else if (v instanceof final Integer i) { jgen.writeNumber(i); } else if (v instanceof final Long l) { diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeltaDeserializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeltaDeserializer.java index 3635ed22718..086a1474b27 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeltaDeserializer.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeltaDeserializer.java @@ -18,23 +18,23 @@ */ package org.apache.syncope.core.provisioning.api.serialization; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.io.IOException; import java.util.List; import org.identityconnectors.framework.common.objects.AttributeDelta; import org.identityconnectors.framework.common.objects.AttributeDeltaBuilder; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ObjectNode; @SuppressWarnings("squid:S3776") class AttributeDeltaDeserializer extends AbstractValueDeserializer { @Override - public AttributeDelta deserialize(final JsonParser jp, final DeserializationContext ctx) throws IOException { + public AttributeDelta deserialize(final JsonParser jp, final DeserializationContext ctx) throws JacksonException { ObjectNode tree = jp.readValueAsTree(); - String name = tree.get("name").asText(); + String name = tree.get("name").asString(); List valuesToAdd = doDeserialize(tree.get("valuesToAdd"), jp); List valuesToRemove = doDeserialize(tree.get("valuesToRemove"), jp); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeltaSerializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeltaSerializer.java index 5d0f5977820..bd69d040417 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeltaSerializer.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeltaSerializer.java @@ -18,28 +18,28 @@ */ package org.apache.syncope.core.provisioning.api.serialization; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import java.io.IOException; import org.identityconnectors.framework.common.objects.AttributeDelta; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.SerializationContext; class AttributeDeltaSerializer extends AbstractValueSerializer { @Override - public void serialize(final AttributeDelta source, final JsonGenerator jgen, final SerializerProvider sp) - throws IOException { + public void serialize(final AttributeDelta source, final JsonGenerator jgen, final SerializationContext ctx) + throws JacksonException { jgen.writeStartObject(); - jgen.writeStringField("name", source.getName()); + jgen.writeStringProperty("name", source.getName()); - jgen.writeFieldName("valuesToAdd"); + jgen.writeName("valuesToAdd"); doSerialize(source.getValuesToAdd(), jgen); - jgen.writeFieldName("valuesToRemove"); + jgen.writeName("valuesToRemove"); doSerialize(source.getValuesToRemove(), jgen); - jgen.writeFieldName("valuesToReplace"); + jgen.writeName("valuesToReplace"); doSerialize(source.getValuesToReplace(), jgen); jgen.writeEndObject(); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializer.java index 3d286c94b25..7cddba48cd9 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializer.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializer.java @@ -18,24 +18,24 @@ */ package org.apache.syncope.core.provisioning.api.serialization; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.io.IOException; import java.util.List; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeBuilder; import org.identityconnectors.framework.common.objects.Name; import org.identityconnectors.framework.common.objects.Uid; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.node.ObjectNode; @SuppressWarnings("squid:S3776") class AttributeDeserializer extends AbstractValueDeserializer { @Override - public Attribute deserialize(final JsonParser jp, final DeserializationContext ctx) throws IOException { + public Attribute deserialize(final JsonParser jp, final DeserializationContext ctx) throws JacksonException { ObjectNode tree = jp.readValueAsTree(); - String name = tree.get("name").asText(); + String name = tree.get("name").asString(); List values = doDeserialize(tree.get("value"), jp); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializer.java index e4ca496e1e4..08c96743533 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializer.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializer.java @@ -18,22 +18,22 @@ */ package org.apache.syncope.core.provisioning.api.serialization; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import java.io.IOException; import org.identityconnectors.framework.common.objects.Attribute; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.SerializationContext; class AttributeSerializer extends AbstractValueSerializer { @Override - public void serialize(final Attribute source, final JsonGenerator jgen, final SerializerProvider sp) - throws IOException { + public void serialize(final Attribute source, final JsonGenerator jgen, final SerializationContext ctx) + throws JacksonException { jgen.writeStartObject(); - jgen.writeStringField("name", source.getName()); + jgen.writeStringProperty("name", source.getName()); - jgen.writeFieldName("value"); + jgen.writeName("value"); doSerialize(source.getValue(), jgen); jgen.writeEndObject(); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/ConnectorObjectIdentificationDeserializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/ConnectorObjectIdentificationDeserializer.java index ef086e569b8..1f802af1ba6 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/ConnectorObjectIdentificationDeserializer.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/ConnectorObjectIdentificationDeserializer.java @@ -18,11 +18,6 @@ */ package org.apache.syncope.core.provisioning.api.serialization; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -32,33 +27,36 @@ import org.identityconnectors.framework.common.objects.Name; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.Uid; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ObjectNode; public class ConnectorObjectIdentificationDeserializer extends AbstractValueDeserializer { + @Override - public ConnectorObjectIdentification deserialize(final JsonParser jp, - final DeserializationContext deserializationContext) throws IOException { + public ConnectorObjectIdentification deserialize( + final JsonParser jp, final DeserializationContext ctx) throws JacksonException { ObjectNode tree = jp.readValueAsTree(); - String objectClass = tree.get("objectClass").asText(); + String objectClass = tree.get("objectClass").asString(); Set attributes = new HashSet<>(); JsonNode attributesNode = tree.get("attributes"); if (attributesNode != null && attributesNode.isArray()) { attributesNode.forEach(attrNode -> { - try { - String name = attrNode.get("name").asText(); - List values = doDeserialize(attrNode.get("value"), jp); - attributes.add(Uid.NAME.equals(name) - ? new Uid(values.isEmpty() || values.getFirst() == null - ? null : values.getFirst().toString()) - : Name.NAME.equals(name) - ? new Name(values.isEmpty() || values.getFirst() == null - ? null : values.getFirst().toString()) - : AttributeBuilder.build(name, values)); - } catch (IOException e) { - } + String name = attrNode.get("name").asString(); + List values = doDeserialize(attrNode.get("value"), jp); + attributes.add(Uid.NAME.equals(name) + ? new Uid(values.isEmpty() || values.getFirst() == null + ? null : values.getFirst().toString()) + : Name.NAME.equals(name) + ? new Name(values.isEmpty() || values.getFirst() == null + ? null : values.getFirst().toString()) + : AttributeBuilder.build(name, values)); }); } diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/ConnectorObjectIdentificationSerializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/ConnectorObjectIdentificationSerializer.java index 088ab5304e4..00cab86482a 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/ConnectorObjectIdentificationSerializer.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/ConnectorObjectIdentificationSerializer.java @@ -18,34 +18,34 @@ */ package org.apache.syncope.core.provisioning.api.serialization; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import java.io.IOException; import org.identityconnectors.framework.common.objects.ConnectorObjectIdentification; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.SerializationContext; public class ConnectorObjectIdentificationSerializer extends AbstractValueSerializer { @Override - public void serialize(final ConnectorObjectIdentification value, final JsonGenerator jgen, - final SerializerProvider serializerProvider) throws IOException { + public void serialize( + final ConnectorObjectIdentification value, + final JsonGenerator jgen, + final SerializationContext ctx) throws JacksonException { + jgen.writeStartObject(); - jgen.writeStringField("objectClass", value.getObjectClass().getObjectClassValue()); + jgen.writeStringProperty("objectClass", value.getObjectClass().getObjectClassValue()); - jgen.writeFieldName("attributes"); + jgen.writeName("attributes"); jgen.writeStartArray(); value.getAttributes().forEach(attr -> { - try { - jgen.writeStartObject(); + jgen.writeStartObject(); - jgen.writeStringField("name", attr.getName()); + jgen.writeStringProperty("name", attr.getName()); - jgen.writeFieldName("value"); - doSerialize(attr.getValue(), jgen); - jgen.writeEndObject(); - } catch (IOException e) { - } + jgen.writeName("value"); + doSerialize(attr.getValue(), jgen); + jgen.writeEndObject(); }); jgen.writeEndArray(); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializer.java index d69a2f46a66..91cd97c066a 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializer.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializer.java @@ -18,11 +18,6 @@ */ package org.apache.syncope.core.provisioning.api.serialization; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.io.IOException; import java.lang.reflect.Field; import java.util.Base64; import org.identityconnectors.common.security.EncryptorFactory; @@ -30,8 +25,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.ReflectionUtils; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.node.ObjectNode; -class GuardedStringDeserializer extends JsonDeserializer { +class GuardedStringDeserializer extends ValueDeserializer { private static final Logger LOG = LoggerFactory.getLogger(GuardedStringDeserializer.class); @@ -44,10 +44,10 @@ class GuardedStringDeserializer extends JsonDeserializer { private static final String BASE64_SHA1_HASH = "base64SHA1Hash"; private static final String LOG_ERROR_MESSAGE = "Could not set field value to {}"; - + @Override public GuardedString deserialize(final JsonParser jp, final DeserializationContext ctx) - throws IOException { + throws JacksonException { ObjectNode tree = jp.readValueAsTree(); @@ -61,11 +61,11 @@ public GuardedString deserialize(final JsonParser jp, final DeserializationConte } byte[] encryptedBytes = null; if (tree.has(ENCRYPTED_BYTES)) { - encryptedBytes = Base64.getDecoder().decode(tree.get(ENCRYPTED_BYTES).asText()); + encryptedBytes = Base64.getDecoder().decode(tree.get(ENCRYPTED_BYTES).asString()); } String base64SHA1Hash = null; if (tree.has(BASE64_SHA1_HASH)) { - base64SHA1Hash = tree.get(BASE64_SHA1_HASH).asText(); + base64SHA1Hash = tree.get(BASE64_SHA1_HASH).asString(); } final byte[] clearBytes = EncryptorFactory.getInstance().getDefaultEncryptor().decrypt(encryptedBytes); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializer.java index df576084935..01bf26aacb7 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializer.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializer.java @@ -18,10 +18,6 @@ */ package org.apache.syncope.core.provisioning.api.serialization; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import java.io.IOException; import java.lang.reflect.Field; import java.util.Base64; import org.identityconnectors.common.security.EncryptorFactory; @@ -30,8 +26,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.ReflectionUtils; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.ValueSerializer; -class GuardedStringSerializer extends JsonSerializer { +class GuardedStringSerializer extends ValueSerializer { private static final Logger LOG = LoggerFactory.getLogger(GuardedStringSerializer.class); @@ -46,8 +46,8 @@ class GuardedStringSerializer extends JsonSerializer { private static final String LOG_ERROR_MESSAGE = "Could not get field value"; @Override - public void serialize(final GuardedString source, final JsonGenerator jgen, final SerializerProvider sp) - throws IOException { + public void serialize(final GuardedString source, final JsonGenerator jgen, final SerializationContext ctx) + throws JacksonException { jgen.writeStartObject(); @@ -59,7 +59,7 @@ public void serialize(final GuardedString source, final JsonGenerator jgen, fina } catch (Exception e) { LOG.error(LOG_ERROR_MESSAGE, e); } - jgen.writeBooleanField(READONLY, readOnly); + jgen.writeBooleanProperty(READONLY, readOnly); boolean disposed = false; try { @@ -69,11 +69,11 @@ public void serialize(final GuardedString source, final JsonGenerator jgen, fina } catch (Exception e) { LOG.error(LOG_ERROR_MESSAGE, e); } - jgen.writeBooleanField(DISPOSED, disposed); + jgen.writeBooleanProperty(DISPOSED, disposed); byte[] encryptedBytes = EncryptorFactory.getInstance().getDefaultEncryptor().encrypt(SecurityUtil.decrypt(source).getBytes()); - jgen.writeStringField(ENCRYPTED_BYTES, Base64.getEncoder().encodeToString(encryptedBytes)); + jgen.writeStringProperty(ENCRYPTED_BYTES, Base64.getEncoder().encodeToString(encryptedBytes)); String base64SHA1Hash = null; try { @@ -84,7 +84,7 @@ public void serialize(final GuardedString source, final JsonGenerator jgen, fina LOG.error(LOG_ERROR_MESSAGE, e); } if (base64SHA1Hash != null) { - jgen.writeStringField(BASE64_SHA1_HASH, base64SHA1Hash); + jgen.writeStringProperty(BASE64_SHA1_HASH, base64SHA1Hash); } jgen.writeEndObject(); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelper.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelper.java index f152a61663e..93784c65289 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelper.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelper.java @@ -18,12 +18,6 @@ */ package org.apache.syncope.core.provisioning.api.serialization; -import com.fasterxml.jackson.core.Version; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo; import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.common.objects.Attribute; @@ -32,6 +26,11 @@ import org.identityconnectors.framework.common.objects.SyncToken; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.core.Version; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; /** * Helper class for serialization and deserialization of configuration objects (POJOs) in JSON. @@ -54,13 +53,14 @@ public final class POJOHelper { pojoModule.addDeserializer(Attribute.class, new AttributeDeserializer()); pojoModule.addDeserializer(AttributeDelta.class, new AttributeDeltaDeserializer()); pojoModule.addDeserializer(SyncToken.class, new SyncTokenDeserializer()); - pojoModule.addDeserializer(ConnectorObjectIdentification.class, + pojoModule.addDeserializer( + ConnectorObjectIdentification.class, new ConnectorObjectIdentificationDeserializer()); MAPPER = JsonMapper.builder(). + findAndAddModules(). addModule(pojoModule). - addModule(new JavaTimeModule()). - disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS). + enable(MapperFeature.USE_GETTERS_AS_SETTERS). build(); } @@ -112,18 +112,6 @@ public static T deserialize(final String serialized, final Ty return result; } - public static T convertValue(final Object value, final Class reference) { - T result = null; - - try { - result = MAPPER.convertValue(value, reference); - } catch (Exception e) { - LOG.error("During conversion", e); - } - - return result; - } - private POJOHelper() { } } diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/PropagationTaskInfoSerializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/PropagationTaskInfoSerializer.java index 215a4546fbb..259fcb7490f 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/PropagationTaskInfoSerializer.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/PropagationTaskInfoSerializer.java @@ -18,43 +18,48 @@ */ package org.apache.syncope.core.provisioning.api.serialization; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import java.io.IOException; import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.ValueSerializer; -class PropagationTaskInfoSerializer extends JsonSerializer { +class PropagationTaskInfoSerializer extends ValueSerializer { @Override - public void serialize(final PropagationTaskInfo propagationTaskInfo, final JsonGenerator jgen, - final SerializerProvider serializerProvider) throws IOException { + public void serialize( + final PropagationTaskInfo propagationTaskInfo, + final JsonGenerator jgen, + final SerializationContext ctx) throws JacksonException { jgen.writeStartObject(); - jgen.writeStringField("key", propagationTaskInfo.getKey()); + jgen.writeStringProperty("key", propagationTaskInfo.getKey()); if (propagationTaskInfo.getResource() != null) { - jgen.writeStringField("resource", propagationTaskInfo.getResource().getKey()); + jgen.writeStringProperty("resource", propagationTaskInfo.getResource().getKey()); } if (propagationTaskInfo.getOperation() != null) { - jgen.writeStringField("operation", propagationTaskInfo.getOperation().name()); + jgen.writeStringProperty("operation", propagationTaskInfo.getOperation().name()); } if (propagationTaskInfo.getObjectClass() != null) { - jgen.writeStringField("objectClass", propagationTaskInfo.getObjectClass().getObjectClassValue()); + jgen.writeStringProperty("objectClass", propagationTaskInfo.getObjectClass().getObjectClassValue()); } if (propagationTaskInfo.getAnyTypeKind() != null) { - jgen.writeStringField("anyTypeKind", propagationTaskInfo.getAnyTypeKind().name()); + jgen.writeStringProperty("anyTypeKind", propagationTaskInfo.getAnyTypeKind().name()); } - jgen.writeStringField("anyType", propagationTaskInfo.getAnyType()); - jgen.writeStringField("entityKey", propagationTaskInfo.getEntityKey()); - jgen.writeStringField("connObjectKey", propagationTaskInfo.getConnObjectKey()); - jgen.writeStringField("oldConnObjectKey", propagationTaskInfo.getOldConnObjectKey()); - jgen.writeObjectField("propagationData", propagationTaskInfo.getPropagationData()); + jgen.writeStringProperty("anyType", propagationTaskInfo.getAnyType()); + jgen.writeStringProperty("entityKey", propagationTaskInfo.getEntityKey()); + jgen.writeStringProperty("connObjectKey", propagationTaskInfo.getConnObjectKey()); + jgen.writeStringProperty("oldConnObjectKey", propagationTaskInfo.getOldConnObjectKey()); + jgen.writeName("propagationData"); + jgen.writePOJO(propagationTaskInfo.getPropagationData()); if (propagationTaskInfo.getConnector() != null) { - jgen.writeObjectField("connector", propagationTaskInfo.getConnector().getConnInstance().getKey()); + jgen.writeName("connector"); + jgen.writePOJO(propagationTaskInfo.getConnector().getConnInstance().getKey()); } - jgen.writeObjectField("beforeObj", propagationTaskInfo.getBeforeObj()); + jgen.writeName("beforeObj"); + jgen.writePOJO(propagationTaskInfo.getBeforeObj()); jgen.writeEndObject(); } diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializer.java index b6f988487eb..c6f5380cdf0 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializer.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializer.java @@ -18,20 +18,20 @@ */ package org.apache.syncope.core.provisioning.api.serialization; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.io.IOException; import java.util.Base64; import java.util.Objects; import org.identityconnectors.framework.common.objects.SyncToken; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.node.ObjectNode; -class SyncTokenDeserializer extends JsonDeserializer { +class SyncTokenDeserializer extends ValueDeserializer { @Override - public SyncToken deserialize(final JsonParser jp, final DeserializationContext ctx) throws IOException { + public SyncToken deserialize(final JsonParser jp, final DeserializationContext ctx) throws JacksonException { ObjectNode tree = jp.readValueAsTree(); Object value = tree.has("value") @@ -44,21 +44,21 @@ public SyncToken deserialize(final JsonParser jp, final DeserializationContext c } private Object deserialize(final JsonNode value, final JsonNode type) { - if (Boolean.class.getSimpleName().equals(type.asText())) { + if (Boolean.class.getSimpleName().equals(type.asString())) { return value.asBoolean(); } - if (Double.class.getSimpleName().equals(type.asText())) { + if (Double.class.getSimpleName().equals(type.asString())) { return value.asDouble(); } - if (Long.class.getSimpleName().equals(type.asText())) { + if (Long.class.getSimpleName().equals(type.asString())) { return value.asLong(); } - if (Integer.class.getSimpleName().equals(type.asText())) { + if (Integer.class.getSimpleName().equals(type.asString())) { return value.asInt(); } - return value.asText(); + return value.asString(); } private Object deserialize(final JsonNode value) { @@ -79,9 +79,9 @@ private Object deserialize(final JsonNode value) { } try { - return Base64.getDecoder().decode(value.asText()); + return Base64.getDecoder().decode(value.asString()); } catch (RuntimeException e) { - return value.asText(); + return value.asString(); } } } diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializer.java index 01a51ad8006..cee4e30e301 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializer.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializer.java @@ -18,43 +18,42 @@ */ package org.apache.syncope.core.provisioning.api.serialization; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import java.io.IOException; import java.util.Base64; import org.identityconnectors.framework.common.objects.SyncToken; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.ValueSerializer; -class SyncTokenSerializer extends JsonSerializer { +class SyncTokenSerializer extends ValueSerializer { @Override - public void serialize(final SyncToken source, final JsonGenerator jgen, final SerializerProvider sp) - throws IOException { + public void serialize(final SyncToken source, final JsonGenerator jgen, final SerializationContext ctx) + throws JacksonException { jgen.writeStartObject(); if (source.getValue() == null) { - jgen.writeNullField("value"); + jgen.writeNullProperty("value"); } else if (source.getValue() instanceof final Boolean b) { - jgen.writeStringField("type", Boolean.class.getSimpleName()); - jgen.writeBooleanField("value", b); + jgen.writeStringProperty("type", Boolean.class.getSimpleName()); + jgen.writeBooleanProperty("value", b); } else if (source.getValue() instanceof final Double v) { - jgen.writeStringField("type", Double.class.getSimpleName()); - jgen.writeNumberField("value", v); + jgen.writeStringProperty("type", Double.class.getSimpleName()); + jgen.writeNumberProperty("value", v); } else if (source.getValue() instanceof final Long l) { - jgen.writeStringField("type", Long.class.getSimpleName()); - jgen.writeNumberField("value", l); + jgen.writeStringProperty("type", Long.class.getSimpleName()); + jgen.writeNumberProperty("value", l); } else if (source.getValue() instanceof final Integer i) { - jgen.writeStringField("type", Integer.class.getSimpleName()); - jgen.writeNumberField("value", i); + jgen.writeStringProperty("type", Integer.class.getSimpleName()); + jgen.writeNumberProperty("value", i); } else if (source.getValue() instanceof final byte[] bytes) { - jgen.writeStringField("value", Base64.getEncoder().encodeToString(bytes)); + jgen.writeStringProperty("value", Base64.getEncoder().encodeToString(bytes)); } else { - jgen.writeStringField("type", String.class.getSimpleName()); - jgen.writeStringField("value", source.getValue().toString()); + jgen.writeStringProperty("type", String.class.getSimpleName()); + jgen.writeStringProperty("value", source.getValue().toString()); } jgen.writeEndObject(); } - } diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java index 39cae5985f8..a31c662ae6d 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java @@ -40,7 +40,6 @@ import org.apache.syncope.common.lib.to.AnyTO; import org.apache.syncope.common.lib.to.RealmTO; import org.apache.syncope.core.persistence.api.entity.Any; -import org.apache.syncope.core.persistence.api.entity.DerSchema; import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.provisioning.api.AbstractTest; @@ -142,8 +141,7 @@ void builderPlainAttrs(final @Mock Collection attrs) { @Test public void addDerAttrsToContext( final @Mock DerAttrHandler derAttrHandler, - final @Mock Any any, - final @Mock DerSchema derSchema) { + final @Mock Any any) { String expression = null; @@ -155,7 +153,7 @@ public void addDerAttrsToContext( JexlContextBuilder builder = new JexlContextBuilder(); ReflectionTestUtils.setField(builder, "jexlContext", context); - builder.derAttrs(any, derAttrHandler); + builder.derAttrs(derAttrHandler.getValues(any)); verify(context).set("derSchema", expression); } @@ -163,7 +161,6 @@ public void addDerAttrsToContext( void evaluateMandatoryCondition( final @Mock DerAttrHandler derAttrHandler, final @Mock Any any, - final @Mock DerSchema derSchema, final @Mock Collection plainAttrs) { String expression = null; diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java index 8f1f7030b3c..fc57d49e614 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java @@ -21,10 +21,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.IOException; import java.util.Collections; import java.util.List; @@ -34,6 +30,10 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ObjectNode; public class AttributeDeserializerTest extends AbstractTest { @@ -67,7 +67,7 @@ public void initTest() throws IOException { @Test public void deserializeIsNull() throws IOException { - when(node2.asText()).thenReturn(name); + when(node2.asString()).thenReturn(name); when(node.isNull()).thenReturn(Boolean.TRUE); Attribute attr = deserializer.deserialize(jp, ct); assertEquals(name, attr.getName()); @@ -76,7 +76,7 @@ public void deserializeIsNull() throws IOException { @Test public void deserializeIsBoolean() throws IOException { - when(node2.asText()).thenReturn(name); + when(node2.asString()).thenReturn(name); when(node.isBoolean()).thenReturn(Boolean.TRUE); when(node.asBoolean()).thenReturn(Boolean.TRUE); Attribute attr = deserializer.deserialize(jp, ct); @@ -88,7 +88,7 @@ public void deserializeIsBoolean() throws IOException { public void deserializeIsDouble() throws IOException { Double number = 9000.1; name = "__TEST__"; - when(node2.asText()).thenReturn(name); + when(node2.asString()).thenReturn(name); when(node.isDouble()).thenReturn(Boolean.TRUE); when(node.asDouble()).thenReturn(number); Attribute attr = deserializer.deserialize(jp, ct); @@ -100,7 +100,7 @@ public void deserializeIsDouble() throws IOException { public void deserializeIsLong() throws IOException { Long number = 9000L; name = "__UID__"; - when(node2.asText()).thenReturn(name); + when(node2.asString()).thenReturn(name); when(node.isLong()).thenReturn(Boolean.TRUE); when(node.asLong()).thenReturn(number); Attribute attr = deserializer.deserialize(jp, ct); @@ -111,7 +111,7 @@ public void deserializeIsLong() throws IOException { @Test public void deserializeIsInt() throws IOException { Integer number = 9000; - when(node2.asText()).thenReturn(name); + when(node2.asString()).thenReturn(name); when(node.isInt()).thenReturn(Boolean.TRUE); when(node.asInt()).thenReturn(number); Attribute attr = deserializer.deserialize(jp, ct); @@ -122,8 +122,8 @@ public void deserializeIsInt() throws IOException { @Test public void deserializeIsText() throws IOException { String text = "test"; - when(node2.asText()).thenReturn(name); - when(node.asText()).thenReturn(text); + when(node2.asString()).thenReturn(name); + when(node.asString()).thenReturn(text); Attribute attr = deserializer.deserialize(jp, ct); assertEquals(attr.getName(), name); assertEquals(List.of(text).getFirst(), attr.getValue().getFirst()); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java index 954bcac14cf..6d90de8d5bf 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java @@ -28,15 +28,14 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import java.io.IOException; import java.util.List; import org.apache.syncope.core.provisioning.api.AbstractTest; import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.common.objects.Attribute; import org.junit.jupiter.api.Test; import org.mockito.Mock; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.SerializationContext; public class AttributeSerializerTest extends AbstractTest { @@ -44,42 +43,41 @@ public class AttributeSerializerTest extends AbstractTest { public void serialize( final @Mock Attribute source, final @Mock JsonGenerator jgen, - final @Mock SerializerProvider sp) - throws IOException { + final @Mock SerializationContext ctx) { AttributeSerializer serializer = new AttributeSerializer(); when(source.getValue()).thenReturn(null); - serializer.serialize(source, jgen, sp); + serializer.serialize(source, jgen, ctx); verify(jgen).writeStartObject(); - verify(jgen).writeFieldName("value"); + verify(jgen).writeName("value"); verify(jgen).writeNull(); when(source.getValue()).thenAnswer(ic -> List.of(new GuardedString())); - serializer.serialize(source, jgen, sp); - verify(jgen).writeObject(any(GuardedString.class)); + serializer.serialize(source, jgen, ctx); + verify(jgen).writePOJO(any(GuardedString.class)); when(source.getValue()).thenAnswer(ic -> List.of(9000)); - serializer.serialize(source, jgen, sp); + serializer.serialize(source, jgen, ctx); verify(jgen).writeNumber(anyInt()); when(source.getValue()).thenAnswer(ic -> List.of(9000L)); - serializer.serialize(source, jgen, sp); + serializer.serialize(source, jgen, ctx); verify(jgen).writeNumber(anyLong()); when(source.getValue()).thenAnswer(ic -> List.of(9000.1)); - serializer.serialize(source, jgen, sp); + serializer.serialize(source, jgen, ctx); verify(jgen).writeNumber(anyDouble()); when(source.getValue()).thenAnswer(ic -> List.of(Boolean.TRUE)); - serializer.serialize(source, jgen, sp); + serializer.serialize(source, jgen, ctx); verify(jgen).writeBoolean(anyBoolean()); when(source.getValue()).thenAnswer(ic -> List.of(new byte[] { 9, 0, 0, 0 })); - serializer.serialize(source, jgen, sp); + serializer.serialize(source, jgen, ctx); verify(jgen).writeString(anyString()); when(source.getValue()).thenAnswer(ic -> List.of("test")); - serializer.serialize(source, jgen, sp); + serializer.serialize(source, jgen, ctx); verify(jgen).writeString(eq("test")); } } diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/ConnObjectIdentificationSerializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/ConnObjectIdentificationSerializerTest.java index fe4608bc930..ea06377d354 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/ConnObjectIdentificationSerializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/ConnObjectIdentificationSerializerTest.java @@ -22,9 +22,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import java.io.IOException; import java.util.Set; import org.apache.syncope.core.provisioning.api.AbstractTest; import org.identityconnectors.framework.common.objects.AttributeBuilder; @@ -33,23 +30,26 @@ import org.identityconnectors.framework.common.objects.Uid; import org.junit.jupiter.api.Test; import org.mockito.Mock; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.SerializationContext; public class ConnObjectIdentificationSerializerTest extends AbstractTest { @Test public void serialize( final @Mock JsonGenerator jgen, - final @Mock SerializerProvider sp) - throws IOException { - ConnectorObjectIdentification source = new ConnectorObjectIdentification(ObjectClass.ACCOUNT, + final @Mock SerializationContext ctx) { + + ConnectorObjectIdentification source = new ConnectorObjectIdentification( + ObjectClass.ACCOUNT, Set.of(AttributeBuilder.build(Uid.NAME, "someuid"))); - - new ConnectorObjectIdentificationSerializer().serialize(source, jgen, sp); + + new ConnectorObjectIdentificationSerializer().serialize(source, jgen, ctx); verify(jgen, times(2)).writeStartObject(); - verify(jgen).writeStringField(eq("objectClass"), eq(ObjectClass.ACCOUNT.getObjectClassValue())); - verify(jgen).writeFieldName("attributes"); - verify(jgen).writeFieldName("value"); - verify(jgen).writeStringField(eq("name"), eq(Uid.NAME)); + verify(jgen).writeStringProperty(eq("objectClass"), eq(ObjectClass.ACCOUNT.getObjectClassValue())); + verify(jgen).writeName("attributes"); + verify(jgen).writeName("value"); + verify(jgen).writeStringProperty(eq("name"), eq(Uid.NAME)); verify(jgen, times(2)).writeStartArray(); verify(jgen).writeString("someuid"); verify(jgen, times(2)).writeEndArray(); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializerTest.java index 8004ca3bbe0..a217a7e766a 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializerTest.java @@ -22,12 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.when; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.io.IOException; import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -38,6 +32,11 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.springframework.test.util.ReflectionTestUtils; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.ObjectNode; public class GuardedStringDeserializerTest extends AbstractTest { @@ -61,7 +60,7 @@ public class GuardedStringDeserializerTest extends AbstractTest { private JsonNode node; @Test - public void deserialize() throws IOException { + public void deserialize() { Map kids = new HashMap<>(); kids.put(READONLY, node); kids.put(DISPOSED, node); @@ -73,12 +72,12 @@ public void deserialize() throws IOException { String encryptedString = Base64.getEncoder().encodeToString(encryptedBytes); when(jp.readValueAsTree()).thenReturn(tree); - when(node.asText()).thenReturn(encryptedString); + when(node.asString()).thenReturn(encryptedString); assertEquals(Boolean.FALSE, ReflectionTestUtils.getField(deserializer.deserialize(jp, ctx), READONLY)); kids.remove(READONLY); assertEquals(Boolean.FALSE, ReflectionTestUtils.getField(deserializer.deserialize(jp, ctx), DISPOSED)); kids.remove(DISPOSED); - assertEquals(encryptedString, + assertEquals(encryptedString, ReflectionTestUtils.getField(deserializer.deserialize(jp, ctx), BASE64_SHA1_HASH)); kids.remove(BASE64_SHA1_HASH); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializerTest.java index e4e8056f3f0..477fbe4b67e 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializerTest.java @@ -22,13 +22,13 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; import org.apache.syncope.core.provisioning.api.AbstractTest; import org.identityconnectors.common.security.GuardedString; import org.junit.jupiter.api.Test; import org.mockito.Mock; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.SerializationContext; public class GuardedStringSerializerTest extends AbstractTest { @@ -45,13 +45,13 @@ public class GuardedStringSerializerTest extends AbstractTest { @Test public void serialize( final @Mock JsonGenerator jgen, - final @Mock SerializerProvider sp) throws IOException { + final @Mock SerializationContext ctx) throws IOException { - serializer.serialize(new GuardedString(), jgen, sp); - verify(jgen).writeBooleanField(READONLY, false); - verify(jgen).writeBooleanField(DISPOSED, false); - verify(jgen).writeStringField(eq(ENCRYPTED_BYTES), anyString()); - verify(jgen).writeStringField(eq(BASE64_SHA1_HASH), anyString()); + serializer.serialize(new GuardedString(), jgen, ctx); + verify(jgen).writeBooleanProperty(READONLY, false); + verify(jgen).writeBooleanProperty(DISPOSED, false); + verify(jgen).writeStringProperty(eq(ENCRYPTED_BYTES), anyString()); + verify(jgen).writeStringProperty(eq(BASE64_SHA1_HASH), anyString()); verify(jgen).writeEndObject(); } } diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java index 484d5ee1d23..d6827baa9c0 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java @@ -21,7 +21,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -import com.fasterxml.jackson.core.type.TypeReference; import java.util.List; import java.util.Set; import org.apache.syncope.core.persistence.api.entity.task.PropagationData; @@ -30,6 +29,7 @@ import org.identityconnectors.framework.common.objects.AttributeDeltaBuilder; import org.junit.jupiter.api.Test; import org.mockito.Mock; +import tools.jackson.core.type.TypeReference; public class POJOHelperTest extends AbstractTest { diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/PropagationTaskInfoSerializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/PropagationTaskInfoSerializerTest.java index e77762cb04d..e04edbda596 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/PropagationTaskInfoSerializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/PropagationTaskInfoSerializerTest.java @@ -22,9 +22,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import java.io.IOException; import java.util.UUID; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.ResourceOperation; @@ -37,12 +34,15 @@ import org.identityconnectors.framework.common.objects.ObjectClass; import org.junit.jupiter.api.Test; import org.mockito.Mock; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.SerializationContext; public class PropagationTaskInfoSerializerTest extends AbstractTest { @Test - public void propagationTaskInfoSerializer(final @Mock JsonGenerator jgen, final @Mock SerializerProvider sp) - throws IOException { + public void propagationTaskInfoSerializer( + final @Mock JsonGenerator jgen, + final @Mock SerializationContext ctx) { PropagationTaskInfoSerializer serializer = new PropagationTaskInfoSerializer(); ExternalResource resource = mock(ExternalResource.class); @@ -63,15 +63,17 @@ public void propagationTaskInfoSerializer(final @Mock JsonGenerator jgen, final when(connInstance.getKey()).thenReturn(UUID.randomUUID().toString()); when(connector.getConnInstance()).thenReturn(connInstance); source.setConnector(connector); - serializer.serialize(source, jgen, sp); + serializer.serialize(source, jgen, ctx); verify(jgen).writeStartObject(); - verify(jgen).writeStringField("key", source.getKey()); - verify(jgen).writeStringField("anyType", source.getAnyType()); - verify(jgen).writeStringField("entityKey", source.getEntityKey()); - verify(jgen).writeStringField("connObjectKey", source.getConnObjectKey()); - verify(jgen).writeStringField("oldConnObjectKey", source.getOldConnObjectKey()); - verify(jgen).writeObjectField("propagationData", source.getPropagationData()); - verify(jgen).writeObjectField("connector", source.getConnector().getConnInstance().getKey()); + verify(jgen).writeStringProperty("key", source.getKey()); + verify(jgen).writeStringProperty("anyType", source.getAnyType()); + verify(jgen).writeStringProperty("entityKey", source.getEntityKey()); + verify(jgen).writeStringProperty("connObjectKey", source.getConnObjectKey()); + verify(jgen).writeStringProperty("oldConnObjectKey", source.getOldConnObjectKey()); + verify(jgen).writeName("propagationData"); + verify(jgen).writePOJO(source.getPropagationData()); + verify(jgen).writeName("connector"); + verify(jgen).writePOJO(source.getConnector().getConnInstance().getKey()); verify(jgen).writeEndObject(); } } diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializerTest.java index 59ff396774c..7c79abf70c8 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializerTest.java @@ -22,10 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.when; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Base64; @@ -34,6 +30,10 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ObjectNode; public class SyncTokenDeserializerTest extends AbstractTest { @@ -93,11 +93,11 @@ public void deserializeIsInt() throws IOException { @Test public void deserializeIsString() throws IOException { String value = "testValue"; - when(node.asText()).thenReturn(value); + when(node.asString()).thenReturn(value); assertEquals(value, deserializer.deserialize(jp, ct).getValue()); value = Base64.getEncoder().encodeToString(value.getBytes(StandardCharsets.ISO_8859_1)); - when(node.asText()).thenReturn(value); + when(node.asString()).thenReturn(value); assertTrue(EqualsBuilder.reflectionEquals(Base64.getDecoder().decode(value), deserializer.deserialize(jp, ct).getValue())); } diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializerTest.java index 5219fd9273f..1085494c661 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializerTest.java @@ -20,8 +20,6 @@ import static org.mockito.Mockito.verify; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; import java.util.Base64; import java.util.UUID; @@ -29,49 +27,51 @@ import org.identityconnectors.framework.common.objects.SyncToken; import org.junit.jupiter.api.Test; import org.mockito.Mock; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.SerializationContext; public class SyncTokenSerializerTest extends AbstractTest { @Test public void syncTokenSerializer( final @Mock JsonGenerator jgen, - final @Mock SerializerProvider sp) throws IOException { + final @Mock SerializationContext ctx) throws IOException { SyncTokenSerializer serializer = new SyncTokenSerializer(); SyncToken source = new SyncToken(UUID.randomUUID().toString()); - serializer.serialize(source, jgen, sp); + serializer.serialize(source, jgen, ctx); verify(jgen).writeStartObject(); - verify(jgen).writeStringField("type", "String"); - verify(jgen).writeStringField("value", source.getValue().toString()); + verify(jgen).writeStringProperty("type", "String"); + verify(jgen).writeStringProperty("value", source.getValue().toString()); verify(jgen).writeEndObject(); boolean bool = false; source = new SyncToken(bool); - serializer.serialize(source, jgen, sp); - verify(jgen).writeStringField("type", "Boolean"); - verify(jgen).writeBooleanField("value", false); + serializer.serialize(source, jgen, ctx); + verify(jgen).writeStringProperty("type", "Boolean"); + verify(jgen).writeBooleanProperty("value", false); double doubleNum = 9000.1; source = new SyncToken(doubleNum); - serializer.serialize(source, jgen, sp); - verify(jgen).writeStringField("type", "Double"); - verify(jgen).writeNumberField("value", doubleNum); + serializer.serialize(source, jgen, ctx); + verify(jgen).writeStringProperty("type", "Double"); + verify(jgen).writeNumberProperty("value", doubleNum); long longNum = 9001; source = new SyncToken(longNum); - serializer.serialize(source, jgen, sp); - verify(jgen).writeStringField("type", "Long"); - verify(jgen).writeNumberField("value", longNum); + serializer.serialize(source, jgen, ctx); + verify(jgen).writeStringProperty("type", "Long"); + verify(jgen).writeNumberProperty("value", longNum); int intNum = 9000; source = new SyncToken(intNum); - serializer.serialize(source, jgen, sp); - verify(jgen).writeStringField("type", "Integer"); - verify(jgen).writeNumberField("value", intNum); + serializer.serialize(source, jgen, ctx); + verify(jgen).writeStringProperty("type", "Integer"); + verify(jgen).writeNumberProperty("value", intNum); byte[] bytes = { 9, 0, 0, 1 }; source = new SyncToken(bytes); - serializer.serialize(source, jgen, sp); - verify(jgen).writeStringField("value", Base64.getEncoder().encodeToString(bytes)); + serializer.serialize(source, jgen, ctx); + verify(jgen).writeStringProperty("value", Base64.getEncoder().encodeToString(bytes)); } } diff --git a/core/provisioning-java/pom.xml b/core/provisioning-java/pom.xml index 7a801a9735c..5afe53e67f6 100644 --- a/core/provisioning-java/pom.xml +++ b/core/provisioning-java/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core Provisioning Java @@ -49,18 +49,13 @@ under the License. spring-boot-starter-mail - - org.springframework.retry - spring-retry - - org.bitbucket.b_c jose4j - com.fasterxml.jackson.dataformat + tools.jackson.dataformat jackson-dataformat-csv @@ -113,6 +108,11 @@ under the License. embedded-postgres test + + org.slf4j + jcl-over-slf4j + test + org.slf4j slf4j-simple @@ -150,6 +150,7 @@ under the License. ${project.build.directory}/test-classes file:${bundles.directory}/ true + slf4j diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java index 082ff528255..294b7fe1e9f 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java @@ -506,7 +506,7 @@ private static Object getPropertyValue(final String propType, final List valu Object value = null; try { - Class propertySchemaClass = ClassUtils.forName(propType, ClassUtils.getDefaultClassLoader()); + Class propertySchemaClass = ClassUtils.forName(propType, ConnectorFacadeProxy.class.getClassLoader()); if (GuardedString.class.equals(propertySchemaClass)) { value = new GuardedString(values.getFirst().toString().toCharArray()); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java index ea15d07dccd..e012a33e9c2 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java @@ -121,7 +121,7 @@ public ConnInstance buildConnInstanceOverride( override.setBundleName(connInstance.getBundleName()); override.setVersion(connInstance.getVersion()); override.setLocation(connInstance.getLocation()); - override.setConf(connInstance.getConf()); + override.getConf().addAll(connInstance.getConf()); override.getCapabilities().addAll(connInstance.getCapabilities()); override.setConnRequestTimeout(connInstance.getConnRequestTimeout()); @@ -147,7 +147,8 @@ public ConnInstance buildConnInstanceOverride( // add override properties not substituted conf.addAll(overridable.values()); - override.setConf(conf); + override.getConf().clear(); + override.getConf().addAll(conf); // replace capabilities capabilitiesOverride.ifPresent(capabilities -> { diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java index 015c3657813..02d851282f6 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java @@ -24,8 +24,8 @@ import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.jexl3.JexlContext; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.entity.Any; -import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.Attributable; import org.apache.syncope.core.persistence.api.entity.DerSchema; import org.apache.syncope.core.persistence.api.entity.Groupable; @@ -45,12 +45,12 @@ public class DefaultDerAttrHandler implements DerAttrHandler { protected static final Logger LOG = LoggerFactory.getLogger(DerAttrHandler.class); - protected final AnyUtilsFactory anyUtilsFactory; + protected final AnyChecker anyChecker; protected final JexlTools jexlTools; - public DefaultDerAttrHandler(final AnyUtilsFactory anyUtilsFactory, final JexlTools jexlTools) { - this.anyUtilsFactory = anyUtilsFactory; + public DefaultDerAttrHandler(final AnyChecker anyChecker, final JexlTools jexlTools) { + this.anyChecker = anyChecker; this.jexlTools = jexlTools; } @@ -94,12 +94,12 @@ public String getValue(final Realm realm, final DerSchema schema) { public Map getValues(final Any any) { return getValues( any, - anyUtilsFactory.getInstance(any).dao().findAllowedSchemas(any, DerSchema.class).self()); + anyChecker.findAllowedSchemas(any, DerSchema.class).self()); } @Override public String getValue(final Any any, final DerSchema schema) { - if (!anyUtilsFactory.getInstance(any).dao().findAllowedSchemas(any, DerSchema.class).selfContains(schema)) { + if (!anyChecker.findAllowedSchemas(any, DerSchema.class).selfContains(schema)) { LOG.debug("{} not allowed for {}", schema, any); return null; } @@ -128,14 +128,14 @@ protected Map getValues( @Override public Map getValues(final Groupable groupable, final Membership membership) { - Set schemas = anyUtilsFactory.getInstance(groupable).dao(). + Set schemas = anyChecker. findAllowedSchemas(groupable, DerSchema.class).membership(membership.getRightEnd()); return getValues(groupable, membership, schemas); } @Override public String getValue(final Groupable groupable, final Membership membership, final DerSchema schema) { - if (!anyUtilsFactory.getInstance(groupable).dao(). + if (!anyChecker. findAllowedSchemas(groupable, DerSchema.class). membership(membership.getRightEnd()).contains(schema)) { @@ -167,7 +167,7 @@ protected Map getValues( @Override public Map getValues(final Relatable relatable, final Relationship relationship) { - Set schemas = anyUtilsFactory.getInstance(relatable).dao(). + Set schemas = anyChecker. findAllowedSchemas(relatable, DerSchema.class).relationshipType(relationship.getType()); return getValues(relatable, relationship, schemas); } @@ -178,7 +178,7 @@ public String getValue( final Relationship relationship, final DerSchema schema) { - if (!anyUtilsFactory.getInstance(relatable).dao(). + if (!anyChecker. findAllowedSchemas(relatable, DerSchema.class). relationshipTypesContains(relationship.getType(), schema)) { diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java index c303c634b63..93ac9be453f 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java @@ -242,7 +242,7 @@ protected Name evaluateNAME(final Any any, final Provision provision, final Stri JexlContext jexlContext = new JexlContextBuilder(). fields(any). plainAttrs(any.getPlainAttrs()). - derAttrs(any, derAttrHandler). + derAttrs(derAttrHandler.getValues(any)). build(); evalConnObjectLink = jexlTools.evaluateExpression(connObjectLink, jexlContext).toString(); @@ -274,7 +274,7 @@ protected Name evaluateNAME(final Realm realm, final OrgUnit orgUnit, final Stri JexlContext jexlContext = new JexlContextBuilder(). fields(realm). plainAttrs(realm.getPlainAttrs()). - derAttrs(realm, derAttrHandler). + derAttrs(derAttrHandler.getValues(realm)). build(); evalConnObjectLink = jexlTools.evaluateExpression(connObjectLink, jexlContext).toString(); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java index 7b2dacb4947..9bb0ac75de9 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java @@ -36,7 +36,10 @@ import org.apache.syncope.common.lib.types.PatchOperation; import org.apache.syncope.common.lib.types.ResourceOperation; import org.apache.syncope.common.lib.types.StatusRType; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.provisioning.api.PropagationByResource; import org.apache.syncope.core.provisioning.api.UserProvisioningManager; import org.apache.syncope.core.provisioning.api.UserWorkflowResult; @@ -62,16 +65,28 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager { protected final UserDAO userDAO; + protected final EncryptorManager encryptorManager; + public DefaultUserProvisioningManager( final UserWorkflowAdapter uwfAdapter, final PropagationManager propagationManager, final PropagationTaskExecutor taskExecutor, - final UserDAO userDAO) { + final UserDAO userDAO, + final EncryptorManager encryptorManager) { this.uwfAdapter = uwfAdapter; this.propagationManager = propagationManager; this.taskExecutor = taskExecutor; this.userDAO = userDAO; + this.encryptorManager = encryptorManager; + } + + @Transactional(readOnly = true) + @Override + public boolean checkSecurityAnswer(final String key, final String value) { + User user = userDAO.findById(key).orElseThrow(() -> new NotFoundException("User " + key)); + + return encryptorManager.getInstance().verify(value, user.getCipherAlgorithm(), user.getSecurityAnswer()); } @Override @@ -296,8 +311,8 @@ protected List propagateStatus( } @Override - public void internalSuspend(final String key, final String updater, final String context) { - Pair, Boolean> updated = uwfAdapter.internalSuspend(key, updater, context); + public void suspendOnAuthFailures(final String key, final String updater, final String context) { + Pair, Boolean> updated = uwfAdapter.suspendOnAuthFailures(key, updater, context); // propagate suspension if and only if it is required by policy if (updated != null && updated.getRight()) { diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java index ada8e33bf3d..9717a71d440 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java @@ -30,6 +30,7 @@ import org.apache.syncope.core.persistence.api.EncryptorManager; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; @@ -292,7 +293,8 @@ public JobManager jobManager( final ReportDAO reportDAO, final ImplementationDAO implementationDAO, final TaskUtilsFactory taskUtilsFactory, - final ConfParamOps confParamOps) { + final ConfParamOps confParamOps, + final ConfigurableApplicationContext ctx) { return new DefaultJobManager( domainHolder, @@ -303,7 +305,8 @@ public JobManager jobManager( implementationDAO, taskUtilsFactory, confParamOps, - securityProperties); + securityProperties, + ctx); } /** @@ -394,8 +397,8 @@ public OutboundMatcher outboundMatcher( @ConditionalOnMissingBean @Bean - public DerAttrHandler derAttrHandler(final AnyUtilsFactory anyUtilsFactory, final JexlTools jexlTools) { - return new DefaultDerAttrHandler(anyUtilsFactory, jexlTools); + public DerAttrHandler derAttrHandler(final AnyChecker anyChecker, final JexlTools jexlTools) { + return new DefaultDerAttrHandler(anyChecker, jexlTools); } @ConditionalOnMissingBean @@ -534,13 +537,15 @@ public UserProvisioningManager userProvisioningManager( final UserWorkflowAdapter uwfAdapter, final PropagationManager propagationManager, final PropagationTaskExecutor taskExecutor, - final UserDAO userDAO) { + final UserDAO userDAO, + final EncryptorManager encryptorManager) { return new DefaultUserProvisioningManager( uwfAdapter, propagationManager, taskExecutor, - userDAO); + userDAO, + encryptorManager); } @ConditionalOnMissingBean @@ -653,14 +658,16 @@ public SystemLoadReporterJob systemLoadReporterJob(final ApplicationContext ctx) @ConditionalOnMissingBean @Bean public NotificationJobDelegate notificationJobDelegate( - final TaskUtilsFactory taskUtilsFactory, + final ConfParamOps confParamOps, final TaskDAO taskDAO, + final TaskUtilsFactory taskUtilsFactory, final AuditManager auditManager, final NotificationManager notificationManager, final ApplicationEventPublisher publisher, final JavaMailSender mailSender) { return new MailNotificationJobDelegate( + confParamOps, taskDAO, taskUtilsFactory, auditManager, @@ -684,7 +691,6 @@ public NotificationJob notificationJob( public LiveSyncTaskSaver liveSyncTaskExecSaver( final ExternalResourceDAO resourceDAO, final TaskDAO taskDAO, - final TaskExecDAO taskExecDAO, final TaskUtilsFactory taskUtilsFactory, final NotificationManager notificationManager, final AuditManager auditManager) { @@ -692,7 +698,6 @@ public LiveSyncTaskSaver liveSyncTaskExecSaver( return new LiveSyncTaskSaver( resourceDAO, taskDAO, - taskExecDAO, taskUtilsFactory, notificationManager, auditManager); @@ -731,6 +736,7 @@ public AnyObjectDataBinder anyObjectDataBinder( final PlainSchemaDAO plainSchemaDAO, final ExternalResourceDAO resourceDAO, final RelationshipTypeDAO relationshipTypeDAO, + final AnyChecker anyChecker, final DerAttrHandler derAttrHandler, final MappingManager mappingManager, final IntAttrNameParser intAttrNameParser, @@ -748,6 +754,7 @@ public AnyObjectDataBinder anyObjectDataBinder( plainSchemaDAO, resourceDAO, relationshipTypeDAO, + anyChecker, entityFactory, anyUtilsFactory, derAttrHandler, @@ -762,11 +769,12 @@ public AnyObjectDataBinder anyObjectDataBinder( @Bean public AnyTypeClassDataBinder anyTypeClassDataBinder( final EntityFactory entityFactory, + final AnyTypeClassDAO anyTypeClassDAO, final PlainSchemaDAO plainSchemaDAO, final DerSchemaDAO derSchemaDAO, final AnyTypeDAO anyTypeDAO) { - return new AnyTypeClassDataBinderImpl(plainSchemaDAO, derSchemaDAO, anyTypeDAO, entityFactory); + return new AnyTypeClassDataBinderImpl(anyTypeClassDAO, plainSchemaDAO, derSchemaDAO, anyTypeDAO, entityFactory); } @ConditionalOnMissingBean @@ -885,6 +893,7 @@ public GroupDataBinder groupDataBinder( final PlainSchemaDAO plainSchemaDAO, final ExternalResourceDAO resourceDAO, final RelationshipTypeDAO relationshipTypeDAO, + final AnyChecker anyChecker, final DerAttrHandler derAttrHandler, final MappingManager mappingManager, final IntAttrNameParser intAttrNameParser, @@ -902,6 +911,7 @@ public GroupDataBinder groupDataBinder( plainSchemaDAO, resourceDAO, relationshipTypeDAO, + anyChecker, entityFactory, anyUtilsFactory, derAttrHandler, @@ -993,11 +1003,12 @@ public RealmDataBinder realmDataBinder( @ConditionalOnMissingBean @Bean public RelationshipTypeDataBinder relationshipTypeDataBinder( + final RelationshipTypeDAO relationshipTypeDAO, final AnyTypeDAO anyTypeDAO, final AnyTypeClassDAO anyTypeClassDAO, final EntityFactory entityFactory) { - return new RelationshipTypeDataBinderImpl(anyTypeDAO, anyTypeClassDAO, entityFactory); + return new RelationshipTypeDataBinderImpl(relationshipTypeDAO, anyTypeDAO, anyTypeClassDAO, entityFactory); } @ConditionalOnMissingBean @@ -1143,6 +1154,7 @@ public UserDataBinder userDataBinder( final PlainSchemaDAO plainSchemaDAO, final ExternalResourceDAO resourceDAO, final RelationshipTypeDAO relationshipTypeDAO, + final AnyChecker anyChecker, final DerAttrHandler derAttrHandler, final MappingManager mappingManager, final IntAttrNameParser intAttrNameParser, @@ -1165,6 +1177,7 @@ public UserDataBinder userDataBinder( plainSchemaDAO, resourceDAO, relationshipTypeDAO, + anyChecker, entityFactory, anyUtilsFactory, derAttrHandler, diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyDataBinder.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyDataBinder.java index c26a1b8b657..523a3646922 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyDataBinder.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyDataBinder.java @@ -51,6 +51,7 @@ import org.apache.syncope.common.lib.types.ResourceOperation; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; @@ -174,6 +175,8 @@ protected static MembershipTO getMembershipTO( protected final RelationshipTypeDAO relationshipTypeDAO; + protected final AnyChecker anyChecker; + protected final EntityFactory entityFactory; protected final AnyUtilsFactory anyUtilsFactory; @@ -190,6 +193,7 @@ protected AnyDataBinder( final PlainSchemaDAO plainSchemaDAO, final ExternalResourceDAO resourceDAO, final RelationshipTypeDAO relationshipTypeDAO, + final AnyChecker anyChecker, final EntityFactory entityFactory, final AnyUtilsFactory anyUtilsFactory, final DerAttrHandler derAttrHandler, @@ -208,6 +212,7 @@ protected AnyDataBinder( this.groupDAO = groupDAO; this.resourceDAO = resourceDAO; this.relationshipTypeDAO = relationshipTypeDAO; + this.anyChecker = anyChecker; this.entityFactory = entityFactory; this.anyUtilsFactory = anyUtilsFactory; this.outboundMatcher = outboundMatcher; @@ -313,11 +318,11 @@ protected SyncopeClientException checkMandatoryOnResources( return reqValMissing; } - protected SyncopeClientException checkMandatory(final Any any, final AnyUtils anyUtils) { + protected SyncopeClientException checkMandatory(final Any any) { SyncopeClientException reqValMissing = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing); // Check if there is some mandatory schema defined for which no value has been provided - AllowedSchemas allowedPlainSchemas = anyUtils.dao().findAllowedSchemas(any, PlainSchema.class); + AllowedSchemas allowedPlainSchemas = anyChecker.findAllowedSchemas(any, PlainSchema.class); allowedPlainSchemas.self().forEach(schema -> checkMandatory( schema, any.getPlainAttr(schema.getKey()).orElse(null), any, reqValMissing)); if (any instanceof Groupable groupable) { @@ -521,7 +526,7 @@ protected void fill( scce.addException(invalidValues); } - SyncopeClientException reqValMissing = checkMandatory(any, anyUtils); + SyncopeClientException reqValMissing = checkMandatory(any); if (!reqValMissing.isEmpty()) { scce.addException(reqValMissing); } @@ -604,13 +609,7 @@ protected void fill( anyCR.getAuxClasses().stream(). map(anyTypeClassDAO::findById). flatMap(Optional::stream). - forEach(auxClass -> { - if (auxClass == null) { - LOG.debug("Invalid {} {}, ignoring...", AnyTypeClass.class.getSimpleName(), auxClass); - } else { - any.add(auxClass); - } - }); + forEach(any::add); // 1. relationships Set> relationships = new HashSet<>(); @@ -668,7 +667,7 @@ protected void fill( scce.addException(invalidValues); } - SyncopeClientException requiredValuesMissing = checkMandatory(any, anyUtils); + SyncopeClientException requiredValuesMissing = checkMandatory(any); if (!requiredValuesMissing.isEmpty()) { scce.addException(requiredValuesMissing); } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java index 70bb2657204..b9c481ce039 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java @@ -34,6 +34,7 @@ import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; @@ -72,6 +73,7 @@ public AnyObjectDataBinderImpl( final PlainSchemaDAO plainSchemaDAO, final ExternalResourceDAO resourceDAO, final RelationshipTypeDAO relationshipTypeDAO, + final AnyChecker anyChecker, final EntityFactory entityFactory, final AnyUtilsFactory anyUtilsFactory, final DerAttrHandler derAttrHandler, @@ -90,6 +92,7 @@ public AnyObjectDataBinderImpl( plainSchemaDAO, resourceDAO, relationshipTypeDAO, + anyChecker, entityFactory, anyUtilsFactory, derAttrHandler, diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderImpl.java index badc2b027e6..304297fcbd1 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderImpl.java @@ -20,6 +20,7 @@ import java.util.List; import org.apache.syncope.common.lib.to.AnyTypeClassTO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; @@ -36,6 +37,8 @@ public class AnyTypeClassDataBinderImpl implements AnyTypeClassDataBinder { protected static final Logger LOG = LoggerFactory.getLogger(AnyTypeClassDataBinder.class); + protected final AnyTypeClassDAO anyTypeClassDAO; + protected final PlainSchemaDAO plainSchemaDAO; protected final DerSchemaDAO derSchemaDAO; @@ -45,11 +48,13 @@ public class AnyTypeClassDataBinderImpl implements AnyTypeClassDataBinder { protected final EntityFactory entityFactory; public AnyTypeClassDataBinderImpl( + final AnyTypeClassDAO anyTypeClassDAO, final PlainSchemaDAO plainSchemaDAO, final DerSchemaDAO derSchemaDAO, final AnyTypeDAO anyTypeDAO, final EntityFactory entityFactory) { + this.anyTypeClassDAO = anyTypeClassDAO; this.plainSchemaDAO = plainSchemaDAO; this.derSchemaDAO = derSchemaDAO; this.anyTypeDAO = anyTypeDAO; @@ -58,50 +63,54 @@ public AnyTypeClassDataBinderImpl( @Override public AnyTypeClass create(final AnyTypeClassTO anyTypeClassTO) { - AnyTypeClass anyTypeClass = entityFactory.newEntity(AnyTypeClass.class); - update(anyTypeClass, anyTypeClassTO); - return anyTypeClass; + return update(entityFactory.newEntity(AnyTypeClass.class), anyTypeClassTO); } @Override - public void update(final AnyTypeClass anyTypeClass, final AnyTypeClassTO anyTypeClassTO) { + public AnyTypeClass update(final AnyTypeClass anyTypeClass, final AnyTypeClassTO anyTypeClassTO) { + AnyTypeClass atc; if (anyTypeClass.getKey() == null) { anyTypeClass.setKey(anyTypeClassTO.getKey()); + atc = anyTypeClassDAO.save(anyTypeClass); + } else { + atc = anyTypeClass; } - plainSchemaDAO.findByAnyTypeClasses(List.of(anyTypeClass)).forEach(schema -> { + plainSchemaDAO.findByAnyTypeClasses(List.of(atc)).forEach(schema -> { schema.setAnyTypeClass(null); plainSchemaDAO.save(schema); }); - anyTypeClass.getPlainSchemas().clear(); + atc.getPlainSchemas().clear(); anyTypeClassTO.getPlainSchemas().forEach(key -> { PlainSchema schema = plainSchemaDAO.findById(key).orElse(null); if (schema == null || schema.getAnyTypeClass() != null) { LOG.debug("Invalid or already in use: {} {}, ignoring...", PlainSchema.class.getSimpleName(), key); } else { - anyTypeClass.add(schema); - schema.setAnyTypeClass(anyTypeClass); + atc.add(schema); + schema.setAnyTypeClass(atc); plainSchemaDAO.save(schema); } }); - derSchemaDAO.findByAnyTypeClasses(List.of(anyTypeClass)).forEach(schema -> { + derSchemaDAO.findByAnyTypeClasses(List.of(atc)).forEach(schema -> { schema.setAnyTypeClass(null); derSchemaDAO.save(schema); }); - anyTypeClass.getDerSchemas().clear(); + atc.getDerSchemas().clear(); anyTypeClassTO.getDerSchemas().forEach(key -> { DerSchema schema = derSchemaDAO.findById(key).orElse(null); if (schema == null || schema.getAnyTypeClass() != null) { LOG.debug("Invalid or already in use: {} {}, ignoring...", DerSchema.class.getSimpleName(), key); } else { - anyTypeClass.add(schema); - schema.setAnyTypeClass(anyTypeClass); + atc.add(schema); + schema.setAnyTypeClass(atc); derSchemaDAO.save(schema); } }); + + return atc; } @Override diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java index 9513856fae4..8799307db67 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.provisioning.java.data; -import com.fasterxml.jackson.core.type.TypeReference; import java.util.HashSet; import java.util.Set; import org.apache.syncope.common.lib.SyncopeClientException; @@ -44,6 +43,7 @@ import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.core.type.TypeReference; public class AnyTypeDataBinderImpl implements AnyTypeDataBinder { diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderImpl.java index 615e1b4a9ea..38cf317e30d 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderImpl.java @@ -53,11 +53,16 @@ public AuthProfile create(final AuthProfileTO authProfileTO) { @Override public AuthProfile update(final AuthProfile authProfile, final AuthProfileTO authProfileTO) { - authProfile.setImpersonationAccounts(authProfileTO.getImpersonationAccounts()); - authProfile.setGoogleMfaAuthTokens(authProfileTO.getGoogleMfaAuthTokens()); - authProfile.setGoogleMfaAuthAccounts(authProfileTO.getGoogleMfaAuthAccounts()); - authProfile.setMfaTrustedDevices(authProfileTO.getMfaTrustedDevices()); - authProfile.setWebAuthnDeviceCredentials(authProfileTO.getWebAuthnDeviceCredentials()); + authProfile.getImpersonationAccounts().clear(); + authProfileTO.getImpersonationAccounts().forEach(authProfile::add); + authProfile.getGoogleMfaAuthTokens().clear(); + authProfileTO.getGoogleMfaAuthTokens().forEach(authProfile::add); + authProfile.getGoogleMfaAuthAccounts().clear(); + authProfileTO.getGoogleMfaAuthAccounts().forEach(authProfile::add); + authProfile.getMfaTrustedDevices().clear(); + authProfileTO.getMfaTrustedDevices().forEach(authProfile::add); + authProfile.getWebAuthnDeviceCredentials().clear(); + authProfileTO.getWebAuthnDeviceCredentials().forEach(authProfile::add); return authProfile; } } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java index 64afec9f61d..9961cbfac27 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java @@ -22,7 +22,6 @@ import java.util.Collection; import java.util.List; import java.util.Optional; -import java.util.function.Predicate; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.to.ConnInstanceTO; @@ -108,7 +107,7 @@ public ConnInstance getConnInstance(final ConnInstanceTO connInstanceTO) { } Optional.ofNullable(connInstanceTO.getLocation()).ifPresent(connInstance::setLocation); - connInstance.setConf(connInstanceTO.getConf()); + connInstance.getConf().addAll(connInstanceTO.getConf()); Optional.ofNullable(connInstanceTO.getPoolConf()). ifPresent(conf -> connInstance.setPoolConf(ConnPoolConfUtils.getConnPoolConf(conf))); @@ -141,9 +140,8 @@ public ConnInstance update(final ConnInstanceTO connInstanceTO) { Optional.ofNullable(connInstanceTO.getVersion()).ifPresent(connInstance::setVersion); Optional.ofNullable(connInstanceTO.getConnectorName()).ifPresent(connInstance::setConnectorName); Optional.ofNullable(connInstanceTO.getDisplayName()).ifPresent(connInstance::setDisplayName); - Optional.ofNullable(connInstanceTO.getConf()). - filter(Predicate.not(Collection::isEmpty)). - ifPresent(connInstance::setConf); + connInstance.getConf().clear(); + connInstance.getConf().addAll(connInstanceTO.getConf()); Optional.ofNullable(connInstanceTO.getConnRequestTimeout()).ifPresent(connInstance::setConnRequestTimeout); Optional.ofNullable(connInstanceTO.getPoolConf()).ifPresentOrElse( conf -> connInstance.setPoolConf(ConnPoolConfUtils.getConnPoolConf(conf)), diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java index e934692ad9b..8790759aa12 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.provisioning.java.data; -import java.util.Iterator; import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.to.DynRealmTO; import org.apache.syncope.common.lib.types.ClientExceptionType; @@ -59,26 +58,6 @@ public DynRealmDataBinderImpl( this.searchCondVisitor = searchCondVisitor; } - protected void setDynMembership(final DynRealm dynRealm, final AnyType anyType, final String dynMembershipFIQL) { - SearchCond dynMembershipCond = SearchCondConverter.convert(searchCondVisitor, dynMembershipFIQL); - if (!dynMembershipCond.isValid()) { - SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchParameters); - sce.getElements().add(dynMembershipFIQL); - throw sce; - } - - DynRealmMembership dynMembership; - if (dynRealm.getDynMembership(anyType).isPresent()) { - dynMembership = dynRealm.getDynMembership(anyType).get(); - } else { - dynMembership = entityFactory.newEntity(DynRealmMembership.class); - dynMembership.setDynRealm(dynRealm); - dynMembership.setAnyType(anyType); - dynRealm.add(dynMembership); - } - dynMembership.setFIQLCond(dynMembershipFIQL); - } - @Override public DynRealm create(final DynRealmTO dynRealmTO) { return update(entityFactory.newEntity(DynRealm.class), dynRealmTO); @@ -89,16 +68,31 @@ public DynRealm update(final DynRealm toBeUpdated, final DynRealmTO dynRealmTO) toBeUpdated.setKey(dynRealmTO.getKey()); DynRealm dynRealm = dynRealmDAO.save(toBeUpdated); - for (Iterator itor = dynRealm.getDynMemberships().iterator(); itor.hasNext();) { - DynRealmMembership memb = itor.next(); - memb.setDynRealm(null); - itor.remove(); - } - dynRealmTO.getDynMembershipConds().forEach((type, fiql) -> anyTypeDAO.findById(type).ifPresentOrElse( - anyType -> setDynMembership(dynRealm, anyType, fiql), + anyType -> { + SearchCond dynMembershipCond = SearchCondConverter.convert(searchCondVisitor, fiql); + if (!dynMembershipCond.isValid()) { + SyncopeClientException sce = SyncopeClientException.build( + ClientExceptionType.InvalidSearchParameters); + sce.getElements().add(fiql); + throw sce; + } + + DynRealmMembership dynMembership = dynRealm.getDynMembership(anyType).orElse(null); + if (dynMembership == null) { + dynMembership = entityFactory.newEntity(DynRealmMembership.class); + dynMembership.setDynRealm(dynRealm); + dynMembership.setAnyType(anyType); + dynRealm.add(dynMembership); + } + dynMembership.setFIQLCond(fiql); + }, () -> LOG.warn("Ignoring invalid {}: {}", AnyType.class.getSimpleName(), type))); + dynRealm.getDynMemberships().removeAll(dynRealm.getDynMemberships().stream(). + filter(d -> !dynRealmTO.getDynMembershipConds().keySet().contains(d.getAnyType().getKey())). + toList()); + return dynRealmDAO.saveAndRefreshDynMemberships(dynRealm); } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java index a955fc678fd..a0602447f83 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java @@ -19,7 +19,6 @@ package org.apache.syncope.core.provisioning.java.data; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -38,6 +37,7 @@ import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.lib.types.ResourceOperation; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; @@ -55,11 +55,8 @@ import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.Groupable; import org.apache.syncope.core.persistence.api.entity.Realm; -import org.apache.syncope.core.persistence.api.entity.Relatable; -import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.group.GroupTypeExtension; -import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.api.search.SearchCondConverter; import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; @@ -88,6 +85,7 @@ public GroupDataBinderImpl( final PlainSchemaDAO plainSchemaDAO, final ExternalResourceDAO resourceDAO, final RelationshipTypeDAO relationshipTypeDAO, + final AnyChecker anyChecker, final EntityFactory entityFactory, final AnyUtilsFactory anyUtilsFactory, final DerAttrHandler derAttrHandler, @@ -107,6 +105,7 @@ public GroupDataBinderImpl( plainSchemaDAO, resourceDAO, relationshipTypeDAO, + anyChecker, entityFactory, anyUtilsFactory, derAttrHandler, @@ -132,20 +131,12 @@ protected void setDynMembership(final Group group, final AnyType anyType, final throw sce; } - DynGroupMembership dynMembership; - if (anyType.getKind() == AnyTypeKind.ANY_OBJECT && group.getADynMembership(anyType).isEmpty()) { - dynMembership = entityFactory.newEntity(ADynGroupMembership.class); + DynGroupMembership dynMembership = group.getDynMembership(anyType).orElse(null); + if (dynMembership == null) { + dynMembership = entityFactory.newEntity(DynGroupMembership.class); + dynMembership.setAnyType(anyType); dynMembership.setGroup(group); - ((ADynGroupMembership) dynMembership).setAnyType(anyType); - group.add((ADynGroupMembership) dynMembership); - } else if (anyType.getKind() == AnyTypeKind.USER && group.getUDynMembership() == null) { - dynMembership = entityFactory.newEntity(UDynGroupMembership.class); - dynMembership.setGroup(group); - group.setUDynMembership((UDynGroupMembership) dynMembership); - } else { - dynMembership = anyType.getKind() == AnyTypeKind.ANY_OBJECT - ? group.getADynMembership(anyType).get() - : group.getUDynMembership(); + group.add(dynMembership); } dynMembership.setFIQLCond(dynMembershipFIQL); } @@ -192,10 +183,7 @@ public void create(final Group group, final GroupCR groupCR) { } // dynamic membership - if (groupCR.getUDynMembershipCond() != null) { - setDynMembership(group, anyTypeDAO.getUser(), groupCR.getUDynMembershipCond()); - } - groupCR.getADynMembershipConds().forEach((type, fiql) -> anyTypeDAO.findById(type).ifPresentOrElse( + groupCR.getDynMembershipConds().forEach((type, fiql) -> anyTypeDAO.findById(type).ifPresentOrElse( anyType -> setDynMembership(group, anyType, fiql), () -> LOG.warn("Ignoring invalid {}: {}", AnyType.class.getSimpleName(), type))); @@ -292,22 +280,7 @@ public PropagationByResource update(final Group toBeUpdated, final Group group = groupDAO.save(group); // dynamic membership - if (groupUR.getUDynMembershipCond() == null) { - if (group.getUDynMembership() != null) { - group.getUDynMembership().setGroup(null); - group.setUDynMembership(null); - groupDAO.clearUDynMembers(group); - } - } else { - setDynMembership(group, anyTypeDAO.getUser(), groupUR.getUDynMembershipCond()); - } - for (Iterator itor = group.getADynMemberships().iterator(); itor.hasNext();) { - ADynGroupMembership memb = itor.next(); - memb.setGroup(null); - itor.remove(); - } - groupDAO.clearADynMembers(group); - for (Map.Entry entry : groupUR.getADynMembershipConds().entrySet()) { + for (Map.Entry entry : groupUR.getDynMembershipConds().entrySet()) { AnyType anyType = anyTypeDAO.findById(entry.getKey()).orElse(null); if (anyType == null) { LOG.warn("Ignoring invalid {}: {}", AnyType.class.getSimpleName(), entry.getKey()); @@ -315,6 +288,9 @@ public PropagationByResource update(final Group toBeUpdated, final Group setDynMembership(group, anyType, entry.getValue()); } } + group.getDynMemberships().removeAll(group.getDynMemberships().stream(). + filter(d -> !groupUR.getDynMembershipConds().keySet().contains(d.getAnyType().getKey())). + toList()); group = groupDAO.saveAndRefreshDynMemberships(group); @@ -416,11 +392,8 @@ public GroupTO getGroupTO(final Group group, final boolean details) { groupTO.setDynamicUserMembershipCount(groupDAO.countUDynMembers(group)); groupTO.setDynamicAnyObjectMembershipCount(groupDAO.countADynMembers(group)); - Optional.ofNullable(group.getUDynMembership()). - map(UDynGroupMembership::getFIQLCond). - ifPresent(groupTO::setUDynMembershipCond); - group.getADynMemberships(). - forEach(memb -> groupTO.getADynMembershipConds().put(memb.getAnyType().getKey(), memb.getFIQLCond())); + group.getDynMemberships(). + forEach(memb -> groupTO.getDynMembershipConds().put(memb.getAnyType().getKey(), memb.getFIQLCond())); group.getTypeExtensions().forEach(typeExt -> groupTO.getTypeExtensions().add(getTypeExtensionTO(typeExt))); @@ -428,7 +401,7 @@ public GroupTO getGroupTO(final Group group, final boolean details) { // relationships groupTO.getRelationships().addAll(group.getRelationships().stream(). map(relationship -> getRelationshipTO(group.getPlainAttrs(relationship), - derAttrHandler.getValues((Relatable) group, relationship), + derAttrHandler.getValues(group, relationship), relationship.getType().getKey(), RelationshipTO.End.LEFT, relationship.getRightEnd())).toList()); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java index a0aef8c3a9c..f6e3ebcec20 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java @@ -37,6 +37,7 @@ import org.apache.syncope.core.provisioning.api.jexl.JexlContextBuilder; import org.apache.syncope.core.provisioning.api.jexl.JexlTools; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; public class JEXLItemTransformerImpl implements JEXLItemTransformer { @@ -69,7 +70,7 @@ protected AttrSchemaType beforePropagation( if (attributable != null) { builder.fields(attributable). plainAttrs(attributable.getPlainAttrs()). - derAttrs(attributable, derAttrHandler); + derAttrs(jexlTools.derAttrs(attributable, derAttrHandler)); } JexlContext jexlContext = builder.build(); @@ -144,6 +145,7 @@ protected AttrSchemaType beforePropagation( return AttrSchemaType.String; } + @Transactional(readOnly = true) @Override public MappingManager.IntValues beforePropagation( final Item item, diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RelationshipTypeDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RelationshipTypeDataBinderImpl.java index 5808882ca00..5e461fc2392 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RelationshipTypeDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RelationshipTypeDataBinderImpl.java @@ -24,6 +24,7 @@ import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.entity.AnyType; import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; import org.apache.syncope.core.persistence.api.entity.EntityFactory; @@ -37,6 +38,8 @@ public class RelationshipTypeDataBinderImpl implements RelationshipTypeDataBinde protected static final Logger LOG = LoggerFactory.getLogger(RelationshipTypeDataBinder.class); + protected final RelationshipTypeDAO relationshipTypeDAO; + protected final AnyTypeDAO anyTypeDAO; protected final AnyTypeClassDAO anyTypeClassDAO; @@ -44,10 +47,12 @@ public class RelationshipTypeDataBinderImpl implements RelationshipTypeDataBinde protected final EntityFactory entityFactory; public RelationshipTypeDataBinderImpl( + final RelationshipTypeDAO relationshipTypeDAO, final AnyTypeDAO anyTypeDAO, final AnyTypeClassDAO anyTypeClassDAO, final EntityFactory entityFactory) { + this.relationshipTypeDAO = relationshipTypeDAO; this.anyTypeDAO = anyTypeDAO; this.anyTypeClassDAO = anyTypeClassDAO; this.entityFactory = entityFactory; @@ -64,29 +69,34 @@ public RelationshipType create(final RelationshipTypeTO relationshipTypeTO) { flatMap(anyTypeDAO::findById). orElseThrow(() -> new NotFoundException("AnyType " + relationshipTypeTO.getRightEndAnyType()))); - update(relationshipType, relationshipTypeTO); - - return relationshipType; + return update(relationshipType, relationshipTypeTO); } @Override - public void update(final RelationshipType relationshipType, final RelationshipTypeTO relationshipTypeTO) { + public RelationshipType update( + final RelationshipType relationshipType, + final RelationshipTypeTO relationshipTypeTO) { + + RelationshipType rt; if (relationshipType.getKey() == null) { relationshipType.setKey(relationshipTypeTO.getKey()); + rt = relationshipTypeDAO.save(relationshipType); + } else { + rt = relationshipType; } - relationshipType.setDescription(relationshipTypeTO.getDescription()); + rt.setDescription(relationshipTypeTO.getDescription()); // type extensions relationshipTypeTO.getTypeExtensions(). forEach(typeExtTO -> anyTypeDAO.findById(typeExtTO.getAnyType()).ifPresentOrElse(anyType -> { - RelationshipTypeExtension typeExt = relationshipType.getTypeExtension(anyType).orElse(null); + RelationshipTypeExtension typeExt = rt.getTypeExtension(anyType).orElse(null); if (typeExt == null) { typeExt = entityFactory.newEntity(RelationshipTypeExtension.class); typeExt.setAnyType(anyType); - typeExt.setRelationshipType(relationshipType); - relationshipType.add(typeExt); + typeExt.setRelationshipType(rt); + rt.add(typeExt); } // add all classes contained in the TO @@ -104,15 +114,17 @@ public void update(final RelationshipType relationshipType, final RelationshipTy // only consider non-empty type extensions if (typeExt.getAuxClasses().isEmpty()) { - relationshipType.getTypeExtensions().remove(typeExt); + rt.getTypeExtensions().remove(typeExt); typeExt.setRelationshipType(null); } }, () -> LOG.warn("Ignoring invalid {}: {}", AnyType.class.getSimpleName(), typeExtTO.getAnyType()))); // remove all type extensions not contained in the TO - relationshipType.getTypeExtensions(). + rt.getTypeExtensions(). removeIf(typeExt -> relationshipTypeTO.getTypeExtension(typeExt.getAnyType().getKey()).isEmpty()); + + return rt; } @Override diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java index a954aadafa7..ec5f663a72a 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java @@ -127,10 +127,6 @@ public ExternalResource update(final ExternalResource resource, final ResourceTO ConnInstance connector = connInstanceDAO.findById(resourceTO.getConnector()). orElseThrow(() -> new NotFoundException("ConnInstance " + resourceTO.getConnector())); resource.setConnector(connector); - - if (!connector.getResources().contains(resource)) { - connector.add(resource); - } } resource.setEnforceMandatoryCondition(resourceTO.isEnforceMandatoryCondition()); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java index 6c127681342..ccea8106b12 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java @@ -50,6 +50,7 @@ import org.apache.syncope.common.lib.types.ResourceOperation; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.AnyChecker; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; @@ -113,6 +114,7 @@ public UserDataBinderImpl( final PlainSchemaDAO plainSchemaDAO, final ExternalResourceDAO resourceDAO, final RelationshipTypeDAO relationshipTypeDAO, + final AnyChecker anyChecker, final EntityFactory entityFactory, final AnyUtilsFactory anyUtilsFactory, final DerAttrHandler derAttrHandler, @@ -137,6 +139,7 @@ public UserDataBinderImpl( plainSchemaDAO, resourceDAO, relationshipTypeDAO, + anyChecker, entityFactory, anyUtilsFactory, derAttrHandler, @@ -255,15 +258,13 @@ protected void linkedAccount( } account.setSuspended(accountTO.isSuspended()); + new HashSet<>(account.getPlainAttrs()).forEach(account::remove); accountTO.getPlainAttrs().stream(). filter(attrTO -> !attrTO.getValues().isEmpty()). forEach(attrTO -> getPlainSchema(attrTO.getSchema()).ifPresent(schema -> { - PlainAttr attr = account.getPlainAttr(schema.getKey()).orElseGet(() -> { - PlainAttr newAttr = new PlainAttr(); - newAttr.setPlainSchema(schema); - return newAttr; - }); + PlainAttr attr = new PlainAttr(); + attr.setPlainSchema(schema); fillAttr(anyTO, attrTO.getValues(), schema, attr, invalidValues); if (!attr.getValuesAsStrings().isEmpty()) { @@ -458,27 +459,34 @@ public UserWorkflowResult.PropagationInfo update(final User toBeUpdated, final U // linked accounts userUR.getLinkedAccounts().stream().filter(patch -> patch.getLinkedAccountTO() != null).forEach(patch -> { - user.getLinkedAccount( - patch.getLinkedAccountTO().getResource(), - patch.getLinkedAccountTO().getConnObjectKeyValue()).ifPresent(account -> { - - if (patch.getOperation() == PatchOperation.DELETE) { - user.getLinkedAccounts().remove(account); - account.setOwner(null); + switch (patch.getOperation()) { + case DELETE -> { + user.getLinkedAccount( + patch.getLinkedAccountTO().getResource(), + patch.getLinkedAccountTO().getConnObjectKeyValue()).ifPresentOrElse( + account -> { + user.getLinkedAccounts().remove(account); + account.setOwner(null); + + propByLinkedAccount.add( + ResourceOperation.DELETE, + Pair.of(account.getResource().getKey(), account.getConnObjectKeyValue())); + }, + () -> LOG.debug("No linked acccount ({},{}) was found, nothing to delete", + patch.getLinkedAccountTO().getResource(), + patch.getLinkedAccountTO().getConnObjectKeyValue())); + } - propByLinkedAccount.add( - ResourceOperation.DELETE, - Pair.of(account.getResource().getKey(), account.getConnObjectKeyValue())); + case ADD_REPLACE -> { + linkedAccount( + anyTO, + user, + patch.getLinkedAccountTO(), + invalidValues); } - new HashSet<>(account.getPlainAttrs()).forEach(account::remove); - }); - if (patch.getOperation() == PatchOperation.ADD_REPLACE) { - linkedAccount( - anyTO, - user, - patch.getLinkedAccountTO(), - invalidValues); + default -> { + } } }); user.getLinkedAccounts().forEach(account -> propByLinkedAccount.add( @@ -495,7 +503,7 @@ public UserWorkflowResult.PropagationInfo update(final User toBeUpdated, final U // Build final information for next stage (propagation) Map afterOnResources = - onResources(user, userDAO.findAllResourceKeys(user.getKey()), password, changePwdRes); + onResources(saved, userDAO.findAllResourceKeys(saved.getKey()), password, changePwdRes); propByRes.merge(propByRes(beforeOnResources, afterOnResources)); if (userUR.getMustChangePassword() != null) { diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java index 5028c59f07e..29e3da8c033 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java @@ -23,7 +23,6 @@ import org.apache.syncope.common.lib.types.OpEvent; import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.core.persistence.api.dao.TaskDAO; -import org.apache.syncope.core.persistence.api.dao.TaskExecDAO; import org.apache.syncope.core.persistence.api.entity.task.SchedTask; import org.apache.syncope.core.persistence.api.entity.task.TaskExec; import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; @@ -59,12 +58,6 @@ public abstract class AbstractSchedTaskJobDelegate implemen */ protected T task; - /** - * Task execution DAO. - */ - @Autowired - protected TaskExecDAO taskExecDAO; - /** * Task DAO. */ @@ -141,7 +134,6 @@ protected void endExecution( if (hasToBeRegistered(execution)) { register(execution); } - task = taskDAO.save(task); notificationManager.createTasks( executor, @@ -171,7 +163,6 @@ protected void end() { } } - @SuppressWarnings("unchecked") @Transactional @Override public void execute( @@ -229,7 +220,8 @@ protected boolean hasToBeRegistered(final TaskExec execution) { return false; } - protected void register(final TaskExec execution) { - taskExecDAO.saveAndAdd(taskType, task.getKey(), execution); + protected void register(final TaskExec execution) { + task.add(execution); + task = taskDAO.save(task); } } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java index 99ce29b75ba..c81d01fb0b6 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java @@ -28,7 +28,6 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.IdRepoImplementationType; import org.apache.syncope.common.lib.types.TaskType; -import org.apache.syncope.core.persistence.api.ApplicationContextProvider; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.SyncopeCoreLoader; import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; @@ -54,6 +53,7 @@ import org.apache.syncope.core.spring.security.SecurityProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.scheduling.support.CronTrigger; import org.springframework.transaction.annotation.Transactional; @@ -79,6 +79,8 @@ public class DefaultJobManager implements JobManager, SyncopeCoreLoader { protected final SecurityProperties securityProperties; + protected final ConfigurableApplicationContext ctx; + public DefaultJobManager( final DomainHolder domainHolder, final SyncopeTaskScheduler scheduler, @@ -88,7 +90,8 @@ public DefaultJobManager( final ImplementationDAO implementationDAO, final TaskUtilsFactory taskUtilsFactory, final ConfParamOps confParamOps, - final SecurityProperties securityProperties) { + final SecurityProperties securityProperties, + final ConfigurableApplicationContext ctx) { this.domainHolder = domainHolder; this.scheduler = scheduler; @@ -99,6 +102,7 @@ public DefaultJobManager( this.taskUtilsFactory = taskUtilsFactory; this.confParamOps = confParamOps; this.securityProperties = securityProperties; + this.ctx = ctx; } @Override @@ -124,7 +128,7 @@ protected void registerJob( } // 1. prepare job - Job job = ApplicationContextProvider.getBeanFactory().createBean(jobClass); + Job job = ctx.getBeanFactory().createBean(jobClass); job.setContext(context); // 2. schedule diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/AbstractNotificationJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/AbstractNotificationJobDelegate.java index f572dab1818..6d5a38e2d8b 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/AbstractNotificationJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/AbstractNotificationJobDelegate.java @@ -21,6 +21,8 @@ import java.time.OffsetDateTime; import java.util.List; import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.OpEvent; import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.common.lib.types.TraceLevel; @@ -44,6 +46,8 @@ public abstract class AbstractNotificationJobDelegate implements NotificationJob protected static final Logger LOG = LoggerFactory.getLogger(NotificationJobDelegate.class); + protected final ConfParamOps confParamOps; + protected final TaskDAO taskDAO; protected final TaskUtilsFactory taskUtilsFactory; @@ -55,12 +59,14 @@ public abstract class AbstractNotificationJobDelegate implements NotificationJob protected final ApplicationEventPublisher publisher; protected AbstractNotificationJobDelegate( + final ConfParamOps confParamOps, final TaskDAO taskDAO, final TaskUtilsFactory taskUtilsFactory, final AuditManager auditManager, final NotificationManager notificationManager, final ApplicationEventPublisher publisher) { + this.confParamOps = confParamOps; this.taskDAO = taskDAO; this.taskUtilsFactory = taskUtilsFactory; this.auditManager = auditManager; @@ -177,7 +183,7 @@ public void execute(final String executor) { } } - protected static boolean hasToBeRegistered(final TaskExec execution) { + protected boolean hasToBeRegistered(final TaskExec execution) { NotificationTask task = execution.getTask(); // True if either failed and failures have to be registered, or if ALL @@ -188,16 +194,18 @@ protected static boolean hasToBeRegistered(final TaskExec exec } protected void handleRetries(final TaskExec execution) { - if (notificationManager.getMaxRetries() <= 0) { + long maxRetries = confParamOps.get(SyncopeConstants.MASTER_DOMAIN, "notification.maxRetries", 0L, Long.class); + + if (maxRetries <= 0) { return; } long failedExecutionsCount = notificationManager.countExecutionsWithStatus( execution.getTask().getKey(), NotificationJob.Status.NOT_SENT.name()); - if (failedExecutionsCount <= notificationManager.getMaxRetries()) { + if (failedExecutionsCount <= maxRetries) { LOG.debug("Execution of notification task {} will be retried [{}/{}]", - execution.getTask(), failedExecutionsCount, notificationManager.getMaxRetries()); + execution.getTask(), failedExecutionsCount, maxRetries); notificationManager.setTaskExecuted(execution.getTask().getKey(), false); auditManager.audit( diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/MailNotificationJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/MailNotificationJobDelegate.java index 5c23a5d5882..9635aa7007e 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/MailNotificationJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/MailNotificationJobDelegate.java @@ -19,6 +19,7 @@ package org.apache.syncope.core.provisioning.java.job.notification; import jakarta.mail.internet.MimeMessage; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.core.persistence.api.dao.TaskDAO; import org.apache.syncope.core.persistence.api.entity.task.NotificationTask; import org.apache.syncope.core.persistence.api.entity.task.TaskExec; @@ -34,6 +35,7 @@ public class MailNotificationJobDelegate extends AbstractNotificationJobDelegate protected final JavaMailSender mailSender; public MailNotificationJobDelegate( + final ConfParamOps confParamOps, final TaskDAO taskDAO, final TaskUtilsFactory taskUtilsFactory, final AuditManager auditManager, @@ -41,7 +43,7 @@ public MailNotificationJobDelegate( final ApplicationEventPublisher publisher, final JavaMailSender mailSender) { - super(taskDAO, taskUtilsFactory, auditManager, notificationManager, publisher); + super(confParamOps, taskDAO, taskUtilsFactory, auditManager, notificationManager, publisher); this.mailSender = mailSender; } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java index 951006a4b12..cbf6229eb25 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java @@ -33,7 +33,6 @@ import org.apache.commons.lang3.mutable.MutableObject; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.keymaster.client.api.ConfParamOps; -import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.AnyObjectTO; import org.apache.syncope.common.lib.to.GroupTO; import org.apache.syncope.common.lib.to.ProvisioningResult; @@ -166,12 +165,6 @@ public DefaultNotificationManager( this.jexlTools = jexlTools; } - @Transactional(readOnly = true) - @Override - public long getMaxRetries() { - return confParamOps.get(SyncopeConstants.MASTER_DOMAIN, "notification.maxRetries", 0L, Long.class); - } - /** * Create a notification task. * @@ -185,7 +178,7 @@ protected NotificationTask getNotificationTask( final Any any, final Map jexlVars) { - jexlVars.put("syncopeConf", confParamOps.list(SyncopeConstants.MASTER_DOMAIN)); + jexlVars.put("syncopeConf", confParamOps.list(AuthContextUtils.getDomain())); jexlVars.put("events", notification.getEvents()); List recipients = new ArrayList<>(); @@ -248,6 +241,7 @@ protected NotificationTask getNotificationTask( return task; } + @Transactional(readOnly = true) @Override public boolean notificationsAvailable( final String domain, diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java index 69a28f7737f..87b69d8bd24 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java @@ -36,6 +36,7 @@ import org.apache.syncope.common.lib.to.Item; import org.apache.syncope.common.lib.to.OrgUnit; import org.apache.syncope.common.lib.to.Provision; +import org.apache.syncope.common.lib.types.BackOffStrategy; import org.apache.syncope.common.lib.types.ExecStatus; import org.apache.syncope.common.lib.types.OpEvent; import org.apache.syncope.common.lib.types.ResourceOperation; @@ -73,6 +74,7 @@ import org.apache.syncope.core.provisioning.java.utils.MappingUtils; import org.apache.syncope.core.spring.implementation.ImplementationManager; import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.apache.syncope.core.spring.security.SecureRandomUtils; import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeBuilder; @@ -88,13 +90,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.retry.RetryException; -import org.springframework.retry.backoff.ExponentialBackOffPolicy; -import org.springframework.retry.backoff.ExponentialRandomBackOffPolicy; -import org.springframework.retry.backoff.FixedBackOffPolicy; -import org.springframework.retry.policy.SimpleRetryPolicy; -import org.springframework.retry.support.RetryTemplate; +import org.springframework.core.retry.RetryException; +import org.springframework.core.retry.RetryPolicy; +import org.springframework.core.retry.RetryTemplate; +import org.springframework.core.retry.Retryable; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.backoff.BackOff; +import org.springframework.util.backoff.ExponentialBackOff; +import org.springframework.util.backoff.FixedBackOff; @Transactional(rollbackFor = { Throwable.class }) public abstract class AbstractPropagationTaskExecutor implements PropagationTaskExecutor { @@ -415,91 +418,70 @@ protected Uid delete( } protected Optional retryTemplate(final ExternalResource resource) { - RetryTemplate retryTemplate = null; - - if (resource.getPropagationPolicy() != null) { - retryTemplate = retryTemplates.get(resource.getKey()); - if (retryTemplate == null) { - retryTemplate = new RetryTemplate(); - - SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); - retryPolicy.setMaxAttempts(resource.getPropagationPolicy().getMaxAttempts()); - retryTemplate.setRetryPolicy(retryPolicy); - - String[] params = resource.getPropagationPolicy().getBackOffParams().split(";"); - - switch (resource.getPropagationPolicy().getBackOffStrategy()) { - case EXPONENTIAL: - ExponentialBackOffPolicy eBackOffPolicy = new ExponentialBackOffPolicy(); - if (params.length > 0) { - try { - eBackOffPolicy.setInitialInterval(Long.parseLong(params[0])); - } catch (NumberFormatException e) { - LOG.error("Could not convert to long: {}", params[0], e); - } - } - if (params.length > 1) { - try { - eBackOffPolicy.setMaxInterval(Long.parseLong(params[1])); - } catch (NumberFormatException e) { - LOG.error("Could not convert to long: {}", params[1], e); - } - } - if (params.length > 2) { - try { - eBackOffPolicy.setMultiplier(Double.parseDouble(params[2])); - } catch (NumberFormatException e) { - LOG.error("Could not convert to double: {}", params[2], e); - } - } - retryTemplate.setBackOffPolicy(eBackOffPolicy); - break; - - case RANDOM: - ExponentialRandomBackOffPolicy erBackOffPolicy = new ExponentialRandomBackOffPolicy(); - if (params.length > 0) { - try { - erBackOffPolicy.setInitialInterval(Long.parseLong(params[0])); - } catch (NumberFormatException e) { - LOG.error("Could not convert to long: {}", params[0], e); - } - } - if (params.length > 1) { - try { - erBackOffPolicy.setMaxInterval(Long.parseLong(params[1])); - } catch (NumberFormatException e) { - LOG.error("Could not convert to long: {}", params[1], e); - } - } - if (params.length > 2) { - try { - erBackOffPolicy.setMultiplier(Double.parseDouble(params[2])); - } catch (NumberFormatException e) { - LOG.error("Could not convert to double: {}", params[2], e); - } - } - retryTemplate.setBackOffPolicy(erBackOffPolicy); - break; - - case FIXED: - default: - FixedBackOffPolicy fBackOffPolicy = new FixedBackOffPolicy(); - if (params.length > 0) { - try { - fBackOffPolicy.setBackOffPeriod(Long.parseLong(params[0])); - } catch (NumberFormatException e) { - LOG.error("Could not convert to long: {}", params[0], e); - } + if (resource.getPropagationPolicy() == null) { + return Optional.empty(); + } - } - retryTemplate.setBackOffPolicy(fBackOffPolicy); + RetryTemplate retryTemplate = retryTemplates.get(resource.getKey()); + if (retryTemplate != null) { + return Optional.of(retryTemplate); + } + + retryTemplate = new RetryTemplate(); + + String[] params = resource.getPropagationPolicy().getBackOffParams().split(";"); + + BackOff backOff = null; + switch (resource.getPropagationPolicy().getBackOffStrategy()) { + case EXPONENTIAL: + case RANDOM: + backOff = new ExponentialBackOff(); + ((ExponentialBackOff) backOff).setMaxAttempts(resource.getPropagationPolicy().getMaxAttempts()); + if (params.length > 0) { + try { + ((ExponentialBackOff) backOff).setInitialInterval(Long.parseLong(params[0])); + } catch (NumberFormatException e) { + LOG.error("Could not convert to long: {}", params[0], e); + } } + if (params.length > 1) { + try { + ((ExponentialBackOff) backOff).setMaxInterval(Long.parseLong(params[1])); + } catch (NumberFormatException e) { + LOG.error("Could not convert to long: {}", params[1], e); + } + } + if (params.length > 2) { + try { + ((ExponentialBackOff) backOff).setMultiplier(Double.parseDouble(params[2])); + } catch (NumberFormatException e) { + LOG.error("Could not convert to double: {}", params[2], e); + } + } + if (resource.getPropagationPolicy().getBackOffStrategy() == BackOffStrategy.RANDOM) { + ((ExponentialBackOff) backOff).setJitter(SecureRandomUtils.generateRandomLong(1, 1000)); + } + break; + + case FIXED: + default: + if (params.length > 0) { + try { + backOff = new FixedBackOff(Long.parseLong(params[0])); + ((FixedBackOff) backOff).setMaxAttempts(resource.getPropagationPolicy().getMaxAttempts()); + } catch (NumberFormatException e) { + LOG.error("Could not convert to long: {}", params[0], e); + } - retryTemplates.put(resource.getKey(), retryTemplate); - } + } } - return Optional.ofNullable(retryTemplate); + RetryPolicy.Builder builder = RetryPolicy.builder(); + Optional.ofNullable(backOff).ifPresent(builder::backOff); + retryTemplate.setRetryPolicy(builder.build()); + retryTemplates.put(resource.getKey(), retryTemplate); + + return Optional.of(retryTemplate); } @Override @@ -508,17 +490,27 @@ public TaskExec execute( final PropagationReporter reporter, final String executor) { - return retryTemplate(taskInfo.getResource()).map(rt -> rt.execute(context -> { - LOG.debug("#{} Propagation attempt", context.getRetryCount()); + return retryTemplate(taskInfo.getResource()).map(rt -> { + Mutable> exec = new MutableObject<>(); + try { + return rt.execute(new Retryable>() { - TaskExec exec = doExecute(taskInfo, reporter, executor); - if (context.getRetryCount() < taskInfo.getResource().getPropagationPolicy().getMaxAttempts() - 1 - && !ExecStatus.SUCCESS.name().equals(exec.getStatus())) { + @Override + public TaskExec execute() throws Throwable { + LOG.debug("New propagation attempt"); - throw new RetryException("Attempt #" + context.getRetryCount() + " failed"); + exec.setValue(doExecute(taskInfo, reporter, executor)); + if (!ExecStatus.SUCCESS.name().equals(exec.get().getStatus())) { + throw new IllegalStateException("Propagation attempt failed"); + } + return exec.get(); + } + }); + } catch (RetryException e) { + LOG.error("Could not propagate successfully, aborting", e); + return exec.get(); } - return exec; - })).orElseGet(() -> doExecute(taskInfo, reporter, executor)); + }).orElseGet(() -> doExecute(taskInfo, reporter, executor)); } protected boolean isFetchRemoteObj(final PropagationTaskInfo taskInfo) { diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java index d4c5f310e43..31be606649d 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java @@ -93,7 +93,7 @@ protected String evaluateGroupConnObjectLink(final String connObjectLinkTemplate JexlContext jexlContext = new JexlContextBuilder(). fields(group). plainAttrs(group.getPlainAttrs()). - derAttrs(group, derAttrHandler). + derAttrs(derAttrHandler.getValues(group)). build(); return jexlTools.evaluateExpression(connObjectLinkTemplate, jexlContext).toString(); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java index 04015988a31..dc2aeba999e 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java @@ -180,7 +180,8 @@ protected OpEvent.Outcome provision( if (!profile.getTask().isPerformCreate()) { LOG.debug("PullTask not configured for create"); - end(provision.getAnyType(), + end(Optional.empty(), + provision.getAnyType(), UnmatchingRule.toOp(rule), OpEvent.Outcome.SUCCESS, null, null, @@ -207,7 +208,8 @@ protected OpEvent.Outcome provision( if (profile.isDryRun()) { result.setKey(null); - end(provision.getAnyType(), + end(Optional.empty(), + provision.getAnyType(), UnmatchingRule.toOp(rule), OpEvent.Outcome.SUCCESS, null, @@ -263,7 +265,8 @@ protected OpEvent.Outcome provision( } } - end(provision.getAnyType(), + end(Optional.ofNullable(result.getKey()), + provision.getAnyType(), UnmatchingRule.toOp(rule), resultStatus, null, @@ -280,7 +283,8 @@ protected OpEvent.Outcome update( if (!profile.getTask().isPerformUpdate()) { LOG.debug("PullTask not configured for update"); - end(provision.getAnyType(), + end(Optional.empty(), + provision.getAnyType(), MatchingRule.toOp(MatchingRule.UPDATE), OpEvent.Outcome.SUCCESS, null, @@ -366,7 +370,8 @@ protected OpEvent.Outcome update( } } } - end(provision.getAnyType(), + end(Optional.of(result.getKey()), + provision.getAnyType(), MatchingRule.toOp(MatchingRule.UPDATE), resultStatus, before, @@ -389,7 +394,8 @@ protected OpEvent.Outcome deprovision( if (!profile.getTask().isPerformUpdate()) { LOG.debug("PullTask not configured for update"); - end(provision.getAnyType(), + end(Optional.empty(), + provision.getAnyType(), MatchingRule.toOp(matchingRule), OpEvent.Outcome.SUCCESS, null, @@ -484,7 +490,8 @@ protected OpEvent.Outcome deprovision( resultStatus = OpEvent.Outcome.FAILURE; } } - end(provision.getAnyType(), + end(Optional.of(result.getKey()), + provision.getAnyType(), MatchingRule.toOp(matchingRule), resultStatus, before, @@ -506,7 +513,8 @@ protected OpEvent.Outcome link( if (!profile.getTask().isPerformUpdate()) { LOG.debug("PullTask not configured for update"); - end(provision.getAnyType(), + end(Optional.empty(), + provision.getAnyType(), unlink ? MatchingRule.toOp(MatchingRule.UNLINK) : MatchingRule.toOp(MatchingRule.LINK), OpEvent.Outcome.SUCCESS, null, @@ -586,7 +594,8 @@ protected OpEvent.Outcome link( resultStatus = OpEvent.Outcome.FAILURE; } } - end(provision.getAnyType(), + end(Optional.of(result.getKey()), + provision.getAnyType(), unlink ? MatchingRule.toOp(MatchingRule.UNLINK) : MatchingRule.toOp(MatchingRule.LINK), resultStatus, before, @@ -607,7 +616,8 @@ protected OpEvent.Outcome delete( if (!profile.getTask().isPerformDelete()) { LOG.debug("PullTask not configured for delete"); - end(provision.getAnyType(), + end(Optional.empty(), + provision.getAnyType(), ResourceOperation.DELETE.name().toLowerCase(), OpEvent.Outcome.SUCCESS, null, @@ -666,7 +676,8 @@ protected OpEvent.Outcome delete( } } - end(provision.getAnyType(), + end(Optional.of(result.getKey()), + provision.getAnyType(), ResourceOperation.DELETE.name().toLowerCase(), resultStatus, before, @@ -708,7 +719,8 @@ protected OpEvent.Outcome ignore( profile.getResults().add(result); - end(provision.getAnyType(), + end(Optional.ofNullable(result.getKey()), + provision.getAnyType(), matching ? MatchingRule.toOp(MatchingRule.IGNORE) : UnmatchingRule.toOp(UnmatchingRule.IGNORE), OpEvent.Outcome.SUCCESS, null, @@ -875,6 +887,7 @@ protected OpEvent.Outcome doHandle( } protected void end( + final Optional key, final String anyType, final String event, final OpEvent.Outcome result, @@ -883,6 +896,8 @@ protected void end( final SyncDelta delta, final Object... furtherInput) { + key.ifPresent(k -> anyUtils().dao().evict(anyUtils().anyClass(), k)); + notificationManager.createTasks( profile.getExecutor(), OpEvent.CategoryType.PULL, diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java index 6a4bf6b0a6a..bf999ffce51 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java @@ -214,11 +214,8 @@ protected void provision(final Any any, final Boolean enable, final Provisioning protected void copyDynMembershipConds(final Any any, final AnyUR req) { if (any instanceof Group group && req instanceof GroupUR gur) { - Optional.ofNullable(group.getUDynMembership()). - ifPresent(udc -> gur.setUDynMembershipCond(udc.getFIQLCond())); - - group.getADynMemberships(). - forEach(adc -> gur.getADynMembershipConds().put(adc.getAnyType().getKey(), adc.getFIQLCond())); + group.getDynMemberships(). + forEach(adc -> gur.getDynMembershipConds().put(adc.getAnyType().getKey(), adc.getFIQLCond())); } } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java index 61087123ae8..f94b39f0465 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java @@ -65,12 +65,6 @@ public class DefaultRealmPullResultHandler extends AbstractRealmResultHandler implements RealmPullResultHandler { - protected static OpEvent.Outcome and(final OpEvent.Outcome left, final OpEvent.Outcome right) { - return left == OpEvent.Outcome.SUCCESS && right == OpEvent.Outcome.SUCCESS - ? OpEvent.Outcome.SUCCESS - : OpEvent.Outcome.FAILURE; - } - @Autowired protected InboundMatcher inboundMatcher; @@ -134,7 +128,6 @@ public boolean handle(final SyncDelta delta) { } protected void throwIgnoreProvisionException(final SyncDelta delta, final Exception exception) { - if (exception instanceof IgnoreProvisionException) { throw IgnoreProvisionException.class.cast(exception); } @@ -150,12 +143,15 @@ protected void throwIgnoreProvisionException(final SyncDelta delta, final Except } } - protected OpEvent.Outcome assign(final SyncDelta delta, final OrgUnit orgUnit) - throws JobExecutionException { - + protected OpEvent.Outcome assign(final SyncDelta delta, final OrgUnit orgUnit) throws JobExecutionException { if (!profile.getTask().isPerformCreate()) { LOG.debug("PullTask not configured for create"); - end(UnmatchingRule.toOp(UnmatchingRule.ASSIGN), OpEvent.Outcome.SUCCESS, null, null, delta); + end(Optional.empty(), + UnmatchingRule.toOp(UnmatchingRule.ASSIGN), + OpEvent.Outcome.SUCCESS, + null, + null, + delta); return OpEvent.Outcome.SUCCESS; } @@ -177,7 +173,12 @@ protected OpEvent.Outcome assign(final SyncDelta delta, final OrgUnit orgUnit) if (profile.isDryRun()) { result.setKey(null); - end(UnmatchingRule.toOp(UnmatchingRule.ASSIGN), OpEvent.Outcome.SUCCESS, null, null, delta); + end(Optional.empty(), + UnmatchingRule.toOp(UnmatchingRule.ASSIGN), + OpEvent.Outcome.SUCCESS, + null, + null, + delta); return OpEvent.Outcome.SUCCESS; } @@ -190,12 +191,15 @@ protected OpEvent.Outcome assign(final SyncDelta delta, final OrgUnit orgUnit) return create(realmTO, delta, UnmatchingRule.ASSIGN, result); } - protected OpEvent.Outcome provision(final SyncDelta delta, final OrgUnit orgUnit) - throws JobExecutionException { - + protected OpEvent.Outcome provision(final SyncDelta delta, final OrgUnit orgUnit) throws JobExecutionException { if (!profile.getTask().isPerformCreate()) { LOG.debug("PullTask not configured for create"); - end(UnmatchingRule.toOp(UnmatchingRule.PROVISION), OpEvent.Outcome.SUCCESS, null, null, delta); + end(Optional.empty(), + UnmatchingRule.toOp(UnmatchingRule.PROVISION), + OpEvent.Outcome.SUCCESS, + null, + null, + delta); return OpEvent.Outcome.SUCCESS; } @@ -216,7 +220,12 @@ protected OpEvent.Outcome provision(final SyncDelta delta, final OrgUnit orgUnit if (profile.isDryRun()) { result.setKey(null); - end(UnmatchingRule.toOp(UnmatchingRule.PROVISION), OpEvent.Outcome.SUCCESS, null, null, delta); + end(Optional.empty(), + UnmatchingRule.toOp(UnmatchingRule.PROVISION), + OpEvent.Outcome.SUCCESS, + null, + null, + delta); return OpEvent.Outcome.SUCCESS; } @@ -280,358 +289,360 @@ protected OpEvent.Outcome create( resultStatus = OpEvent.Outcome.FAILURE; } - end(UnmatchingRule.toOp(unmatchingRule), resultStatus, null, output, delta); + end(Optional.of(result.getKey()), UnmatchingRule.toOp(unmatchingRule), resultStatus, null, output, delta); return resultStatus; } - protected OpEvent.Outcome update(final SyncDelta delta, final List realms, final boolean inLink) + protected OpEvent.Outcome update(final SyncDelta delta, final Realm realm, final boolean inLink) throws JobExecutionException { if (!profile.getTask().isPerformUpdate()) { LOG.debug("PullTask not configured for update"); - end(MatchingRule.toOp(MatchingRule.UPDATE), OpEvent.Outcome.SUCCESS, null, null, delta); + end(Optional.empty(), MatchingRule.toOp(MatchingRule.UPDATE), OpEvent.Outcome.SUCCESS, null, null, delta); return OpEvent.Outcome.SUCCESS; } - LOG.debug("About to update {}", realms); - - OpEvent.Outcome global = OpEvent.Outcome.SUCCESS; - for (Realm realm : realms) { - LOG.debug("About to update {}", realm); - - ProvisioningReport result = new ProvisioningReport(); - result.setOperation(ResourceOperation.UPDATE); - result.setAnyType(SyncopeConstants.REALM_ANYTYPE); - result.setStatus(ProvisioningReport.Status.SUCCESS); - result.setKey(realm.getKey()); - result.setName(realm.getFullPath()); - - if (!profile.isDryRun()) { - OpEvent.Outcome resultStatus; - Object output; + LOG.debug("About to update {}", realm); - RealmTO before = binder.getRealmTO(realm, true); - try { - if (!inLink) { - for (InboundActions action : profile.getActions()) { - action.beforeUpdate(profile, delta, before, null); - } - } - - List beforeAttrs = propagationManager.prepareAttrs(realm); - - PropagationByResource propByRes = binder.update(realm, before); - Realm merged = realmDAO.save(realm); - RealmTO updated = binder.getRealmTO(merged, true); + ProvisioningReport result = new ProvisioningReport(); + result.setOperation(ResourceOperation.UPDATE); + result.setAnyType(SyncopeConstants.REALM_ANYTYPE); + result.setStatus(ProvisioningReport.Status.SUCCESS); + result.setKey(realm.getKey()); + result.setName(realm.getFullPath()); - List taskInfos = propagationManager.setAttributeDeltas( - propagationManager.createTasks(merged, propByRes, null), - beforeAttrs); - taskExecutor.execute(taskInfos, false, securityProperties.getAdminUser()); + OpEvent.Outcome resultStatus = OpEvent.Outcome.SUCCESS; + if (!profile.isDryRun()) { + Object output; + RealmTO before = binder.getRealmTO(realm, true); + try { + if (!inLink) { for (InboundActions action : profile.getActions()) { - action.after(profile, delta, updated, result); + action.beforeUpdate(profile, delta, before, null); } + } - output = updated; - resultStatus = OpEvent.Outcome.SUCCESS; - result.setName(updated.getFullPath()); + List beforeAttrs = propagationManager.prepareAttrs(realm); - LOG.debug("{} successfully updated", updated); - } catch (PropagationException e) { - // A propagation failure doesn't imply a pull failure. - // The propagation exception status will be reported into the propagation task execution. - LOG.error("Could not propagate Realm {}", delta.getUid().getUidValue(), e); - output = e; - resultStatus = OpEvent.Outcome.FAILURE; - } catch (Exception e) { - throwIgnoreProvisionException(delta, e); + PropagationByResource propByRes = binder.update(realm, before); + Realm merged = realmDAO.save(realm); + RealmTO updated = binder.getRealmTO(merged, true); - result.setStatus(ProvisioningReport.Status.FAILURE); - result.setMessage(ExceptionUtils.getRootCauseMessage(e)); - LOG.error("Could not update Realm {}", delta.getUid().getUidValue(), e); - output = e; - resultStatus = OpEvent.Outcome.FAILURE; + List taskInfos = propagationManager.setAttributeDeltas( + propagationManager.createTasks(merged, propByRes, null), + beforeAttrs); + taskExecutor.execute(taskInfos, false, securityProperties.getAdminUser()); + + for (InboundActions action : profile.getActions()) { + action.after(profile, delta, updated, result); } - end(MatchingRule.toOp(MatchingRule.UPDATE), resultStatus, before, output, delta); - global = and(global, resultStatus); + output = updated; + resultStatus = OpEvent.Outcome.SUCCESS; + result.setName(updated.getFullPath()); + + LOG.debug("{} successfully updated", updated); + } catch (PropagationException e) { + // A propagation failure doesn't imply a pull failure. + // The propagation exception status will be reported into the propagation task execution. + LOG.error("Could not propagate Realm {}", delta.getUid().getUidValue(), e); + output = e; + resultStatus = OpEvent.Outcome.FAILURE; + } catch (Exception e) { + throwIgnoreProvisionException(delta, e); + + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(ExceptionUtils.getRootCauseMessage(e)); + LOG.error("Could not update Realm {}", delta.getUid().getUidValue(), e); + output = e; + resultStatus = OpEvent.Outcome.FAILURE; } - profile.getResults().add(result); + end(Optional.of(realm.getKey()), + MatchingRule.toOp(MatchingRule.UPDATE), + resultStatus, + before, + output, + delta); } - return global; + profile.getResults().add(result); + + return resultStatus; } - protected OpEvent.Outcome deprovision(final SyncDelta delta, final List realms, final boolean unlink) + protected OpEvent.Outcome deprovision(final SyncDelta delta, final Realm realm, final boolean unlink) throws JobExecutionException { if (!profile.getTask().isPerformUpdate()) { LOG.debug("PullTask not configured for update"); - end(unlink - ? MatchingRule.toOp(MatchingRule.UNASSIGN) - : MatchingRule.toOp(MatchingRule.DEPROVISION), OpEvent.Outcome.SUCCESS, null, null, delta); + end(Optional.empty(), + unlink ? MatchingRule.toOp(MatchingRule.UNASSIGN) : MatchingRule.toOp(MatchingRule.DEPROVISION), + OpEvent.Outcome.SUCCESS, + null, + null, + delta); return OpEvent.Outcome.SUCCESS; } - LOG.debug("About to deprovision {}", realms); - - OpEvent.Outcome global = OpEvent.Outcome.SUCCESS; - for (Realm realm : realms) { - LOG.debug("About to unassign resource {}", realm); + LOG.debug("About to deprovision {}", realm); - ProvisioningReport result = new ProvisioningReport(); - result.setOperation(ResourceOperation.DELETE); - result.setAnyType(SyncopeConstants.REALM_ANYTYPE); - result.setStatus(ProvisioningReport.Status.SUCCESS); - result.setKey(realm.getKey()); - result.setName(realm.getFullPath()); - - if (!profile.isDryRun()) { - Object output; - OpEvent.Outcome resultStatus; + ProvisioningReport result = new ProvisioningReport(); + result.setOperation(ResourceOperation.DELETE); + result.setAnyType(SyncopeConstants.REALM_ANYTYPE); + result.setStatus(ProvisioningReport.Status.SUCCESS); + result.setKey(realm.getKey()); + result.setName(realm.getFullPath()); - RealmTO before = binder.getRealmTO(realm, true); - try { - if (unlink) { - for (InboundActions action : profile.getActions()) { - action.beforeUnassign(profile, delta, before); - } - } else { - for (InboundActions action : profile.getActions()) { - action.beforeDeprovision(profile, delta, before); - } - } + OpEvent.Outcome resultStatus = OpEvent.Outcome.SUCCESS; + if (!profile.isDryRun()) { + Object output; - PropagationByResource propByRes = new PropagationByResource<>(); - propByRes.add(ResourceOperation.DELETE, profile.getTask().getResource().getKey()); - taskExecutor.execute( - propagationManager.createTasks(realm, propByRes, null), - false, securityProperties.getAdminUser()); - - RealmTO realmTO; - if (unlink) { - realm.getResources().remove(profile.getTask().getResource()); - realmTO = binder.getRealmTO(realmDAO.save(realm), true); - } else { - realmTO = binder.getRealmTO(realm, true); + RealmTO before = binder.getRealmTO(realm, true); + try { + if (unlink) { + for (InboundActions action : profile.getActions()) { + action.beforeUnassign(profile, delta, before); } - output = realmTO; - + } else { for (InboundActions action : profile.getActions()) { - action.after(profile, delta, realmTO, result); + action.beforeDeprovision(profile, delta, before); } + } - resultStatus = OpEvent.Outcome.SUCCESS; - - LOG.debug("{} successfully updated", realm); - } catch (PropagationException e) { - // A propagation failure doesn't imply a pull failure. - // The propagation exception status will be reported into the propagation task execution. - LOG.error("Could not propagate Realm {}", delta.getUid().getUidValue(), e); - output = e; - resultStatus = OpEvent.Outcome.FAILURE; - } catch (Exception e) { - throwIgnoreProvisionException(delta, e); + PropagationByResource propByRes = new PropagationByResource<>(); + propByRes.add(ResourceOperation.DELETE, profile.getTask().getResource().getKey()); + taskExecutor.execute( + propagationManager.createTasks(realm, propByRes, null), + false, securityProperties.getAdminUser()); + + RealmTO realmTO; + if (unlink) { + realm.getResources().remove(profile.getTask().getResource()); + realmTO = binder.getRealmTO(realmDAO.save(realm), true); + } else { + realmTO = binder.getRealmTO(realm, true); + } + output = realmTO; - result.setStatus(ProvisioningReport.Status.FAILURE); - result.setMessage(ExceptionUtils.getRootCauseMessage(e)); - LOG.error("Could not update Realm {}", delta.getUid().getUidValue(), e); - output = e; - resultStatus = OpEvent.Outcome.FAILURE; + for (InboundActions action : profile.getActions()) { + action.after(profile, delta, realmTO, result); } - end(unlink - ? MatchingRule.toOp(MatchingRule.UNASSIGN) - : MatchingRule.toOp(MatchingRule.DEPROVISION), - resultStatus, before, output, delta); - global = and(global, resultStatus); + resultStatus = OpEvent.Outcome.SUCCESS; + + LOG.debug("{} successfully updated", realm); + } catch (PropagationException e) { + // A propagation failure doesn't imply a pull failure. + // The propagation exception status will be reported into the propagation task execution. + LOG.error("Could not propagate Realm {}", delta.getUid().getUidValue(), e); + output = e; + resultStatus = OpEvent.Outcome.FAILURE; + } catch (Exception e) { + throwIgnoreProvisionException(delta, e); + + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(ExceptionUtils.getRootCauseMessage(e)); + LOG.error("Could not update Realm {}", delta.getUid().getUidValue(), e); + output = e; + resultStatus = OpEvent.Outcome.FAILURE; } - profile.getResults().add(result); + end(Optional.of(result.getKey()), + unlink ? MatchingRule.toOp(MatchingRule.UNASSIGN) : MatchingRule.toOp(MatchingRule.DEPROVISION), + resultStatus, + before, + output, + delta); } - return global; + profile.getResults().add(result); + + return resultStatus; } - protected OpEvent.Outcome link(final SyncDelta delta, final List realms, final boolean unlink) + protected OpEvent.Outcome link(final SyncDelta delta, final Realm realm, final boolean unlink) throws JobExecutionException { if (!profile.getTask().isPerformUpdate()) { LOG.debug("PullTask not configured for update"); - end(unlink - ? MatchingRule.toOp(MatchingRule.UNLINK) - : MatchingRule.toOp(MatchingRule.LINK), OpEvent.Outcome.SUCCESS, null, null, delta); + end(Optional.empty(), + unlink ? MatchingRule.toOp(MatchingRule.UNLINK) : MatchingRule.toOp(MatchingRule.LINK), + OpEvent.Outcome.SUCCESS, + null, + null, + delta); return OpEvent.Outcome.SUCCESS; } - LOG.debug("About to link {}", realms); - - OpEvent.Outcome global = OpEvent.Outcome.SUCCESS; - for (Realm realm : realms) { - LOG.debug("About to unassign resource {}", realm); + LOG.debug("About to link {}", realm); - ProvisioningReport result = new ProvisioningReport(); - result.setOperation(ResourceOperation.NONE); - result.setAnyType(SyncopeConstants.REALM_ANYTYPE); - result.setStatus(ProvisioningReport.Status.SUCCESS); - result.setKey(realm.getKey()); - result.setName(realm.getFullPath()); + ProvisioningReport result = new ProvisioningReport(); + result.setOperation(ResourceOperation.NONE); + result.setAnyType(SyncopeConstants.REALM_ANYTYPE); + result.setStatus(ProvisioningReport.Status.SUCCESS); + result.setKey(realm.getKey()); + result.setName(realm.getFullPath()); - if (!profile.isDryRun()) { - Object output; - OpEvent.Outcome resultStatus; + OpEvent.Outcome resultStatus = OpEvent.Outcome.SUCCESS; + if (!profile.isDryRun()) { + Object output; - RealmTO before = binder.getRealmTO(realm, true); - try { - if (unlink) { - for (InboundActions action : profile.getActions()) { - action.beforeUnlink(profile, delta, before); - } - } else { - for (InboundActions action : profile.getActions()) { - action.beforeLink(profile, delta, before); - } + RealmTO before = binder.getRealmTO(realm, true); + try { + if (unlink) { + for (InboundActions action : profile.getActions()) { + action.beforeUnlink(profile, delta, before); } - - if (unlink) { - realm.getResources().remove(profile.getTask().getResource()); - } else { - realm.add(profile.getTask().getResource()); + } else { + for (InboundActions action : profile.getActions()) { + action.beforeLink(profile, delta, before); } - output = update(delta, List.of(realm), true); + } - resultStatus = OpEvent.Outcome.SUCCESS; + if (unlink) { + realm.getResources().remove(profile.getTask().getResource()); + } else { + realm.add(profile.getTask().getResource()); + } + output = update(delta, realm, true); - LOG.debug("{} successfully updated", realm); - } catch (PropagationException e) { - // A propagation failure doesn't imply a pull failure. - // The propagation exception status will be reported into the propagation task execution. - LOG.error("Could not propagate Realm {}", delta.getUid().getUidValue(), e); - output = e; - resultStatus = OpEvent.Outcome.FAILURE; - } catch (Exception e) { - throwIgnoreProvisionException(delta, e); + resultStatus = OpEvent.Outcome.SUCCESS; - result.setStatus(ProvisioningReport.Status.FAILURE); - result.setMessage(ExceptionUtils.getRootCauseMessage(e)); - LOG.error("Could not update Realm {}", delta.getUid().getUidValue(), e); - output = e; - resultStatus = OpEvent.Outcome.FAILURE; - } + LOG.debug("{} successfully updated", realm); + } catch (PropagationException e) { + // A propagation failure doesn't imply a pull failure. + // The propagation exception status will be reported into the propagation task execution. + LOG.error("Could not propagate Realm {}", delta.getUid().getUidValue(), e); + output = e; + resultStatus = OpEvent.Outcome.FAILURE; + } catch (Exception e) { + throwIgnoreProvisionException(delta, e); - end(unlink - ? MatchingRule.toOp(MatchingRule.UNLINK) - : MatchingRule.toOp(MatchingRule.LINK), - resultStatus, before, output, delta); - global = and(global, resultStatus); + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(ExceptionUtils.getRootCauseMessage(e)); + LOG.error("Could not update Realm {}", delta.getUid().getUidValue(), e); + output = e; + resultStatus = OpEvent.Outcome.FAILURE; } - profile.getResults().add(result); + end(Optional.of(result.getKey()), + unlink ? MatchingRule.toOp(MatchingRule.UNLINK) : MatchingRule.toOp(MatchingRule.LINK), + resultStatus, + before, + output, + delta); } - return global; - } + profile.getResults().add(result); - protected OpEvent.Outcome delete(final SyncDelta delta, final List realms) { + return resultStatus; + } + protected OpEvent.Outcome delete(final SyncDelta delta, final Realm realm) { if (!profile.getTask().isPerformDelete()) { LOG.debug("PullTask not configured for delete"); - end(ResourceOperation.DELETE.name().toLowerCase(), OpEvent.Outcome.SUCCESS, null, null, delta); + end(Optional.empty(), + ResourceOperation.DELETE.name().toLowerCase(), + OpEvent.Outcome.SUCCESS, + null, + null, + delta); return OpEvent.Outcome.SUCCESS; } - LOG.debug("About to delete {}", realms); - - OpEvent.Outcome global = OpEvent.Outcome.SUCCESS; - for (Realm realm : realms) { - Object output; - OpEvent.Outcome resultStatus = OpEvent.Outcome.FAILURE; + LOG.debug("About to delete {}", realm); - ProvisioningReport result = new ProvisioningReport(); + Object output; + OpEvent.Outcome resultStatus = OpEvent.Outcome.FAILURE; - RealmTO before = binder.getRealmTO(realm, true); - try { - result.setKey(realm.getKey()); - result.setName(realm.getFullPath()); - result.setOperation(ResourceOperation.DELETE); - result.setAnyType(SyncopeConstants.REALM_ANYTYPE); - result.setStatus(ProvisioningReport.Status.SUCCESS); + ProvisioningReport result = new ProvisioningReport(); - if (!profile.isDryRun()) { - for (InboundActions action : profile.getActions()) { - action.beforeDelete(profile, delta, before); - } + RealmTO before = binder.getRealmTO(realm, true); + try { + result.setKey(realm.getKey()); + result.setName(realm.getFullPath()); + result.setOperation(ResourceOperation.DELETE); + result.setAnyType(SyncopeConstants.REALM_ANYTYPE); + result.setStatus(ProvisioningReport.Status.SUCCESS); - try { - if (!realmSearchDAO.findChildren(realm).isEmpty()) { - throw SyncopeClientException.build(ClientExceptionType.RealmContains); - } + if (!profile.isDryRun()) { + for (InboundActions action : profile.getActions()) { + action.beforeDelete(profile, delta, before); + } - Set adminRealms = Set.of(realm.getFullPath()); - AnyCond keyCond = new AnyCond(AttrCond.Type.ISNOTNULL); - keyCond.setSchema("key"); - SearchCond allMatchingCond = SearchCond.of(keyCond); - long users = searchDAO.count( - realmDAO.getRoot(), true, adminRealms, allMatchingCond, AnyTypeKind.USER); - long groups = searchDAO.count( - realmDAO.getRoot(), true, adminRealms, allMatchingCond, AnyTypeKind.GROUP); - long anyObjects = searchDAO.count( - realmDAO.getRoot(), true, adminRealms, allMatchingCond, AnyTypeKind.ANY_OBJECT); - long macroTasks = taskDAO.findByRealm(realm).size(); - long clientApps = casSPClientAppDAO.findAllByRealm(realm).size() - + saml2SPClientAppDAO.findAllByRealm(realm).size() - + oidcRPClientAppDAO.findAllByRealm(realm).size(); - - if (users + groups + anyObjects + macroTasks + clientApps > 0) { - SyncopeClientException realmContains = - SyncopeClientException.build(ClientExceptionType.RealmContains); - realmContains.getElements().add(users + " user(s)"); - realmContains.getElements().add(groups + " group(s)"); - realmContains.getElements().add(anyObjects + " anyObject(s)"); - realmContains.getElements().add(macroTasks + " command task(s)"); - realmContains.getElements().add(clientApps + " client app(s)"); - throw realmContains; - } + try { + if (!realmSearchDAO.findChildren(realm).isEmpty()) { + throw SyncopeClientException.build(ClientExceptionType.RealmContains); + } - PropagationByResource propByRes = new PropagationByResource<>(); - propByRes.addAll( - ResourceOperation.DELETE, - realm.getResources().stream().map(ExternalResource::getKey).toList()); - List taskInfos = propagationManager.createTasks(realm, propByRes, null); - taskExecutor.execute(taskInfos, false, securityProperties.getAdminUser()); + Set adminRealms = Set.of(realm.getFullPath()); + AnyCond keyCond = new AnyCond(AttrCond.Type.ISNOTNULL); + keyCond.setSchema("key"); + SearchCond allMatchingCond = SearchCond.of(keyCond); + long users = searchDAO.count( + realmDAO.getRoot(), true, adminRealms, allMatchingCond, AnyTypeKind.USER); + long groups = searchDAO.count( + realmDAO.getRoot(), true, adminRealms, allMatchingCond, AnyTypeKind.GROUP); + long anyObjects = searchDAO.count( + realmDAO.getRoot(), true, adminRealms, allMatchingCond, AnyTypeKind.ANY_OBJECT); + long macroTasks = taskDAO.findByRealm(realm).size(); + long clientApps = casSPClientAppDAO.findAllByRealm(realm).size() + + saml2SPClientAppDAO.findAllByRealm(realm).size() + + oidcRPClientAppDAO.findAllByRealm(realm).size(); + + if (users + groups + anyObjects + macroTasks + clientApps > 0) { + SyncopeClientException realmContains = + SyncopeClientException.build(ClientExceptionType.RealmContains); + realmContains.getElements().add(users + " user(s)"); + realmContains.getElements().add(groups + " group(s)"); + realmContains.getElements().add(anyObjects + " anyObject(s)"); + realmContains.getElements().add(macroTasks + " command task(s)"); + realmContains.getElements().add(clientApps + " client app(s)"); + throw realmContains; + } - realmDAO.delete(realm); + PropagationByResource propByRes = new PropagationByResource<>(); + propByRes.addAll( + ResourceOperation.DELETE, + realm.getResources().stream().map(ExternalResource::getKey).toList()); + List taskInfos = propagationManager.createTasks(realm, propByRes, null); + taskExecutor.execute(taskInfos, false, securityProperties.getAdminUser()); - output = null; - resultStatus = OpEvent.Outcome.SUCCESS; + realmDAO.delete(realm); - for (InboundActions action : profile.getActions()) { - action.after(profile, delta, before, result); - } - } catch (Exception e) { - throwIgnoreProvisionException(delta, e); + output = null; + resultStatus = OpEvent.Outcome.SUCCESS; - result.setStatus(ProvisioningReport.Status.FAILURE); - result.setMessage(ExceptionUtils.getRootCauseMessage(e)); - LOG.error("Could not delete {}", realm, e); - output = e; + for (InboundActions action : profile.getActions()) { + action.after(profile, delta, before, result); } + } catch (Exception e) { + throwIgnoreProvisionException(delta, e); - end(ResourceOperation.DELETE.name().toLowerCase(), resultStatus, before, output, delta); - global = and(global, resultStatus); + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(ExceptionUtils.getRootCauseMessage(e)); + LOG.error("Could not delete {}", realm, e); + output = e; } - profile.getResults().add(result); - } catch (DelegatedAdministrationException e) { - LOG.error("Not allowed to read Realm {}", realm, e); - } catch (Exception e) { - LOG.error("Could not delete Realm {}", realm, e); + end(Optional.of(result.getKey()), + ResourceOperation.DELETE.name().toLowerCase(), + resultStatus, + before, + output, + delta); } + + profile.getResults().add(result); + } catch (DelegatedAdministrationException e) { + LOG.error("Not allowed to read Realm {}", realm, e); + } catch (Exception e) { + LOG.error("Could not delete Realm {}", realm, e); } - return global; + return resultStatus; } protected OpEvent.Outcome ignore(final SyncDelta delta, final boolean matching) { @@ -646,9 +657,12 @@ protected OpEvent.Outcome ignore(final SyncDelta delta, final boolean matching) profile.getResults().add(report); if (!profile.isDryRun()) { - end(matching - ? MatchingRule.toOp(MatchingRule.IGNORE) - : UnmatchingRule.toOp(UnmatchingRule.IGNORE), OpEvent.Outcome.SUCCESS, null, null, delta); + end(Optional.empty(), + matching ? MatchingRule.toOp(MatchingRule.IGNORE) : UnmatchingRule.toOp(UnmatchingRule.IGNORE), + OpEvent.Outcome.SUCCESS, + null, + null, + delta); return OpEvent.Outcome.SUCCESS; } @@ -671,23 +685,25 @@ protected OpEvent.Outcome doHandle(final SyncDelta delta, final OrgUnit orgUnit) LOG.debug("Match found for {} as {}: {}", finalDelta.getUid().getUidValue(), finalDelta.getObject().getObjectClass(), realms); + if (realms.isEmpty()) { + LOG.debug("Nothing to do"); + return OpEvent.Outcome.SUCCESS; + } + + Realm realm = realms.getFirst(); if (realms.size() > 1) { switch (profile.getConflictResolutionAction()) { case IGNORE: throw new IgnoreProvisionException("More than one match found for " + finalDelta.getObject().getUid().getUidValue() + ": " + realms); - case FIRSTMATCH: - realms = realms.subList(0, 1); - break; - case LASTMATCH: - realms = realms.subList(realms.size() - 1, realms.size()); + realm = realms.getLast(); break; + case FIRSTMATCH: default: - // keep keys unmodified - } + } } OpEvent.Outcome result = OpEvent.Outcome.SUCCESS; @@ -716,23 +732,23 @@ protected OpEvent.Outcome doHandle(final SyncDelta delta, final OrgUnit orgUnit) } else { switch (profile.getTask().getMatchingRule()) { case UPDATE: - result = update(finalDelta, realms, false); + result = update(finalDelta, realm, false); break; case DEPROVISION: - result = deprovision(finalDelta, realms, false); + result = deprovision(finalDelta, realm, false); break; case UNASSIGN: - result = deprovision(finalDelta, realms, true); + result = deprovision(finalDelta, realm, true); break; case LINK: - result = link(finalDelta, realms, false); + result = link(finalDelta, realm, false); break; case UNLINK: - result = link(finalDelta, realms, true); + result = link(finalDelta, realm, true); break; case IGNORE: @@ -746,13 +762,7 @@ protected OpEvent.Outcome doHandle(final SyncDelta delta, final OrgUnit orgUnit) break; case DELETE: - if (realms.isEmpty()) { - end(ResourceOperation.DELETE.name().toLowerCase(), OpEvent.Outcome.SUCCESS, null, null, - finalDelta); - LOG.debug("No match found for deletion"); - } else { - result = delete(finalDelta, realms); - } + result = delete(finalDelta, realm); break; default: @@ -765,12 +775,15 @@ protected OpEvent.Outcome doHandle(final SyncDelta delta, final OrgUnit orgUnit) } protected void end( + final Optional key, final String event, final OpEvent.Outcome result, final Object before, final Object output, final SyncDelta delta) { + key.ifPresent(realmDAO::evict); + notificationManager.createTasks( AuthContextUtils.getWho(), OpEvent.CategoryType.PULL, diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java index fe934d5b0f1..669202f5b03 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java @@ -219,7 +219,8 @@ protected OpEvent.Outcome handleLinkedAccount( break; case DELETE: - end(AnyTypeKind.USER.name(), + end(Optional.empty(), + AnyTypeKind.USER.name(), ResourceOperation.DELETE.name().toLowerCase(), OpEvent.Outcome.SUCCESS, null, @@ -242,7 +243,8 @@ protected OpEvent.Outcome deprovision( if (!profile.getTask().isPerformUpdate()) { LOG.debug("PullTask not configured for update"); - end(AnyTypeKind.USER.name(), + end(Optional.empty(), + AnyTypeKind.USER.name(), MatchingRule.toOp(MatchingRule.UPDATE), OpEvent.Outcome.SUCCESS, null, @@ -313,7 +315,8 @@ protected OpEvent.Outcome deprovision( resultStatus = OpEvent.Outcome.FAILURE; } - end(AnyTypeKind.USER.name(), + end(Optional.of(account.getOwner().getKey()), + AnyTypeKind.USER.name(), MatchingRule.toOp(matchingRule), resultStatus, before, @@ -335,7 +338,9 @@ protected OpEvent.Outcome provision( if (!profile.getTask().isPerformCreate()) { LOG.debug("PullTask not configured for create"); - end(AnyTypeKind.USER.name(), + end( + Optional.empty(), + AnyTypeKind.USER.name(), UnmatchingRule.toOp(rule), OpEvent.Outcome.SUCCESS, null, @@ -355,7 +360,8 @@ protected OpEvent.Outcome provision( if (profile.isDryRun()) { result.setKey(null); - end(AnyTypeKind.USER.name(), + end(Optional.empty(), + AnyTypeKind.USER.name(), UnmatchingRule.toOp(rule), OpEvent.Outcome.SUCCESS, null, @@ -451,7 +457,8 @@ protected OpEvent.Outcome provision( } } - end(AnyTypeKind.USER.name(), + end(Optional.of(user.getKey()), + AnyTypeKind.USER.name(), UnmatchingRule.toOp(rule), resultStatus, null, @@ -470,7 +477,8 @@ protected OpEvent.Outcome update( if (!profile.getTask().isPerformUpdate()) { LOG.debug("PullTask not configured for update"); - end(AnyTypeKind.USER.name(), + end(Optional.empty(), + AnyTypeKind.USER.name(), MatchingRule.toOp(MatchingRule.UPDATE), OpEvent.Outcome.SUCCESS, null, @@ -579,7 +587,8 @@ protected OpEvent.Outcome update( } } - end(AnyTypeKind.USER.name(), + end(Optional.of(account.getOwner().getKey()), + AnyTypeKind.USER.name(), MatchingRule.toOp(MatchingRule.UPDATE), resultStatus, before, @@ -598,7 +607,8 @@ protected OpEvent.Outcome delete( if (!profile.getTask().isPerformDelete()) { LOG.debug("PullTask not configured for delete"); - end(AnyTypeKind.USER.name(), + end(Optional.empty(), + AnyTypeKind.USER.name(), ResourceOperation.DELETE.name().toLowerCase(), OpEvent.Outcome.SUCCESS, null, @@ -663,7 +673,8 @@ protected OpEvent.Outcome delete( } } - end(AnyTypeKind.USER.name(), + end(Optional.of(account.getOwner().getKey()), + AnyTypeKind.USER.name(), ResourceOperation.DELETE.name().toLowerCase(), resultStatus, before, @@ -697,7 +708,8 @@ protected OpEvent.Outcome ignore( result.setMessage(message[0]); } - end(AnyTypeKind.USER.name(), + end(Optional.ofNullable(result.getKey()), + AnyTypeKind.USER.name(), matching ? MatchingRule.toOp(MatchingRule.IGNORE) : UnmatchingRule.toOp(UnmatchingRule.IGNORE), OpEvent.Outcome.SUCCESS, null, diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActions.java index 51edb543ee2..5feda50fa07 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActions.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActions.java @@ -53,7 +53,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Pageable; -import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; /** @@ -189,7 +188,6 @@ public void after( () -> LOG.warn("Could not find matching user for {}", membValue))); } - @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public void afterAll(final ProvisioningProfile profile) { List updateReqs = new ArrayList<>(); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncTaskSaver.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncTaskSaver.java index 9b459d30444..b8783731780 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncTaskSaver.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncTaskSaver.java @@ -27,7 +27,6 @@ import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; import org.apache.syncope.core.persistence.api.dao.TaskDAO; -import org.apache.syncope.core.persistence.api.dao.TaskExecDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtils; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.PlainSchema; @@ -50,8 +49,6 @@ public class LiveSyncTaskSaver { protected final TaskDAO taskDAO; - protected final TaskExecDAO taskExecDAO; - protected final TaskUtilsFactory taskUtilsFactory; protected final NotificationManager notificationManager; @@ -61,14 +58,12 @@ public class LiveSyncTaskSaver { public LiveSyncTaskSaver( final ExternalResourceDAO resourceDAO, final TaskDAO taskDAO, - final TaskExecDAO taskExecDAO, final TaskUtilsFactory taskUtilsFactory, final NotificationManager notificationManager, final AuditManager auditManager) { this.resourceDAO = resourceDAO; this.taskDAO = taskDAO; - this.taskExecDAO = taskExecDAO; this.taskUtilsFactory = taskUtilsFactory; this.notificationManager = notificationManager; this.auditManager = auditManager; @@ -103,9 +98,9 @@ public void save( execution.setEnd(OffsetDateTime.now()); if (hasToBeRegistered.apply(execution)) { - taskExecDAO.saveAndAdd(TaskType.LIVE_SYNC, task.getKey(), execution); + task.add(execution); + task = taskDAO.save(task); } - task = taskDAO.save(task); notificationManager.createTasks( execution.getExecutor(), diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java index cfe628e1168..fd5f5a3f541 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java @@ -18,11 +18,6 @@ */ package org.apache.syncope.core.provisioning.java.pushpull.stream; -import com.fasterxml.jackson.databind.MappingIterator; -import com.fasterxml.jackson.databind.SequenceWriter; -import com.fasterxml.jackson.dataformat.csv.CsvMapper; -import com.fasterxml.jackson.dataformat.csv.CsvParser; -import com.fasterxml.jackson.dataformat.csv.CsvSchema; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -57,6 +52,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; +import tools.jackson.databind.MappingIterator; +import tools.jackson.databind.SequenceWriter; +import tools.jackson.dataformat.csv.CsvMapper; +import tools.jackson.dataformat.csv.CsvReadFeature; +import tools.jackson.dataformat.csv.CsvSchema; public class CSVStreamConnector implements Connector, AutoCloseable { @@ -107,8 +107,9 @@ public void close() throws IOException { public MappingIterator> reader() throws IOException { synchronized (this) { if (reader == null) { - reader = new CsvMapper(). - enable(CsvParser.Feature.SKIP_EMPTY_LINES). + reader = CsvMapper.builder(). + enable(CsvReadFeature.SKIP_EMPTY_LINES). + build(). readerFor(Map.class).with(schemaBuilder.build()).readValues(in); } } @@ -117,7 +118,7 @@ public MappingIterator> reader() throws IOException { public List getColumns(final CSVPullSpec spec) throws IOException { List fromSpec = new ArrayList<>(); - ((CsvSchema) reader().getParserSchema()).forEach(column -> { + ((CsvSchema) reader().parserSchema()).forEach(column -> { if (!spec.getIgnoreColumns().contains(column.getName())) { fromSpec.add(column.getName()); } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java index 6f91109ab30..b7b8bb05d0e 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java @@ -293,8 +293,7 @@ public U getAnyUR( } updatedGroup.setUserOwner(originalGroup.getUserOwner()); updatedGroup.setGroupOwner(originalGroup.getGroupOwner()); - updatedGroup.setUDynMembershipCond(originalGroup.getUDynMembershipCond()); - updatedGroup.getADynMembershipConds().putAll(originalGroup.getADynMembershipConds()); + updatedGroup.getDynMembershipConds().putAll(originalGroup.getDynMembershipConds()); updatedGroup.getTypeExtensions().addAll(originalGroup.getTypeExtensions()); anyUR = (U) AnyOperations.diff(updatedGroup, originalGroup, true); diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DefaultMappingManagerTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DefaultMappingManagerTest.java index 95561f9e712..4c11d736585 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DefaultMappingManagerTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DefaultMappingManagerTest.java @@ -31,16 +31,17 @@ import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.DynGroupMembership; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; -import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.provisioning.api.MappingManager; import org.identityconnectors.common.security.SecurityUtil; @@ -58,6 +59,9 @@ public class DefaultMappingManagerTest extends AbstractTest { @Autowired private MappingManager mappingManager; + @Autowired + private AnyTypeDAO anyTypeDAO; + @Autowired private UserDAO userDAO; @@ -254,10 +258,11 @@ public void issueSYNCOPE1583() { Group group = groupDAO.findByName("root").orElseThrow(); assertNotNull(group); - UDynGroupMembership dynMembership = entityFactory.newEntity(UDynGroupMembership.class); + DynGroupMembership dynMembership = entityFactory.newEntity(DynGroupMembership.class); + dynMembership.setAnyType(anyTypeDAO.getUser()); dynMembership.setFIQLCond("cool==true"); dynMembership.setGroup(group); - group.setUDynMembership(dynMembership); + group.add(dynMembership); group = groupDAO.saveAndRefreshDynMemberships(group); assertNotNull(group); diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java index 64587ffded6..5a7570870ac 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java @@ -19,10 +19,9 @@ package org.apache.syncope.core.provisioning.java.data; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import org.apache.syncope.common.lib.SyncopeConstants; @@ -85,18 +84,12 @@ public static void unsetAuthContext() { public void issue42() { PlainSchema userId = plainSchemaDAO.findById("userId").orElseThrow(); - Set beforeUserIdMappings = new HashSet<>(); - for (ExternalResource res : resourceDAO.findAll()) { - if (res.getProvisionByAnyType(AnyTypeKind.USER.name()).isPresent() - && res.getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping() != null) { - - for (Item mapItem : res.getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping().getItems()) { - if (userId.getKey().equals(mapItem.getIntAttrName())) { - beforeUserIdMappings.add(mapItem); - } - } - } - } + Set beforeUserIdMappings = resourceDAO.findAll().stream(). + map(r -> r.getProvisionByAnyType(AnyTypeKind.USER.name())). + flatMap(Optional::stream). + flatMap(p -> p.getMapping().getItems().stream()). + filter(i -> userId.getKey().equals(i.getIntAttrName())). + collect(Collectors.toSet()); ResourceTO resourceTO = new ResourceTO(); resourceTO.setKey("resource-issue42"); @@ -122,29 +115,18 @@ public void issue42() { ExternalResource resource = binder.create(resourceTO); resource = resourceDAO.save(resource); entityManager.flush(); - assertNotNull(resource); - assertNotNull(resource.getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping()); - assertEquals(1, resource.getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping().getItems().size()); ExternalResource actual = resourceDAO.findById("resource-issue42").orElseThrow(); - entityManager.flush(); - assertNotNull(actual); assertEquals(resource, actual); - - userId = plainSchemaDAO.findById("userId").orElseThrow(); - - Set afterUserIdMappings = new HashSet<>(); - for (ExternalResource res : resourceDAO.findAll()) { - if (res.getProvisionByAnyType(AnyTypeKind.USER.name()).isPresent() - && res.getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping() != null) { - - for (Item mapItem : res.getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping().getItems()) { - if (userId.getKey().equals(mapItem.getIntAttrName())) { - afterUserIdMappings.add(mapItem); - } - } - } - } + assertEquals(1, actual.getProvisionByAnyType(AnyTypeKind.USER.name()).orElseThrow(). + getMapping().getItems().size()); + + Set afterUserIdMappings = resourceDAO.findAll().stream(). + map(r -> r.getProvisionByAnyType(AnyTypeKind.USER.name())). + flatMap(Optional::stream). + flatMap(p -> p.getMapping().getItems().stream()). + filter(i -> userId.getKey().equals(i.getIntAttrName())). + collect(Collectors.toSet()); assertEquals(beforeUserIdMappings.size(), afterUserIdMappings.size() - 1); } diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderTest.java index 78ffec858cf..ed0c60fef8a 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderTest.java @@ -151,8 +151,7 @@ public void relationshipWithAttr() { typeExt.getAuxClasses().add("other"); relTypeTO.getTypeExtensions().add(typeExt); - relationshipTypeDataBinder.update(relType, relTypeTO); - relType = relationshipTypeDAO.save(relType); + relType = relationshipTypeDAO.save(relationshipTypeDataBinder.update(relType, relTypeTO)); relTypeTO = relationshipTypeDataBinder.getRelationshipTypeTO(relType); assertEquals("other", relTypeTO.getTypeExtension(AnyTypeKind.USER.name()).get().getAuxClasses().getFirst()); diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java index a0aaaeb749b..87e91237972 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java @@ -21,7 +21,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import com.fasterxml.jackson.dataformat.csv.CsvSchema; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.List; @@ -45,6 +44,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; +import tools.jackson.dataformat.csv.CsvSchema; @Transactional public class StreamPullJobDelegateTest extends AbstractTest { diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java index 916dcf11459..8ac9ebfc57f 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java @@ -22,9 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.fasterxml.jackson.databind.MappingIterator; -import com.fasterxml.jackson.dataformat.csv.CsvMapper; -import com.fasterxml.jackson.dataformat.csv.CsvSchema; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; @@ -45,6 +42,9 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; +import tools.jackson.databind.MappingIterator; +import tools.jackson.dataformat.csv.CsvMapper; +import tools.jackson.dataformat.csv.CsvSchema; @Transactional public class StreamPushJobDelegateTest extends AbstractTest { diff --git a/core/provisioning-java/src/test/resources/core-test.properties b/core/provisioning-java/src/test/resources/core-test.properties index 10da9c173cd..23c11998f58 100644 --- a/core/provisioning-java/src/test/resources/core-test.properties +++ b/core/provisioning-java/src/test/resources/core-test.properties @@ -25,7 +25,7 @@ persistence.domain[0].jdbcDriver=org.postgresql.Driver persistence.domain[0].jdbcURL=${DB_URL} persistence.domain[0].dbUsername=${DB_USER} persistence.domain[0].dbPassword=${DB_PASSWORD} -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary +persistence.domain[0].databasePlatform=org.apache.syncope.core.persistence.jpa.hibernate.SyncopePostgreSQLDialect persistence.domain[0].poolMaxActive=20 persistence.domain[0].poolMinIdle=5 diff --git a/core/self-keymaster-starter/pom.xml b/core/self-keymaster-starter/pom.xml index fb38d204c3e..98f34c2f8b2 100644 --- a/core/self-keymaster-starter/pom.xml +++ b/core/self-keymaster-starter/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core Spring Boot Self Keymaster @@ -60,17 +60,18 @@ under the License. - org.apache.openjpa - openjpa-maven-plugin - true + org.hibernate.orm + hibernate-maven-plugin - ${rootpom.basedir}/core/persistence-jpa/src/main/resources/persistence-enhance.xml - org/apache/syncope/core/persistence/jpa/entity/**/*.class + + + ${project.build.outputDirectory}/org/apache/syncope/core/persistence/jpa/entity/ + + - enhancer - process-classes + enhance enhance diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/InternalConfParamHelper.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/InternalConfParamHelper.java index b256438298f..acc5f89843e 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/InternalConfParamHelper.java +++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/InternalConfParamHelper.java @@ -18,11 +18,9 @@ */ package org.apache.syncope.core.keymaster.internal; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.util.Map; import java.util.TreeMap; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.core.persistence.api.dao.NotFoundException; import org.apache.syncope.core.persistence.api.dao.keymaster.ConfParamDAO; import org.apache.syncope.core.persistence.api.entity.EntityFactory; @@ -30,12 +28,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Transactional; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; public class InternalConfParamHelper { protected static final Logger LOG = LoggerFactory.getLogger(InternalConfParamHelper.class); - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); protected final ConfParamDAO confParamDAO; @@ -52,7 +53,7 @@ public Map list() { confParamDAO.findAll().forEach(param -> { try { params.put(param.getKey(), MAPPER.treeToValue(param.getValue(), Object.class)); - } catch (JsonProcessingException e) { + } catch (JacksonException e) { LOG.error("While processing {}'s value", param.getKey(), e); } }); diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalConfParamOps.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalConfParamOps.java index a7fed0f856e..c407fbbb47c 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalConfParamOps.java +++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalConfParamOps.java @@ -18,20 +18,21 @@ */ package org.apache.syncope.core.keymaster.internal; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; -import java.io.IOException; import java.util.Map; import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Transactional; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; public class SelfKeymasterInternalConfParamOps implements ConfParamOps { protected static final Logger LOG = LoggerFactory.getLogger(ConfParamOps.class); - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); protected final InternalConfParamHelper helper; @@ -55,7 +56,7 @@ public T get(final String domain, final String key, final T defaultValue, fi try { return MAPPER.treeToValue(valueNode, reference); - } catch (IOException e) { + } catch (JacksonException e) { LOG.error("Could not deserialize {}", valueNode, e); return defaultValue; } diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/ConfParamServiceImpl.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/ConfParamServiceImpl.java index efd3edb4d74..a1810309d96 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/ConfParamServiceImpl.java +++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/ConfParamServiceImpl.java @@ -18,16 +18,17 @@ */ package org.apache.syncope.core.keymaster.rest.cxf.service; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; import jakarta.ws.rs.core.Response; -import java.io.IOException; import java.io.InputStream; import java.util.Map; import org.apache.syncope.common.keymaster.rest.api.service.ConfParamService; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.core.logic.ConfParamLogic; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; public class ConfParamServiceImpl implements ConfParamService { @@ -35,7 +36,7 @@ public class ConfParamServiceImpl implements ConfParamService { protected static final Logger LOG = LoggerFactory.getLogger(ConfParamService.class); - private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + private static final JsonMapper MAPPER = new SyncopeJsonMapper(); protected final ConfParamLogic logic; @@ -58,7 +59,7 @@ public void set(final String key, final InputStream value) { JsonNode valueNode = null; try { valueNode = MAPPER.readTree(value); - } catch (IOException e) { + } catch (JacksonException e) { LOG.error("Could not deserialize body as valid JSON", e); } diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/ConfParamLogic.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/ConfParamLogic.java index f7a0a6906c2..5b41a88ea0e 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/ConfParamLogic.java +++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/ConfParamLogic.java @@ -18,13 +18,13 @@ */ package org.apache.syncope.core.logic; -import com.fasterxml.jackson.databind.JsonNode; import java.lang.reflect.Method; import java.util.Map; import org.apache.syncope.common.lib.to.EntityTO; import org.apache.syncope.core.keymaster.internal.InternalConfParamHelper; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Transactional; +import tools.jackson.databind.JsonNode; public class ConfParamLogic extends AbstractTransactionalLogic { diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java index bce05a8b9cc..2aa43a642ec 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java +++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.starter; -import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import io.swagger.v3.oas.integration.api.OpenAPIConfiguration; import io.swagger.v3.oas.models.ExternalDocumentation; import io.swagger.v3.oas.models.media.Schema; @@ -90,6 +89,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; +import tools.jackson.jakarta.rs.json.JacksonJsonProvider; @EnableConfigurationProperties(KeymasterProperties.class) @Configuration(proxyBeanMethods = false) diff --git a/core/self-keymaster-starter/src/test/resources/core-debug.properties b/core/self-keymaster-starter/src/test/resources/core-debug.properties index 8af477ddffb..9c9e087ecf5 100644 --- a/core/self-keymaster-starter/src/test/resources/core-debug.properties +++ b/core/self-keymaster-starter/src/test/resources/core-debug.properties @@ -36,6 +36,6 @@ persistence.domain[0].jdbcDriver=org.postgresql.Driver persistence.domain[0].jdbcURL=jdbc:postgresql://${DB_CONTAINER_IP}:5432/syncope?stringtype=unspecified persistence.domain[0].dbUsername=syncope persistence.domain[0].dbPassword=syncope -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary +persistence.domain[0].databasePlatform=org.apache.syncope.core.persistence.jpa.hibernate.SyncopePostgreSQLDialect persistence.domain[0].poolMaxActive=20 persistence.domain[0].poolMinIdle=5 diff --git a/core/self-keymaster-starter/src/test/resources/log4j2.xml b/core/self-keymaster-starter/src/test/resources/log4j2.xml index 7bf54970d07..54793cc8f97 100644 --- a/core/self-keymaster-starter/src/test/resources/log4j2.xml +++ b/core/self-keymaster-starter/src/test/resources/log4j2.xml @@ -35,7 +35,7 @@ under the License. - + diff --git a/core/spring/pom.xml b/core/spring/pom.xml index d2dcb844ba9..0ef02189b2b 100644 --- a/core/spring/pom.xml +++ b/core/spring/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core Spring diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecureRandomUtils.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecureRandomUtils.java index 0b38f5c8326..4b78d52bd11 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecureRandomUtils.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecureRandomUtils.java @@ -78,8 +78,12 @@ public static String generateRandomNonAlphanumericChar(final char[] characters) }).get().generate(1); } - public static int generateRandomInt(final int startInclusive, final int endExclusive) { - return startInclusive + RANDOM.nextInt(endExclusive - startInclusive); + public static int generateRandomInt(final int origin, final int bound) { + return origin + RANDOM.nextInt(origin, bound); + } + + public static long generateRandomLong(final int origin, final int bound) { + return origin + RANDOM.nextLong(origin, bound); } public static UUID generateRandomUUID() { diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeGrantedAuthority.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeGrantedAuthority.java index d10b72c4767..0409907a6dc 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeGrantedAuthority.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeGrantedAuthority.java @@ -38,6 +38,7 @@ public class SyncopeGrantedAuthority implements GrantedAuthority { @JsonProperty private final String entitlement; + @JsonProperty private final Set realms = new TreeSet<>(); @JsonCreator diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java index 3c882ccb0a0..caf8e7bf75f 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.spring.security; -import com.fasterxml.jackson.core.type.TypeReference; import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSHeader; @@ -39,6 +38,7 @@ import org.slf4j.LoggerFactory; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.transaction.annotation.Transactional; +import tools.jackson.core.type.TypeReference; /** * Default implementation for internal JWT validation. diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java index d903a59a9ce..f382e7c0496 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java @@ -123,7 +123,7 @@ public Authentication authenticate(final Authentication authentication) { username.setValue(authResult.user().getUsername()); if (!authenticated) { - AuthContextUtils.runAsAdmin(domainKey, () -> provisioningManager.internalSuspend( + AuthContextUtils.runAsAdmin(domainKey, () -> provisioningManager.suspendOnAuthFailures( authResult.user().getKey(), securityProperties.getAdminUser(), "Failed authentication")); } } diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/MSEntraAccessTokenJWSVerifier.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/MSEntraAccessTokenJWSVerifier.java index b67c1d4700e..5de0dafe5e6 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/MSEntraAccessTokenJWSVerifier.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/MSEntraAccessTokenJWSVerifier.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.spring.security.jws; -import com.fasterxml.jackson.databind.json.JsonMapper; import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSHeader; @@ -47,8 +46,6 @@ public class MSEntraAccessTokenJWSVerifier implements JWSVerifier { protected static final Logger LOG = LoggerFactory.getLogger(MSEntraAccessTokenJWSVerifier.class); - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); - protected final Cache verifiersCache; public MSEntraAccessTokenJWSVerifier( diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/MSEntraJWSVerifierCacheLoader.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/MSEntraJWSVerifierCacheLoader.java index 71107c89400..84c9810cc6f 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/MSEntraJWSVerifierCacheLoader.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/MSEntraJWSVerifierCacheLoader.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.spring.security.jws; -import com.fasterxml.jackson.databind.json.JsonMapper; import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.JWSVerifier; import com.nimbusds.jose.crypto.ECDSAVerifier; @@ -44,6 +43,8 @@ import javax.cache.integration.CacheLoaderException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.json.JsonMapper; public class MSEntraJWSVerifierCacheLoader implements CacheLoader { @@ -69,8 +70,8 @@ protected String getOpenIDMetadataDocumentUrl() { protected String extractJwksUri(final String openIdMetadataDocument) { try { - return MAPPER.readTree(openIdMetadataDocument).get("jwks_uri").asText(); - } catch (IOException e) { + return MAPPER.readTree(openIdMetadataDocument).get("jwks_uri").asString(); + } catch (JacksonException e) { throw new IllegalArgumentException("Extracting value of 'jwks_url' key from OpenID Metadata JSON " + "document for Microsoft Entra failed:", e); } diff --git a/core/starter/pom.xml b/core/starter/pom.xml index fcbecef39cf..c973d6c0247 100644 --- a/core/starter/pom.xml +++ b/core/starter/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core Spring Boot Starter @@ -67,6 +67,10 @@ under the License. org.springframework.boot spring-boot-starter-actuator + + org.springframework.boot + spring-boot-health + org.springframework.boot @@ -76,6 +80,10 @@ under the License. com.lmax disruptor + + org.slf4j + jcl-over-slf4j + org.apache.syncope.core.idrepo diff --git a/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java b/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java index 45c89a29600..b204baa3ae3 100644 --- a/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java +++ b/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java @@ -51,26 +51,18 @@ import org.apache.syncope.core.starter.actuate.SyncopeCoreInfoContributor; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.actuate.mail.MailHealthIndicator; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration; -import org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveDataAutoConfiguration; -import org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveRepositoriesAutoConfiguration; -import org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration; -import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchClientAutoConfiguration; -import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration; -import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; -import org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration; import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; import org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.hibernate.autoconfigure.HibernateJpaAutoConfiguration; +import org.springframework.boot.http.converter.autoconfigure.HttpMessageConvertersAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration; +import org.springframework.boot.mail.health.MailHealthIndicator; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.PayloadApplicationEvent; @@ -82,22 +74,14 @@ @SpringBootApplication( exclude = { - ErrorMvcAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, OpenApiAutoConfiguration.class, DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, - SqlInitializationAutoConfiguration.class, HibernateJpaAutoConfiguration.class, JdbcTemplateAutoConfiguration.class, - Neo4jDataAutoConfiguration.class, - Neo4jRepositoriesAutoConfiguration.class, - Neo4jReactiveDataAutoConfiguration.class, - Neo4jReactiveRepositoriesAutoConfiguration.class, TaskExecutionAutoConfiguration.class, - TaskSchedulingAutoConfiguration.class, - ElasticsearchRestClientAutoConfiguration.class, - ElasticsearchClientAutoConfiguration.class }, + TaskSchedulingAutoConfiguration.class }, proxyBeanMethods = false) @EnableTransactionManagement @EnableCaching diff --git a/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/DomainsHealthIndicator.java b/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/DomainsHealthIndicator.java index 393e9d58986..73a420ea12f 100644 --- a/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/DomainsHealthIndicator.java +++ b/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/DomainsHealthIndicator.java @@ -23,9 +23,9 @@ import org.apache.syncope.core.persistence.api.DomainHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.actuate.health.Health; -import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.boot.actuate.health.Status; +import org.springframework.boot.health.contributor.Health; +import org.springframework.boot.health.contributor.HealthIndicator; +import org.springframework.boot.health.contributor.Status; public class DomainsHealthIndicator implements HealthIndicator { diff --git a/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/EntityCacheEndpoint.java b/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/EntityCacheEndpoint.java index 2587bf84775..7c8f83ce71f 100644 --- a/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/EntityCacheEndpoint.java +++ b/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/EntityCacheEndpoint.java @@ -45,22 +45,16 @@ public Map statistics() { @WriteOperation public void statistics(final @Selector String operation) { switch (operation) { - case "enable": - case "ENABLE": + case "enable", "ENABLE" -> entityCacheDAO.enableStatistics(); - break; - case "disable": - case "DISABLE": + case "disable", "DISABLE" -> entityCacheDAO.disableStatistics(); - break; - case "reset": - case "RESET": + case "reset", "RESET" -> entityCacheDAO.resetStatistics(); - break; - default: + default -> throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Unsupported Operation: " + operation); } } diff --git a/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicator.java b/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicator.java index f283369d60f..8efffb37576 100644 --- a/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicator.java +++ b/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicator.java @@ -30,9 +30,9 @@ import org.apache.syncope.core.spring.security.AuthContextUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.actuate.health.Health; -import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.boot.actuate.health.Status; +import org.springframework.boot.health.contributor.Health; +import org.springframework.boot.health.contributor.HealthIndicator; +import org.springframework.boot.health.contributor.Status; public class ExternalResourcesHealthIndicator implements HealthIndicator { diff --git a/core/starter/src/main/resources/core.properties b/core/starter/src/main/resources/core.properties index 23d8724c3c1..7d49610b82d 100644 --- a/core/starter/src/main/resources/core.properties +++ b/core/starter/src/main/resources/core.properties @@ -45,14 +45,14 @@ service.discovery.address=http://localhost:8080/syncope/rest/ # acceptable values: POSTGRESQL | MYSQL | MARIADB | ORACLE persistence.db-type=POSTGRESQL -persistence.remoteCommitProvider=sjvm +persistence.cacheProvider=com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider persistence.domain[0].key=Master persistence.domain[0].jdbcDriver=org.postgresql.Driver persistence.domain[0].jdbcURL=jdbc:postgresql://localhost:5432/syncope?stringtype=unspecified persistence.domain[0].dbUsername=syncope persistence.domain[0].dbPassword=syncope -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary +persistence.domain[0].databasePlatform=org.apache.syncope.core.persistence.jpa.hibernate.SyncopePostgreSQLDialect persistence.domain[0].poolMaxActive=20 persistence.domain[0].poolMinIdle=5 diff --git a/core/workflow-api/pom.xml b/core/workflow-api/pom.xml index 06491b5bea8..9f0f0bbf399 100644 --- a/core/workflow-api/pom.xml +++ b/core/workflow-api/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core Workflow API diff --git a/core/workflow-api/src/main/java/org/apache/syncope/core/workflow/api/UserWorkflowAdapter.java b/core/workflow-api/src/main/java/org/apache/syncope/core/workflow/api/UserWorkflowAdapter.java index 288d61e1dc4..3588c5c182c 100644 --- a/core/workflow-api/src/main/java/org/apache/syncope/core/workflow/api/UserWorkflowAdapter.java +++ b/core/workflow-api/src/main/java/org/apache/syncope/core/workflow/api/UserWorkflowAdapter.java @@ -95,7 +95,7 @@ UserWorkflowResult> create( * @param context context information * @return user just suspended and information whether to propagate suspension */ - Pair, Boolean> internalSuspend(String key, String updater, String context); + Pair, Boolean> suspendOnAuthFailures(String key, String updater, String context); /** * Reactivate an user. diff --git a/core/workflow-java/pom.xml b/core/workflow-java/pom.xml index f5006e1b182..c1dca81c8d0 100644 --- a/core/workflow-java/pom.xml +++ b/core/workflow-java/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-core - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Core Workflow Java @@ -59,6 +59,11 @@ under the License. + + com.github.ben-manes.caffeine + jcache + test + org.springframework.boot spring-boot-starter-validation @@ -112,6 +117,7 @@ under the License. ${project.build.directory}/test-classes file:${bundles.directory}/ true + slf4j diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java index 013af583a92..a114a935bc4 100644 --- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java +++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java @@ -371,7 +371,7 @@ public UserWorkflowResult suspend(final String key, final String updater } @Override - public Pair, Boolean> internalSuspend( + public Pair, Boolean> suspendOnAuthFailures( final String key, final String updater, final String context) { User user = userDAO.authFind(key); diff --git a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java index 6f16ea8120e..664db44d1d2 100644 --- a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java +++ b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java @@ -38,6 +38,7 @@ import org.apache.syncope.core.persistence.jpa.PGPersistenceContext; import org.apache.syncope.core.persistence.jpa.PersistenceContext; import org.apache.syncope.core.persistence.jpa.StartupDomainLoader; +import org.apache.syncope.core.provisioning.api.ConnectorManager; import org.apache.syncope.core.provisioning.api.ImplementationLookup; import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder; import org.apache.syncope.core.provisioning.api.data.GroupDataBinder; @@ -122,4 +123,9 @@ public DomainOps domainOps(final DomainRegistry domainRegistry) { public NotificationManager notificationManager() { return mock(NotificationManager.class); } + + @Bean + public ConnectorManager connectorManager() { + return mock(ConnectorManager.class); + } } diff --git a/docker/console/pom.xml b/docker/console/pom.xml index 4fcbf5eaf04..28e73272c41 100644 --- a/docker/console/pom.xml +++ b/docker/console/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-docker - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Docker Console diff --git a/docker/core/LICENSE b/docker/core/LICENSE index 6592203e1d9..1606c34f3db 100644 --- a/docker/core/LICENSE +++ b/docker/core/LICENSE @@ -576,11 +576,6 @@ This is licensed under the AL 2.0, see above. == -For Jakarta Messaging (https://projects.eclipse.org/projects/ee4j.messaging): -This is licensed under the EPL 1.0, see above. - -== - For Jakarta Mail (https://projects.eclipse.org/projects/ee4j.mail): This is licensed under the EPL 1.0, see above. @@ -616,6 +611,11 @@ This is licensed under the AL 2.0, see above. == +For Byte Buddy (https://bytebuddy.net/): +This is licensed under the AL 2.0, see above. + +== + For json-smart-v2 (https://github.com/netplex/json-smart-v2): This is licensed under the AL 2.0, see above. @@ -1102,11 +1102,6 @@ This is licensed under the EDL 1.0, see above. == -For Eclipse Angus - Mail (https://eclipse-ee4j.github.io/angus-mail/): -This is licensed under the EDL 1.0, see above. - -== - For Flowable (http://www.flowable.org/): This is licensed under the AL 2.0, see above. @@ -1255,6 +1250,16 @@ Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. +== + +For Hibernate models (https://github.com/hibernate/hibernate-models): +This is licensed under the AL 2.0, see above. + +== + +For Hibernate ORM (https://hibernate.org/orm/): +This is licensed under the AL 2.0, see above. + == For Hibernate Validator (https://hibernate.org/validator/): diff --git a/docker/core/NOTICE b/docker/core/NOTICE index 442198b521a..93a91916f21 100644 --- a/docker/core/NOTICE +++ b/docker/core/NOTICE @@ -114,12 +114,6 @@ Copyright (c) 2018,2020 Eclipse Foundation == -This product includes software developed by the Eclipse Project for Jakarta Messaging. -Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. -Copyright 2021 Contributors to the Eclipse Foundation - -== - This product includes software developed by the Eclipse Project for Jakarta Mail. Copyright (c) 1997, 2024 Oracle and/or its affiliates. All rights reserved. @@ -156,6 +150,11 @@ Copyright (c) 2002-2016 Joda.org. All Rights Reserved. == +This product includes software developed by Byte Buddy +Copyright 2014 - Present Rafael Winterhalter + +== + This product includes software developed by the ConnId project. Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. Copyright 2011-2016 Tirasa S.r.l. All rights reserved. @@ -222,11 +221,6 @@ Copyright (c) 1997, 2024 Oracle and/or its affiliates. All rights reserved. == -This product includes software developed by the Eclipse Angus Project. -Copyright (c) 1997, 2024 Oracle and/or its affiliates. All rights reserved. - -== - This product includes software developed by the Flowable project. == @@ -236,6 +230,16 @@ All content is the property of the respective authors or their employers. == +This product includes software developed by Hibernate models +Copyright: Red Hat Inc. and Hibernate Authors + +== + +This product includes software developed by Hibernate ORM +Copyright Red Hat Inc. and Hibernate Authors + +== + This product includes software developed by Hibernate Validator Copyright: Red Hat Inc. and Hibernate Authors diff --git a/docker/core/pom.xml b/docker/core/pom.xml index ca7a5761712..007b47a45f0 100644 --- a/docker/core/pom.xml +++ b/docker/core/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-docker - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Docker Core diff --git a/docker/core/src/main/resources/core-docker.properties b/docker/core/src/main/resources/core-docker.properties index f32d4e6059a..c172b505d82 100644 --- a/docker/core/src/main/resources/core-docker.properties +++ b/docker/core/src/main/resources/core-docker.properties @@ -23,7 +23,7 @@ service.discovery.address=${SERVICE_DISCOVERY_ADDRESS} security.anonymousUser=${ANONYMOUS_USER:anonymous} security.anonymousKey=${ANONYMOUS_KEY:anonymousKey} -persistence.remoteCommitProvider=${OPENJPA_REMOTE_COMMIT} +persistence.cacheProvider=${HIBERNATE_JCACHE_PROVIDER} spring.threads.virtual.enabled=true diff --git a/docker/core/src/main/resources/core-mariadb.properties b/docker/core/src/main/resources/core-mariadb.properties index 928f6589db6..4733a2b34eb 100644 --- a/docker/core/src/main/resources/core-mariadb.properties +++ b/docker/core/src/main/resources/core-mariadb.properties @@ -23,7 +23,7 @@ persistence.domain[0].jdbcDriver=org.mariadb.jdbc.Driver persistence.domain[0].jdbcURL=${DB_URL} persistence.domain[0].dbUsername=${DB_USER} persistence.domain[0].dbPassword=${DB_PASSWORD} -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3) +persistence.domain[0].databasePlatform=org.hibernate.dialect.MariaDBDialect persistence.domain[0].orm=META-INF/mariadb/spring-orm.xml persistence.domain[0].poolMaxActive=${DB_POOL_MAX} persistence.domain[0].poolMinIdle=${DB_POOL_MIN} diff --git a/docker/core/src/main/resources/core-mysql.properties b/docker/core/src/main/resources/core-mysql.properties index d4b73b5db65..84fb2bbcd96 100644 --- a/docker/core/src/main/resources/core-mysql.properties +++ b/docker/core/src/main/resources/core-mysql.properties @@ -23,7 +23,7 @@ persistence.domain[0].jdbcDriver=com.mysql.cj.jdbc.Driver persistence.domain[0].jdbcURL=${DB_URL} persistence.domain[0].dbUsername=${DB_USER} persistence.domain[0].dbPassword=${DB_PASSWORD} -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3,useSetStringForClobs=true) +persistence.domain[0].databasePlatform=org.hibernate.dialect.MySQLDialect persistence.domain[0].orm=META-INF/mysql/spring-orm.xml persistence.domain[0].poolMaxActive=${DB_POOL_MAX} persistence.domain[0].poolMinIdle=${DB_POOL_MIN} diff --git a/docker/core/src/main/resources/core-oracle.properties b/docker/core/src/main/resources/core-oracle.properties index 1df1c6e56ec..6635d154328 100644 --- a/docker/core/src/main/resources/core-oracle.properties +++ b/docker/core/src/main/resources/core-oracle.properties @@ -24,7 +24,7 @@ persistence.domain[0].jdbcURL=${DB_URL} persistence.domain[0].dbSchema=${DB_SCHEMA} persistence.domain[0].dbUsername=${DB_USER} persistence.domain[0].dbPassword=${DB_PASSWORD} -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.OracleDictionary +persistence.domain[0].databasePlatform=org.hibernate.dialect.OracleDialect persistence.domain[0].orm=META-INF/oracle/spring-orm.xml persistence.domain[0].poolMaxActive=${DB_POOL_MAX} persistence.domain[0].poolMinIdle=${DB_POOL_MIN} diff --git a/docker/core/src/main/resources/core-postgresql.properties b/docker/core/src/main/resources/core-postgresql.properties index 82fb6ee0393..f512e75d865 100644 --- a/docker/core/src/main/resources/core-postgresql.properties +++ b/docker/core/src/main/resources/core-postgresql.properties @@ -20,6 +20,6 @@ persistence.domain[0].jdbcDriver=org.postgresql.Driver persistence.domain[0].jdbcURL=${DB_URL} persistence.domain[0].dbUsername=${DB_USER} persistence.domain[0].dbPassword=${DB_PASSWORD} -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary +persistence.domain[0].databasePlatform=org.apache.syncope.core.persistence.jpa.hibernate.SyncopePostgreSQLDialect persistence.domain[0].poolMaxActive=${DB_POOL_MAX} persistence.domain[0].poolMinIdle=${DB_POOL_MIN} diff --git a/docker/core/src/main/resources/log4j2.xml b/docker/core/src/main/resources/log4j2.xml index 7bf54970d07..afa800dcd76 100644 --- a/docker/core/src/main/resources/log4j2.xml +++ b/docker/core/src/main/resources/log4j2.xml @@ -32,16 +32,41 @@ under the License. - + - + - + + + + + + + + + + + + + + + + @@ -54,7 +79,10 @@ under the License. - + + + + @@ -64,7 +92,7 @@ under the License. - + @@ -104,13 +132,22 @@ under the License. + + + - + + + + + + + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.slf4j slf4j-simple diff --git a/ext/openfga/client-openfga/src/main/java/org/apache/syncope/ext/openfga/client/OpenFGAHealthIndicator.java b/ext/openfga/client-openfga/src/main/java/org/apache/syncope/ext/openfga/client/OpenFGAHealthIndicator.java index 3968022bf7e..bc04527d410 100644 --- a/ext/openfga/client-openfga/src/main/java/org/apache/syncope/ext/openfga/client/OpenFGAHealthIndicator.java +++ b/ext/openfga/client-openfga/src/main/java/org/apache/syncope/ext/openfga/client/OpenFGAHealthIndicator.java @@ -23,9 +23,9 @@ import org.apache.commons.lang3.mutable.MutableObject; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.ext.openfga.client.model.ListStoresResponse; -import org.springframework.boot.actuate.health.Health; -import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.boot.actuate.health.Status; +import org.springframework.boot.health.contributor.Health; +import org.springframework.boot.health.contributor.HealthIndicator; +import org.springframework.boot.health.contributor.Status; public class OpenFGAHealthIndicator implements HealthIndicator { diff --git a/ext/openfga/pom.xml b/ext/openfga/pom.xml index 89726a9fd09..5d07a4f8927 100644 --- a/ext/openfga/pom.xml +++ b/ext/openfga/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-ext - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: OpenFGA diff --git a/ext/openfga/provisioning-java/pom.xml b/ext/openfga/provisioning-java/pom.xml index 9e01f42e4f9..349c20debb9 100644 --- a/ext/openfga/provisioning-java/pom.xml +++ b/ext/openfga/provisioning-java/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-openfga - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: OpenFGA Provisioning Java diff --git a/ext/opensearch/client-opensearch/pom.xml b/ext/opensearch/client-opensearch/pom.xml index acd96cace07..7677d742c01 100644 --- a/ext/opensearch/client-opensearch/pom.xml +++ b/ext/opensearch/client-opensearch/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-opensearch - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: OpenSearch Client @@ -53,9 +53,19 @@ under the License. opensearch-java + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + org.springframework.boot - spring-boot-starter-actuator + spring-boot-health diff --git a/ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/OpenSearchClientContext.java b/ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/OpenSearchClientContext.java index 3a36aaf107f..53ce8403e42 100644 --- a/ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/OpenSearchClientContext.java +++ b/ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/OpenSearchClientContext.java @@ -28,9 +28,9 @@ import org.opensearch.client.opensearch.OpenSearchClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.actuate.health.HealthContributor; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.health.contributor.HealthContributor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; diff --git a/ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/SyncopeOpenSearchHealthContributor.java b/ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/SyncopeOpenSearchHealthContributor.java index d163fa4b672..2307086bbda 100644 --- a/ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/SyncopeOpenSearchHealthContributor.java +++ b/ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/SyncopeOpenSearchHealthContributor.java @@ -20,8 +20,8 @@ import org.opensearch.client.opensearch.OpenSearchClient; import org.opensearch.client.opensearch.cluster.HealthResponse; -import org.springframework.boot.actuate.health.Health; -import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.health.contributor.Health; +import org.springframework.boot.health.contributor.HealthIndicator; public class SyncopeOpenSearchHealthContributor implements HealthIndicator { diff --git a/ext/opensearch/persistence/pom.xml b/ext/opensearch/persistence/pom.xml index 656546c3b7e..1ef71ac3526 100644 --- a/ext/opensearch/persistence/pom.xml +++ b/ext/opensearch/persistence/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-opensearch - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: OpenSearch Persistence diff --git a/ext/opensearch/pom.xml b/ext/opensearch/pom.xml index 8c52dd7d966..301512fcba0 100644 --- a/ext/opensearch/pom.xml +++ b/ext/opensearch/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-ext - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: OpenSearch diff --git a/ext/opensearch/provisioning-java/pom.xml b/ext/opensearch/provisioning-java/pom.xml index 639129ee7d6..195b7fdce3f 100644 --- a/ext/opensearch/provisioning-java/pom.xml +++ b/ext/opensearch/provisioning-java/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-opensearch - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: OpenSearch Provisioning Java diff --git a/ext/pom.xml b/ext/pom.xml index 7f99a3cb508..0669f9f6a00 100644 --- a/ext/pom.xml +++ b/ext/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext diff --git a/ext/saml2sp4ui/client-common-ui/pom.xml b/ext/saml2sp4ui/client-common-ui/pom.xml index ffa8652b19e..0dec5f82fbe 100644 --- a/ext/saml2sp4ui/client-common-ui/pom.xml +++ b/ext/saml2sp4ui/client-common-ui/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-saml2sp4ui - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SAML2 SP4UI Common UI diff --git a/ext/saml2sp4ui/client-common-ui/src/main/java/org/apache/syncope/client/ui/commons/resources/saml2sp4ui/AssertionConsumerResource.java b/ext/saml2sp4ui/client-common-ui/src/main/java/org/apache/syncope/client/ui/commons/resources/saml2sp4ui/AssertionConsumerResource.java index bebb656c076..21cf30d9dcf 100644 --- a/ext/saml2sp4ui/client-common-ui/src/main/java/org/apache/syncope/client/ui/commons/resources/saml2sp4ui/AssertionConsumerResource.java +++ b/ext/saml2sp4ui/client-common-ui/src/main/java/org/apache/syncope/client/ui/commons/resources/saml2sp4ui/AssertionConsumerResource.java @@ -19,8 +19,6 @@ package org.apache.syncope.client.ui.commons.resources.saml2sp4ui; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.json.JsonMapper; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.client.ui.commons.BaseSession; import org.apache.syncope.client.ui.commons.SAML2SP4UIConstants; @@ -32,18 +30,22 @@ import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.request.mapper.parameter.PageParameters; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.json.JsonMapper; public abstract class AssertionConsumerResource extends AbstractSAML2SP4UIResource { private static final long serialVersionUID = 3858609271031003370L; protected static final JsonMapper MAPPER = - JsonMapper.builder().findAndAddModules().serializationInclusion(JsonInclude.Include.NON_EMPTY).build(); + JsonMapper.builder().findAndAddModules(). + changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_EMPTY)). + changeDefaultPropertyInclusion(incl -> incl.withContentInclusion(JsonInclude.Include.NON_EMPTY)). + build(); protected abstract Class getLoginPageClass(); - protected abstract Pair, PageParameters> getSelfRegInfo(UserTO newUser) - throws JsonProcessingException; + protected abstract Pair, PageParameters> getSelfRegInfo(UserTO newUser); @Override protected ResourceResponse newResourceResponse(final Attributes attributes) { @@ -58,7 +60,7 @@ protected ResourceResponse newResourceResponse(final Attributes attributes) { try { Pair, PageParameters> selfRegInfo = getSelfRegInfo(newUser); throw new RestartResponseException(selfRegInfo.getLeft(), selfRegInfo.getRight()); - } catch (JsonProcessingException e) { + } catch (JacksonException e) { LOG.error("Could not serialize new user {}", newUser, e); throw new WicketRuntimeException(e); } diff --git a/ext/saml2sp4ui/client-console/pom.xml b/ext/saml2sp4ui/client-console/pom.xml index f7fb0c93068..8012ddef49b 100644 --- a/ext/saml2sp4ui/client-console/pom.xml +++ b/ext/saml2sp4ui/client-console/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-saml2sp4ui - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SAML2 SP4UI Client Console diff --git a/ext/saml2sp4ui/client-enduser/pom.xml b/ext/saml2sp4ui/client-enduser/pom.xml index b889131e53a..d7856001bae 100644 --- a/ext/saml2sp4ui/client-enduser/pom.xml +++ b/ext/saml2sp4ui/client-enduser/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-saml2sp4ui - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SAML2 SP4UI Client Enduser diff --git a/ext/saml2sp4ui/client-enduser/src/main/java/org/apache/syncope/client/enduser/resources/saml2sp4ui/EnduserAssertionConsumerResource.java b/ext/saml2sp4ui/client-enduser/src/main/java/org/apache/syncope/client/enduser/resources/saml2sp4ui/EnduserAssertionConsumerResource.java index b848379144e..12d4337536e 100644 --- a/ext/saml2sp4ui/client-enduser/src/main/java/org/apache/syncope/client/enduser/resources/saml2sp4ui/EnduserAssertionConsumerResource.java +++ b/ext/saml2sp4ui/client-enduser/src/main/java/org/apache/syncope/client/enduser/resources/saml2sp4ui/EnduserAssertionConsumerResource.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.client.enduser.resources.saml2sp4ui; -import com.fasterxml.jackson.core.JsonProcessingException; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.client.enduser.pages.SAML2SPLogin; import org.apache.syncope.client.enduser.pages.SelfRegistration; @@ -42,8 +41,7 @@ protected Class getLoginPageClass() { } @Override - protected Pair, PageParameters> getSelfRegInfo(final UserTO newUser) - throws JsonProcessingException { + protected Pair, PageParameters> getSelfRegInfo(final UserTO newUser) { return Pair.of(SelfRegistration.class, new PageParameters().add(SelfRegistration.NEW_USER_PARAM, MAPPER.writeValueAsString(newUser))); } diff --git a/ext/saml2sp4ui/common-lib/pom.xml b/ext/saml2sp4ui/common-lib/pom.xml index f7ba06a14f8..84ea72efa49 100644 --- a/ext/saml2sp4ui/common-lib/pom.xml +++ b/ext/saml2sp4ui/common-lib/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-saml2sp4ui - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SAML2 SP4UI Common Lib diff --git a/ext/saml2sp4ui/logic/pom.xml b/ext/saml2sp4ui/logic/pom.xml index e2b903a5cf3..b6f8a370914 100644 --- a/ext/saml2sp4ui/logic/pom.xml +++ b/ext/saml2sp4ui/logic/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-saml2sp4ui - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SAML2 SP4UI Logic diff --git a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java index 458100b6d7a..be22b719020 100644 --- a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java +++ b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java @@ -285,7 +285,7 @@ public SAML2Request createLoginRequest( @Override public Optional getRedirectionAction(final CallContext ctx) { - this.saml2ObjectBuilder = new SAML2AuthnRequestBuilder() { + saml2Client.getConfiguration().setSamlAuthnRequestBuilder(new SAML2AuthnRequestBuilder() { @Override public AuthnRequest build(final SAML2MessageContext context) { @@ -295,7 +295,7 @@ public AuthnRequest build(final SAML2MessageContext context) { ifPresent(provider -> authnRequest.setRequestedAuthnContext(provider.get())); return authnRequest; } - }; + }); return super.getRedirectionAction(ctx); } }); diff --git a/ext/saml2sp4ui/persistence-api/pom.xml b/ext/saml2sp4ui/persistence-api/pom.xml index 170633be20a..73b02fc2edd 100644 --- a/ext/saml2sp4ui/persistence-api/pom.xml +++ b/ext/saml2sp4ui/persistence-api/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-saml2sp4ui - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SAML2 SP4UI Persistence API diff --git a/ext/saml2sp4ui/persistence-jpa/pom.xml b/ext/saml2sp4ui/persistence-jpa/pom.xml index 4e4fa0d7df0..a9eb91e8c5c 100644 --- a/ext/saml2sp4ui/persistence-jpa/pom.xml +++ b/ext/saml2sp4ui/persistence-jpa/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-saml2sp4ui - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SAML2 SP4UI Persistence JPA @@ -53,17 +53,30 @@ under the License. - org.apache.openjpa - openjpa-maven-plugin - true + org.hibernate.orm + hibernate-maven-plugin - ${rootpom.basedir}/core/persistence-jpa/src/main/resources/persistence-enhance.xml - org/apache/syncope/core/persistence/jpa/entity/**/*.class + + + ${project.build.outputDirectory}/org/apache/syncope/core/persistence/jpa/entity/ + + + + + org.apache.syncope.core + syncope-core-persistence-jpa + ${project.version} + + + org.apache.syncope.ext.saml2sp4ui + syncope-ext-saml2sp4ui-persistence-api + ${project.version} + + - enhancer - process-classes + enhance enhance @@ -84,37 +97,4 @@ under the License. - - - - sqlgen - - - true - - - - clean verify - - - - org.apache.openjpa - openjpa-maven-plugin - true - - - sqlgen - process-classes - - sql - - - - - - - - - - diff --git a/ext/saml2sp4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASAML2SP4UIIdP.java b/ext/saml2sp4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASAML2SP4UIIdP.java index a1b72eb950c..9b0511369d1 100644 --- a/ext/saml2sp4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASAML2SP4UIIdP.java +++ b/ext/saml2sp4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASAML2SP4UIIdP.java @@ -18,11 +18,11 @@ */ package org.apache.syncope.core.persistence.jpa.entity; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.Basic; import jakarta.persistence.Cacheable; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.JoinColumn; @@ -30,13 +30,7 @@ import jakarta.persistence.Lob; import jakarta.persistence.ManyToMany; import jakarta.persistence.OneToOne; -import jakarta.persistence.PostLoad; -import jakarta.persistence.PostPersist; -import jakarta.persistence.PostUpdate; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; @@ -49,7 +43,7 @@ import org.apache.syncope.core.persistence.api.entity.SAML2SP4UIIdP; import org.apache.syncope.core.persistence.api.entity.SAML2SP4UIUserTemplate; import org.apache.syncope.core.persistence.api.validation.SAML2SP4UIIdPCheck; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.persistence.jpa.converters.ItemListConverter; @Entity @Table(name = JPASAML2SP4UIIdP.TABLE) @@ -89,11 +83,9 @@ public class JPASAML2SP4UIIdP extends AbstractGeneratedKeyEntity implements SAML @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "idp") private JPASAML2SP4UIUserTemplate userTemplate; + @Convert(converter = ItemListConverter.class) @Lob - private String items; - - @Transient - private final List itemList = new ArrayList<>(); + private List items = new ArrayList<>(); @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "SAML2IdP4UIAction", @@ -199,7 +191,7 @@ public void setUserTemplate(final SAML2SP4UIUserTemplate userTemplate) { @Override public List getItems() { - return itemList; + return items; } @Override @@ -237,32 +229,4 @@ public void setRequestedAuthnContextProvider(final Implementation requestedAuthn SAML2SP4UIImplementationType.REQUESTED_AUTHN_CONTEXT_PROVIDER); this.requestedAuthnContextProvider = (JPAImplementation) requestedAuthnContextProvider; } - - protected void json2list(final boolean clearFirst) { - if (clearFirst) { - getItems().clear(); - } - if (items != null) { - getItems().addAll( - POJOHelper.deserialize(items, new TypeReference>() { - })); - } - } - - @PostLoad - public void postLoad() { - json2list(false); - } - - @PostPersist - @PostUpdate - public void postSave() { - json2list(true); - } - - @PrePersist - @PreUpdate - public void list2json() { - items = POJOHelper.serialize(getItems()); - } } diff --git a/ext/saml2sp4ui/pom.xml b/ext/saml2sp4ui/pom.xml index 5ca7165ac17..c59f595a7f0 100644 --- a/ext/saml2sp4ui/pom.xml +++ b/ext/saml2sp4ui/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-ext - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SAML2 SP4UI diff --git a/ext/saml2sp4ui/provisioning-api/pom.xml b/ext/saml2sp4ui/provisioning-api/pom.xml index f3d2373125c..cbe4223a2a4 100644 --- a/ext/saml2sp4ui/provisioning-api/pom.xml +++ b/ext/saml2sp4ui/provisioning-api/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-saml2sp4ui - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SAML2 SP4UI Provisioning API diff --git a/ext/saml2sp4ui/provisioning-java/pom.xml b/ext/saml2sp4ui/provisioning-java/pom.xml index 1c1def523d5..cf7db309faf 100644 --- a/ext/saml2sp4ui/provisioning-java/pom.xml +++ b/ext/saml2sp4ui/provisioning-java/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-saml2sp4ui - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SAML2 SP4UI Provisioning Java diff --git a/ext/saml2sp4ui/rest-api/pom.xml b/ext/saml2sp4ui/rest-api/pom.xml index 6ab7701710e..0e78a9ff85a 100644 --- a/ext/saml2sp4ui/rest-api/pom.xml +++ b/ext/saml2sp4ui/rest-api/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-saml2sp4ui - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SAML2 SP4UI REST API diff --git a/ext/saml2sp4ui/rest-cxf/pom.xml b/ext/saml2sp4ui/rest-cxf/pom.xml index 434e913c139..9e5412a9926 100644 --- a/ext/saml2sp4ui/rest-cxf/pom.xml +++ b/ext/saml2sp4ui/rest-cxf/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-saml2sp4ui - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SAML2 SP4UI REST CXF diff --git a/ext/scimv2/client-console/pom.xml b/ext/scimv2/client-console/pom.xml index b896d0cd2d1..1f059d97639 100644 --- a/ext/scimv2/client-console/pom.xml +++ b/ext/scimv2/client-console/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-scimv2 - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SCIMv2 Client Console diff --git a/ext/scimv2/common-lib/pom.xml b/ext/scimv2/common-lib/pom.xml index 768ad5eaa97..b0420c72ad9 100644 --- a/ext/scimv2/common-lib/pom.xml +++ b/ext/scimv2/common-lib/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-scimv2 - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SCIMv2 Common Lib diff --git a/ext/scimv2/logic/pom.xml b/ext/scimv2/logic/pom.xml index a08c4cf5805..0ee2dd2f95d 100644 --- a/ext/scimv2/logic/pom.xml +++ b/ext/scimv2/logic/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-scimv2 - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SCIMv2 Logic diff --git a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java index c7a95330e94..96383ea160b 100644 --- a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java +++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java @@ -1235,7 +1235,7 @@ public Pair, StatusR> toUserUpdate( // Workaround for Microsoft Entra being not SCIM compliant on PATCH requests if (op.getValue().getFirst() instanceof String a) { - op.setValue(List.of(BooleanUtils.toBoolean(a))); + op.getValue().set(0, BooleanUtils.toBoolean(a)); } statusR.setValue(new StatusR.Builder(before.getKey(), (boolean) op.getValue().getFirst() @@ -1479,14 +1479,14 @@ public SCIMGroup toSCIMGroup( searchCond, PageRequest.of(0, 1), SyncopeConstants.ROOT_REALM, true, false).getTotalElements(); for (int page = 0; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) { - List users = groupDAO.findUMemberships( + List membs = groupDAO.findUMemberships( groupDAO.findById(groupTO.getKey()) .orElseThrow(() -> new NotFoundException("Group " + groupTO.getKey())), PageRequest.of(page, AnyDAO.DEFAULT_PAGE_SIZE)); - users.forEach(uMembership -> group.getMembers().add(new Member( - uMembership.getLeftEnd().getKey(), - StringUtils.substringBefore(location, "/Groups") + "/Users/" + uMembership.getKey(), - uMembership.getLeftEnd().getUsername()))); + membs.forEach(memb -> group.getMembers().add(new Member( + memb.getLeftEnd().getKey(), + StringUtils.substringBefore(location, "/Groups") + "/Users/" + memb.getKey(), + memb.getLeftEnd().getUsername()))); } } diff --git a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMLogic.java b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMLogic.java index 719aac8b9f1..94544f5717f 100644 --- a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMLogic.java +++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMLogic.java @@ -18,10 +18,6 @@ */ package org.apache.syncope.core.logic; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.ws.rs.core.UriBuilder; import java.io.IOException; import java.lang.reflect.Method; @@ -43,6 +39,12 @@ import org.apache.syncope.ext.scimv2.api.data.ServiceProviderConfig; import org.apache.syncope.ext.scimv2.api.type.Resource; import org.springframework.security.access.prepost.PreAuthorize; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.ObjectNode; public class SCIMLogic extends AbstractLogic { @@ -50,7 +52,8 @@ public class SCIMLogic extends AbstractLogic { protected static final Object MONITOR = new Object(); - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules(). + enable(MapperFeature.USE_GETTERS_AS_SETTERS).build(); protected static ServiceProviderConfig SERVICE_PROVIDER_CONFIG; @@ -123,42 +126,41 @@ protected void init() { + "\"location\": \"/v2/Schemas/urn:ietf:params:scim:schemas:extension:syncope:2.0:Group\"}")); schemaArray.add(extensionObject); } - if (!conf.getExtensionAnyObjectsConf().isEmpty()) { - conf.getExtensionAnyObjectsConf().forEach(confItem -> { - ObjectNode extensionObject = MAPPER.createObjectNode(); - extensionObject.put("id", - "urn:ietf:params:scim:schemas:extension:syncope:2.0:" + confItem.getType()); - extensionObject.put("name", confItem.getName()); - extensionObject.put("description", confItem.getDescription()); - ArrayNode attributes = MAPPER.createArrayNode(); - confItem.getAttributes().forEach(scimItem -> { - ObjectNode attribute = MAPPER.createObjectNode(); - attribute.put("name", scimItem.getIntAttrName()); - attribute.put("type", "string"); - attribute.put("multiValued", scimItem.isMultiValued()); - attribute.put("required", scimItem.getMandatoryCondition()); - attribute.put("caseExact", scimItem.isCaseExact()); - attribute.put("mutability", scimItem.isMutability()); - attribute.put("returned", scimItem.getReturned().getReturned()); - attribute.put("uniqueness", scimItem.isUniqueness()); - attributes.add(attribute); - }); - extensionObject.putIfAbsent("attributes", attributes); - try { - extensionObject.putIfAbsent("meta", MAPPER.readTree("{\"resourceType\": \"Schema\"," - + "\"location\": \"/v2/Schemas/urn:ietf:params:scim:schemas:extension:syncope:2.0:" - + confItem.getType() + "}\"")); - } catch (IOException e) { - LOG.error("Could not parse the default schema definitions", e); - } - schemaArray.add(extensionObject); + + conf.getExtensionAnyObjectsConf().forEach(confItem -> { + ObjectNode extensionObject = MAPPER.createObjectNode(); + extensionObject.put("id", + "urn:ietf:params:scim:schemas:extension:syncope:2.0:" + confItem.getType()); + extensionObject.put("name", confItem.getName()); + extensionObject.put("description", confItem.getDescription()); + ArrayNode attributes = MAPPER.createArrayNode(); + confItem.getAttributes().forEach(scimItem -> { + ObjectNode attribute = MAPPER.createObjectNode(); + attribute.put("name", scimItem.getIntAttrName()); + attribute.put("type", "string"); + attribute.put("multiValued", scimItem.isMultiValued()); + attribute.put("required", scimItem.getMandatoryCondition()); + attribute.put("caseExact", scimItem.isCaseExact()); + attribute.put("mutability", scimItem.isMutability()); + attribute.put("returned", scimItem.getReturned().getReturned()); + attribute.put("uniqueness", scimItem.isUniqueness()); + attributes.add(attribute); }); - } + extensionObject.putIfAbsent("attributes", attributes); + try { + extensionObject.putIfAbsent("meta", MAPPER.readTree("{\"resourceType\": \"Schema\"," + + "\"location\": \"/v2/Schemas/urn:ietf:params:scim:schemas:extension:syncope:2.0:" + + confItem.getType() + "}\"")); + } catch (JacksonException e) { + LOG.error("Could not parse the default schema definitions", e); + } + schemaArray.add(extensionObject); + }); schemas = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(tree); schemaMap.clear(); for (JsonNode schema : schemaArray) { - schemaMap.put(schema.get("id").asText(), MAPPER.writeValueAsString(schema)); + schemaMap.put(schema.get("id").asString(), MAPPER.writeValueAsString(schema)); } } catch (IOException e) { LOG.error("Could not parse the default schema definitions", e); diff --git a/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/SCIMDataBinderTest.java b/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/SCIMDataBinderTest.java index 87f3af282bd..59a3b368a0d 100644 --- a/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/SCIMDataBinderTest.java +++ b/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/SCIMDataBinderTest.java @@ -25,7 +25,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -103,11 +102,27 @@ void setup() { dataBinder = new SCIMDataBinder(scimConfManager, userLogic, authDataAccessor, groupDAO, jexlTools); } + private static SCIMPatchOperation operation( + final String attribute, + final String sub, + final PatchOp op, + final String value) { + + SCIMPatchOperation operation = new SCIMPatchOperation(); + SCIMPatchPath scimPatchPath = new SCIMPatchPath(); + scimPatchPath.setAttribute(attribute); + scimPatchPath.setSub(sub); + operation.setOp(op); + operation.setPath(scimPatchPath); + Optional.ofNullable(value).ifPresent(v -> operation.getValue().add(v)); + return operation; + } + @ParameterizedTest @MethodSource("getValue") void toUserUpdateActive(final String value) { SCIMPatchOp scimPatchOp = new SCIMPatchOp(); - scimPatchOp.setOperations(List.of(getOperation("active", null, PatchOp.add, value))); + scimPatchOp.getOperations().add(operation("active", null, PatchOp.add, value)); Pair, StatusR> result = dataBinder.toUserUpdate(new UserTO(), scimPatchOp); assertNotNull(result); assertEquals(1, result.getLeft().size()); @@ -122,9 +137,7 @@ void toUserUpdateActive(final String value) { @Test void toUserUpdate() { SCIMPatchOp scimPatchOp = new SCIMPatchOp(); - List operations = new ArrayList<>(); - operations.add(getOperation("name", "familyName", PatchOp.add, "Rossini")); - scimPatchOp.setOperations(operations); + scimPatchOp.getOperations().add(operation("name", "familyName", PatchOp.add, "Rossini")); Pair, StatusR> result = dataBinder.toUserUpdate(new UserTO(), scimPatchOp); assertNotNull(result); @@ -136,10 +149,9 @@ void toUserUpdate() { && attrPatch.getAttr().getSchema().equals("surname") && attrPatch.getAttr().getValues().contains("Rossini"))); - operations.clear(); - operations.add(getOperation("name", "givenName", PatchOp.add, "Gioacchino")); - operations.add(getOperation("name", "familyName", PatchOp.remove, null)); - scimPatchOp.setOperations(operations); + scimPatchOp.getOperations().clear(); + scimPatchOp.getOperations().add(operation("name", "givenName", PatchOp.add, "Gioacchino")); + scimPatchOp.getOperations().add(operation("name", "familyName", PatchOp.remove, null)); result = dataBinder.toUserUpdate(new UserTO(), scimPatchOp); assertNotNull(result); assertNull(result.getRight()); @@ -154,11 +166,10 @@ void toUserUpdate() { && attrPatch.getAttr().getSchema().equals("surname") && attrPatch.getAttr().getValues().isEmpty())); - operations.clear(); - operations.add(getOperation("name", "familyName", PatchOp.add, "Verdi")); - operations.add(getOperation("name", "givenName", PatchOp.replace, "Giuseppe")); - operations.add(getOperation("userName", null, PatchOp.add, "gverdi")); - scimPatchOp.setOperations(operations); + scimPatchOp.getOperations().clear(); + scimPatchOp.getOperations().add(operation("name", "familyName", PatchOp.add, "Verdi")); + scimPatchOp.getOperations().add(operation("name", "givenName", PatchOp.replace, "Giuseppe")); + scimPatchOp.getOperations().add(operation("userName", null, PatchOp.add, "gverdi")); result = dataBinder.toUserUpdate(new UserTO(), scimPatchOp); assertNotNull(result); assertNull(result.getRight()); @@ -175,11 +186,10 @@ void toUserUpdate() { && attrPatch.getAttr().getSchema().equals("firstname") && attrPatch.getAttr().getValues().contains("Giuseppe"))); - operations.clear(); - operations.add(getOperation("name", "familyName", PatchOp.replace, "Puccini")); - operations.add(getOperation("name", "givenName", PatchOp.remove, null)); - operations.add(getOperation("active", null, PatchOp.add, "True")); - scimPatchOp.setOperations(operations); + scimPatchOp.getOperations().clear(); + scimPatchOp.getOperations().add(operation("name", "familyName", PatchOp.replace, "Puccini")); + scimPatchOp.getOperations().add(operation("name", "givenName", PatchOp.remove, null)); + scimPatchOp.getOperations().add(operation("active", null, PatchOp.add, "True")); result = dataBinder.toUserUpdate(new UserTO(), scimPatchOp); assertNotNull(result); assertNotNull(result.getRight()); @@ -187,12 +197,12 @@ void toUserUpdate() { assertEquals(StatusRType.REACTIVATE, result.getRight().getType()); assertEquals(1, result.getLeft().size()); assertEquals(2, result.getLeft().getFirst().getPlainAttrs().size()); - assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch - -> PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation()) + assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch( + attrPatch -> PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation()) && attrPatch.getAttr().getSchema().equals("surname") && attrPatch.getAttr().getValues().contains("Puccini"))); - assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch - -> PatchOperation.DELETE.equals(attrPatch.getOperation()) + assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch( + attrPatch -> PatchOperation.DELETE.equals(attrPatch.getOperation()) && attrPatch.getAttr().getSchema().equals("firstname") && attrPatch.getAttr().getValues().isEmpty())); @@ -206,10 +216,9 @@ void toUserUpdate() { scimUser.getName().setFamilyName("Bellini"); SCIMPatchOperation operation = new SCIMPatchOperation(); operation.setOp(PatchOp.add); - operation.setValue(List.of(scimUser)); - operations.clear(); - operations.add(operation); - scimPatchOp.setOperations(operations); + operation.getValue().add(scimUser); + scimPatchOp.getOperations().clear(); + scimPatchOp.getOperations().add(operation); result = dataBinder.toUserUpdate(userTO, scimPatchOp); assertNotNull(result); assertNull(result.getRight()); @@ -226,8 +235,8 @@ void toUserUpdate() { assertEquals(PatchOperation.ADD_REPLACE, result.getLeft().getFirst().getUsername().getOperation()); assertEquals("bellini", result.getLeft().getFirst().getUsername().getValue()); assertEquals(1, result.getLeft().getFirst().getPlainAttrs().size()); - assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch - -> PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation()) + assertTrue(result.getLeft().getFirst().getPlainAttrs().stream(). + anyMatch(attrPatch -> PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation()) && attrPatch.getAttr().getSchema().equals("surname") && attrPatch.getAttr().getValues().contains("Bellini"))); @@ -245,35 +254,18 @@ void toUserUpdate() { assertEquals(1, result.getLeft().size()); assertNull(result.getLeft().getFirst().getUsername()); assertEquals(1, result.getLeft().getFirst().getPlainAttrs().size()); - assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch - -> PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation()) + assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch( + attrPatch -> PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation()) && attrPatch.getAttr().getSchema().equals("firstname") && attrPatch.getAttr().getValues().contains("Gioacchino"))); assertEquals(1, result.getLeft().getFirst().getRoles().size()); - assertTrue(result.getLeft().getFirst().getRoles().stream().anyMatch(role - -> PatchOperation.ADD_REPLACE.equals(role.getOperation()) + assertTrue(result.getLeft().getFirst().getRoles().stream().anyMatch( + role -> PatchOperation.ADD_REPLACE.equals(role.getOperation()) && role.getValue().equals("User reviewer"))); + } - userTO = new UserTO(); - Group group = new Group("37d15e4c-cdc1-460b-a591-8505c8133806", null, "root", null); - scimUser = new SCIMUser( - UUID.randomUUID().toString(), List.of(Resource.User.schema()), null, "bellini", true); - scimUser.getGroups().add(group); - group = new Group("29f96485-729e-4d31-88a1-6fc60e4677f3", null, "citizen", null); - scimUser.getGroups().add(group); - operation.setOp(PatchOp.add); - operation.setValue(List.of(scimUser)); - operations.clear(); - operations.add(operation); - group = new Group("f779c0d4-633b-4be5-8f57-32eb478a3ca5", null, "otherchild", null); - SCIMUser scimUser2 = - new SCIMUser(UUID.randomUUID().toString(), List.of(Resource.User.schema()), null, "bellini", true); - scimUser2.getGroups().add(group); - SCIMPatchOperation operation2 = new SCIMPatchOperation(); - operation2.setOp(PatchOp.add); - operation2.setValue(List.of(scimUser2)); - operations.add(operation2); - scimPatchOp.setOperations(operations); + @Test + void groups() { when(groupDAO.findById("37d15e4c-cdc1-460b-a591-8505c8133806")).thenAnswer(ic -> { org.apache.syncope.core.persistence.api.entity.group.Group syncopeGroup = mock(org.apache.syncope.core.persistence.api.entity.group.Group.class); @@ -311,33 +303,47 @@ void toUserUpdate() { when(syncopeGroup.getResources()).thenAnswer(invocation -> List.of(resource, resource2)); return Optional.of(syncopeGroup); }); - result = dataBinder.toUserUpdate(userTO, scimPatchOp); + + UserTO userTO = new UserTO(); + + Group group = new Group("37d15e4c-cdc1-460b-a591-8505c8133806", null, "root", null); + SCIMUser scimUser = new SCIMUser( + UUID.randomUUID().toString(), List.of(Resource.User.schema()), null, "bellini", true); + scimUser.getGroups().add(group); + group = new Group("29f96485-729e-4d31-88a1-6fc60e4677f3", null, "citizen", null); + scimUser.getGroups().add(group); + + SCIMPatchOperation operation = new SCIMPatchOperation(); + operation.setOp(PatchOp.add); + operation.getValue().clear(); + operation.getValue().add(scimUser); + SCIMPatchOp scimPatchOp = new SCIMPatchOp(); + scimPatchOp.getOperations().add(operation); + + group = new Group("f779c0d4-633b-4be5-8f57-32eb478a3ca5", null, "otherchild", null); + SCIMUser scimUser2 = new SCIMUser( + UUID.randomUUID().toString(), List.of(Resource.User.schema()), null, "bellini", true); + scimUser2.getGroups().add(group); + SCIMPatchOperation operation2 = new SCIMPatchOperation(); + operation2.setOp(PatchOp.add); + operation2.getValue().add(scimUser2); + scimPatchOp.getOperations().add(operation2); + + Pair, StatusR> result = dataBinder.toUserUpdate(userTO, scimPatchOp); assertNotNull(result); assertNull(result.getRight()); assertEquals(3, result.getLeft().size()); assertTrue(result.getLeft().get(0).isEmpty()); assertEquals(2, result.getLeft().get(1).getMemberships().size()); - assertTrue(result.getLeft().get(1).getMemberships().stream().anyMatch(membershipUR - -> PatchOperation.ADD_REPLACE.equals(membershipUR.getOperation()) + assertTrue(result.getLeft().get(1).getMemberships().stream().anyMatch( + membershipUR -> PatchOperation.ADD_REPLACE.equals(membershipUR.getOperation()) && membershipUR.getGroup().equals("37d15e4c-cdc1-460b-a591-8505c8133806"))); - assertTrue(result.getLeft().get(1).getMemberships().stream().anyMatch(membershipUR - -> PatchOperation.ADD_REPLACE.equals(membershipUR.getOperation()) + assertTrue(result.getLeft().get(1).getMemberships().stream().anyMatch( + membershipUR -> PatchOperation.ADD_REPLACE.equals(membershipUR.getOperation()) && membershipUR.getGroup().equals("29f96485-729e-4d31-88a1-6fc60e4677f3"))); assertEquals(1, result.getLeft().get(2).getMemberships().size()); - assertTrue(result.getLeft().get(2).getMemberships().stream().anyMatch(membershipUR - -> PatchOperation.ADD_REPLACE.equals(membershipUR.getOperation()) + assertTrue(result.getLeft().get(2).getMemberships().stream().anyMatch( + membershipUR -> PatchOperation.ADD_REPLACE.equals(membershipUR.getOperation()) && membershipUR.getGroup().equals("f779c0d4-633b-4be5-8f57-32eb478a3ca5"))); } - - private SCIMPatchOperation getOperation( - final String attribute, final String sub, final PatchOp op, final String value) { - SCIMPatchOperation operation = new SCIMPatchOperation(); - SCIMPatchPath scimPatchPath = new SCIMPatchPath(); - scimPatchPath.setAttribute(attribute); - scimPatchPath.setSub(sub); - operation.setOp(op); - operation.setPath(scimPatchPath); - operation.setValue(value == null ? List.of() : List.of(value)); - return operation; - } } diff --git a/ext/scimv2/pom.xml b/ext/scimv2/pom.xml index d2a72eec7ca..a69661a11da 100644 --- a/ext/scimv2/pom.xml +++ b/ext/scimv2/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-ext - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SCIMv2 diff --git a/ext/scimv2/rest-api/pom.xml b/ext/scimv2/rest-api/pom.xml index 07412647431..2d17399ee25 100644 --- a/ext/scimv2/rest-api/pom.xml +++ b/ext/scimv2/rest-api/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-scimv2 - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SCIMv2 REST API diff --git a/ext/scimv2/rest-cxf/pom.xml b/ext/scimv2/rest-cxf/pom.xml index 35cd5a8a5c9..5ec960a5e7c 100644 --- a/ext/scimv2/rest-cxf/pom.xml +++ b/ext/scimv2/rest-cxf/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-scimv2 - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SCIMv2 REST CXF diff --git a/ext/scimv2/scim-rest-api/pom.xml b/ext/scimv2/scim-rest-api/pom.xml index 6d72de8f174..64aca15a3e3 100644 --- a/ext/scimv2/scim-rest-api/pom.xml +++ b/ext/scimv2/scim-rest-api/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-scimv2 - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SCIMv2 SCIM REST API @@ -44,7 +44,7 @@ under the License. - com.fasterxml.jackson.core + tools.jackson.core jackson-databind @@ -66,6 +66,15 @@ under the License. org.apache.maven.plugins maven-checkstyle-plugin + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.1 + + 21 + 21 + + diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ListResponse.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ListResponse.java index 6e1e68b2b4b..c3e8ce7a5a3 100644 --- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ListResponse.java +++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ListResponse.java @@ -19,6 +19,7 @@ package org.apache.syncope.ext.scimv2.api.data; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.ArrayList; import java.util.List; @@ -28,6 +29,7 @@ public class ListResponse extends SCIMBean { private static final long serialVersionUID = -776611610457583160L; + @JsonIgnore private final List schemas = List.of(Resource.ListResponse.schema()); private final long totalResults; diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ResourceType.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ResourceType.java index ba79d1523b0..650694a82e8 100644 --- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ResourceType.java +++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ResourceType.java @@ -19,6 +19,7 @@ package org.apache.syncope.ext.scimv2.api.data; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.ArrayList; import java.util.List; @@ -28,6 +29,7 @@ public class ResourceType extends SCIMBean { private static final long serialVersionUID = -6559584102333757279L; + @JsonIgnore private final List schemas = List.of(Resource.ResourceType.schema()); private final String id; diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java index 35b94c0381f..56e20d5a957 100644 --- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java +++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat.Shape; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import jakarta.ws.rs.core.Response; @@ -34,6 +35,7 @@ public class SCIMError extends SCIMBean { private static final long serialVersionUID = -8836902509266522394L; + @JsonIgnore private final List schemas = List.of(Resource.Error.schema()); private ErrorType scimType; diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOp.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOp.java index eb713e95ea2..0ead18419a6 100644 --- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOp.java +++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOp.java @@ -18,7 +18,9 @@ */ package org.apache.syncope.ext.scimv2.api.data; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; import java.util.List; import org.apache.syncope.ext.scimv2.api.type.Resource; @@ -26,10 +28,11 @@ public class SCIMPatchOp extends SCIMBean { private static final long serialVersionUID = 3957352317667344898L; + @JsonIgnore private final List schemas = List.of(Resource.PatchOp.schema()); @JsonProperty("Operations") - private List operations; + private final List operations = new ArrayList<>(); public List getSchemas() { return schemas; @@ -38,8 +41,4 @@ public List getSchemas() { public List getOperations() { return operations; } - - public void setOperations(final List operations) { - this.operations = operations; - } } diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperation.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperation.java index 1421baff894..0653e12fcfc 100644 --- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperation.java +++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperation.java @@ -18,10 +18,11 @@ */ package org.apache.syncope.ext.scimv2.api.data; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.io.Serializable; +import java.util.ArrayList; import java.util.List; import org.apache.syncope.ext.scimv2.api.type.PatchOp; +import tools.jackson.databind.annotation.JsonDeserialize; @JsonDeserialize(using = SCIMPatchOperationDeserializer.class) public class SCIMPatchOperation extends SCIMBean { @@ -32,7 +33,7 @@ public class SCIMPatchOperation extends SCIMBean { private SCIMPatchPath path; - private List value; + private final List value = new ArrayList<>(); public PatchOp getOp() { return op; @@ -53,8 +54,4 @@ public void setPath(final SCIMPatchPath path) { public List getValue() { return value; } - - public void setValue(final List value) { - this.value = value; - } } diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperationDeserializer.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperationDeserializer.java index 2b4af8d694b..3d6a2f78ccc 100644 --- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperationDeserializer.java +++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperationDeserializer.java @@ -18,11 +18,6 @@ */ package org.apache.syncope.ext.scimv2.api.data; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import java.io.IOException; import java.io.Serializable; import java.util.List; import java.util.Optional; @@ -30,11 +25,15 @@ import java.util.regex.Pattern; import org.apache.cxf.common.util.StringUtils; import org.apache.syncope.ext.scimv2.api.type.PatchOp; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.deser.std.StdDeserializer; public class SCIMPatchOperationDeserializer extends StdDeserializer { - private static final long serialVersionUID = -7401353969242788372L; - private static final Pattern PATH_PATTERN = Pattern.compile( "^(?[A-Za-z0-9:.]+:)?(?\\w+)(?\\[.*\\])?(?\\.\\w+)?"); @@ -61,11 +60,11 @@ private static Serializable scalar(final JsonNode v) { return v.longValue(); } - return v.asText(); + return v.asString(); } public SCIMPatchOperationDeserializer() { - this(null); + this(SCIMPatchOperation.class); } public SCIMPatchOperationDeserializer(final Class vc) { @@ -74,18 +73,18 @@ public SCIMPatchOperationDeserializer(final Class vc) { @Override public SCIMPatchOperation deserialize(final JsonParser jp, final DeserializationContext ctxt) - throws IOException { + throws JacksonException { - JsonNode node = jp.getCodec().readTree(jp); + JsonNode node = jp.readValueAsTree(); SCIMPatchOperation scimPatchOperation = new SCIMPatchOperation(); if (node.has("op")) { - scimPatchOperation.setOp(PatchOp.valueOf(node.get("op").asText().toLowerCase())); + scimPatchOperation.setOp(PatchOp.valueOf(node.get("op").asString().toLowerCase())); } if (node.has("path")) { - Matcher matcher = PATH_PATTERN.matcher(node.get("path").asText()); + Matcher matcher = PATH_PATTERN.matcher(node.get("path").asString()); if (matcher.matches()) { SCIMPatchPath path = new SCIMPatchPath(); scimPatchOperation.setPath(path); @@ -107,116 +106,103 @@ public SCIMPatchOperation deserialize(final JsonParser jp, final Deserialization JsonNode value = node.get("value"); if (scimPatchOperation.getPath() == null) { - scimPatchOperation.setValue(List.of(jp.getCodec().treeToValue(value, SCIMUser.class))); + scimPatchOperation.getValue().add( + jp.objectReadContext().treeAsTokens(value).readValueAs(SCIMUser.class)); } else { if ("members".equals(scimPatchOperation.getPath().getAttribute())) { - scimPatchOperation.setValue(List.of( - (Serializable[]) jp.getCodec().treeToValue(value, Member[].class))); + scimPatchOperation.getValue().addAll( + jp.objectReadContext().treeAsTokens(value).readValueAs(new TypeReference>() { + })); } else if (value.isObject()) { SCIMUser user = new SCIMUser( null, List.of(), null, - "userName".equals(scimPatchOperation.getPath().getAttribute()) ? value.asText() : null, + "userName".equals(scimPatchOperation.getPath().getAttribute()) ? value.asString() : null, "active".equals(scimPatchOperation.getPath().getAttribute()) ? value.asBoolean() : null); user.setEnterpriseInfo(new SCIMEnterpriseInfo()); switch (scimPatchOperation.getPath().getAttribute()) { - case "externalId": - user.setExternalId(value.asText()); - break; + case "externalId" -> + user.setExternalId(value.asString()); - case "name": - user.setName(jp.getCodec().treeToValue(value, SCIMUserName.class)); - break; + case "name" -> + user.setName(jp.objectReadContext().treeAsTokens(value).readValueAs(SCIMUserName.class)); - case "displayName": - user.setDisplayName(value.asText()); - break; + case "displayName" -> + user.setDisplayName(value.asString()); - case "nickName": - user.setNickName(value.asText()); - break; + case "nickName" -> + user.setNickName(value.asString()); - case "profileUrl": - user.setProfileUrl(value.asText()); - break; + case "profileUrl" -> + user.setProfileUrl(value.asString()); - case "title": - user.setTitle(value.asText()); - break; + case "title" -> + user.setTitle(value.asString()); - case "userType": - user.setUserType(value.asText()); - break; + case "userType" -> + user.setUserType(value.asString()); - case "preferredLanguage": - user.setPreferredLanguage(value.asText()); - break; + case "preferredLanguage" -> + user.setPreferredLanguage(value.asString()); - case "locale": - user.setLocale(value.asText()); - break; + case "locale" -> + user.setLocale(value.asString()); - case "timezone": - user.setTimezone(value.asText()); - break; + case "timezone" -> + user.setTimezone(value.asString()); - case "emails": - user.getEmails().add(jp.getCodec().treeToValue(value, SCIMComplexValue.class)); - break; + case "emails" -> + user.getEmails().add( + jp.objectReadContext().treeAsTokens(value).readValueAs(SCIMComplexValue.class)); - case "phoneNumbers": - user.getPhoneNumbers().add(jp.getCodec().treeToValue(value, SCIMComplexValue.class)); - break; + case "phoneNumbers" -> + user.getPhoneNumbers().add( + jp.objectReadContext().treeAsTokens(value).readValueAs(SCIMComplexValue.class)); - case "ims": - user.getIms().add(jp.getCodec().treeToValue(value, SCIMComplexValue.class)); - break; + case "ims" -> + user.getIms().add( + jp.objectReadContext().treeAsTokens(value).readValueAs(SCIMComplexValue.class)); - case "photos": - user.getPhotos().add(jp.getCodec().treeToValue(value, SCIMComplexValue.class)); - break; + case "photos" -> + user.getPhotos().add( + jp.objectReadContext().treeAsTokens(value).readValueAs(SCIMComplexValue.class)); - case "addresses": - user.getAddresses().add(jp.getCodec().treeToValue(value, SCIMUserAddress.class)); - break; + case "addresses" -> + user.getAddresses().add( + jp.objectReadContext().treeAsTokens(value).readValueAs(SCIMUserAddress.class)); - case "x509Certificates": - user.getX509Certificates().add(jp.getCodec().treeToValue(value, Value.class)); - break; + case "x509Certificates" -> + user.getX509Certificates().add( + jp.objectReadContext().treeAsTokens(value).readValueAs(Value.class)); - case "employeeNumber": - user.getEnterpriseInfo().setEmployeeNumber(value.asText()); - break; + case "employeeNumber" -> + user.getEnterpriseInfo().setEmployeeNumber(value.asString()); - case "costCenter": - user.getEnterpriseInfo().setCostCenter(value.asText()); - break; + case "costCenter" -> + user.getEnterpriseInfo().setCostCenter(value.asString()); - case "organization": - user.getEnterpriseInfo().setOrganization(value.asText()); - break; + case "organization" -> + user.getEnterpriseInfo().setOrganization(value.asString()); - case "division": - user.getEnterpriseInfo().setDivision(value.asText()); - break; + case "division" -> + user.getEnterpriseInfo().setDivision(value.asString()); - case "department": - user.getEnterpriseInfo().setDepartment(value.asText()); - break; + case "department" -> + user.getEnterpriseInfo().setDepartment(value.asString()); - case "manager": - user.getEnterpriseInfo(). - setManager(jp.getCodec().treeToValue(value, SCIMUserManager.class)); - break; + case "manager" -> + user.getEnterpriseInfo().setManager( + jp.objectReadContext().treeAsTokens(value).readValueAs(SCIMUserManager.class)); - default: + default -> { + } } - scimPatchOperation.setValue(List.of(user)); - } else if (!value.isContainerNode()) { - scimPatchOperation.setValue(List.of(scalar(value))); + scimPatchOperation.getValue().add(user); + } else if (!value.isContainer()) { + scimPatchOperation.getValue().add(scalar(value)); } } } diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMSearchRequest.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMSearchRequest.java index 03bd0d7f17b..ab8b0a27999 100644 --- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMSearchRequest.java +++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMSearchRequest.java @@ -19,6 +19,7 @@ package org.apache.syncope.ext.scimv2.api.data; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.ArrayList; import java.util.List; @@ -30,6 +31,7 @@ public class SCIMSearchRequest extends SCIMBean { private static final long serialVersionUID = 5759362928661983543L; + @JsonIgnore private final List schemas = List.of(Resource.SearchRequest.schema()); private final List attributes = new ArrayList<>(); diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ServiceProviderConfig.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ServiceProviderConfig.java index 9e03c96c28c..7a0f865e408 100644 --- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ServiceProviderConfig.java +++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ServiceProviderConfig.java @@ -19,6 +19,7 @@ package org.apache.syncope.ext.scimv2.api.data; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import java.util.ArrayList; @@ -31,6 +32,7 @@ public class ServiceProviderConfig extends SCIMBean { private static final long serialVersionUID = 1027738509789460252L; + @JsonIgnore private final List schemas = List.of(Resource.ServiceProviderConfig.schema()); private final Meta meta; diff --git a/ext/scimv2/scim-rest-api/src/test/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperationDeserializerTest.java b/ext/scimv2/scim-rest-api/src/test/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperationDeserializerTest.java index 32167f7f22a..ea114084caf 100644 --- a/ext/scimv2/scim-rest-api/src/test/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperationDeserializerTest.java +++ b/ext/scimv2/scim-rest-api/src/test/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperationDeserializerTest.java @@ -23,21 +23,22 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.util.List; import org.apache.syncope.ext.scimv2.api.type.PatchOp; import org.apache.syncope.ext.scimv2.api.type.Resource; import org.junit.jupiter.api.Test; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.json.JsonMapper; public class SCIMPatchOperationDeserializerTest { private static final JsonMapper MAPPER = JsonMapper.builder(). - findAndAddModules().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).build(); + findAndAddModules(). + enable(MapperFeature.USE_GETTERS_AS_SETTERS). + build(); @Test - public void addMember() throws JsonProcessingException { + public void addMember() { String input = "{ " + "\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"]," @@ -75,7 +76,7 @@ public void addMember() throws JsonProcessingException { } @Test - public void removeMembers() throws JsonProcessingException { + public void removeMembers() { String input = "{" + " \"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"]," @@ -98,7 +99,7 @@ public void removeMembers() throws JsonProcessingException { } @Test - public void removeAndAddMembers() throws JsonProcessingException { + public void removeAndAddMembers() { String input = "{ \"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"]," + " \"Operations\": [" @@ -156,7 +157,7 @@ public void removeAndAddMembers() throws JsonProcessingException { } @Test - public void addAttributes() throws JsonProcessingException { + public void addAttributes() { String input = "{" + " \"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"]," @@ -199,7 +200,7 @@ public void addAttributes() throws JsonProcessingException { } @Test - public void replaceMembers() throws JsonProcessingException { + public void replaceMembers() { String input = " {" + " \"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"]," @@ -245,7 +246,7 @@ public void replaceMembers() throws JsonProcessingException { } @Test - public void replaceAttribute() throws JsonProcessingException { + public void replaceAttribute() { String input = "{" + " \"schemas\": [\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"]," @@ -289,7 +290,7 @@ public void replaceAttribute() throws JsonProcessingException { } @Test - public void replaceAttributeValue() throws JsonProcessingException { + public void replaceAttributeValue() { String input = "{" + " \"schemas\": [\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"]," @@ -319,7 +320,7 @@ public void replaceAttributeValue() throws JsonProcessingException { } @Test - public void addAttributeValue() throws JsonProcessingException { + public void addAttributeValue() { String input = "{" + " \"schemas\": [\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"]," diff --git a/ext/scimv2/scim-rest-cxf/pom.xml b/ext/scimv2/scim-rest-cxf/pom.xml index 7f3cc572ce2..ed6fbb92075 100644 --- a/ext/scimv2/scim-rest-cxf/pom.xml +++ b/ext/scimv2/scim-rest-cxf/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope.ext syncope-ext-scimv2 - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Ext: SCIMv2 SCIM REST CXF @@ -50,7 +50,7 @@ under the License. - com.fasterxml.jackson.jakarta.rs + tools.jackson.jakarta.rs jackson-jakarta-rs-json-provider diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java index 70dcc2f9f52..21c3c22d76e 100644 --- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java +++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java @@ -102,10 +102,10 @@ public Response toResponse(final Exception ex) { || ExceptionUtils.getRootCause(ex) instanceof DelegatedAdministrationException) { builder = builder(ClientExceptionType.DelegatedAdministration, ExceptionUtils.getRootCauseMessage(ex)); - } else if (ENTITYEXISTS_EXCLASS.isAssignableFrom(ex.getClass()) - || ex instanceof DuplicateException - || PERSISTENCE_EXCLASS.isAssignableFrom(ex.getClass()) - && ENTITYEXISTS_EXCLASS.isAssignableFrom(ex.getCause().getClass())) { + } else if (ENTITYEXISTS_EXCLASS.isAssignableFrom(ex.getClass()) || ex instanceof DuplicateException + || (ex.getCause() != null && ENTITYEXISTS_EXCLASS.isAssignableFrom(ex.getCause().getClass())) + || ex.getMessage().contains("already exists") + || ex.getMessage().contains("UNIQUE") || ex.getMessage().contains("Duplicate")) { builder = builder(ClientExceptionType.EntityExists, ExceptionUtils.getRootCauseMessage(ex)); } else if (ex instanceof DataIntegrityViolationException || JPASYSTEM_EXCLASS.isAssignableFrom(ex.getClass())) { diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMv2RESTCXFContext.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMv2RESTCXFContext.java index f5a0a228c3d..ae2ff6fe967 100644 --- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMv2RESTCXFContext.java +++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMv2RESTCXFContext.java @@ -18,9 +18,6 @@ */ package org.apache.syncope.ext.scimv2.cxf; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import java.util.List; import java.util.Map; import org.apache.cxf.Bus; @@ -51,6 +48,9 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.jakarta.rs.json.JacksonJsonProvider; @Configuration(proxyBeanMethods = false) public class SCIMv2RESTCXFContext { @@ -59,7 +59,7 @@ public class SCIMv2RESTCXFContext { @Bean public JacksonJsonProvider scimJacksonJsonProvider() { return new JacksonJsonProvider(JsonMapper.builder(). - findAndAddModules().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).build()); + findAndAddModules().enable(MapperFeature.USE_GETTERS_AS_SETTERS).build()); } @ConditionalOnMissingBean diff --git a/fit/build-tools/LICENSE b/fit/build-tools/LICENSE index e592ea625bb..72d4d2380ad 100644 --- a/fit/build-tools/LICENSE +++ b/fit/build-tools/LICENSE @@ -203,57 +203,7 @@ == -For Jackson (http://wiki.fasterxml.com/JacksonHome): -This is licensed under the AL 2.0, see above. - -== - -For ClassMate (https://github.com/FasterXML/java-classmate): -This is licensed under the AL 2.0, see above. - -== - -For Caffeine (https://github.com/ben-manes/caffeine): -This is licensed under the AL 2.0, see above. - -== - -For JNI binding for Zstd (https://github.com/luben/zstd-jni): -This is licensed under the BSD license: - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its contributors -may be used to endorse or promote products derived from this software without -specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -== - -For Error Prone (https://errorprone.info/): -This is licensed under the AL 2.0, see above. - -== - -For H2 (http://www.h2database.com/): +For Logback (https://logback.qos.ch/): This is licensed under the EPL 1.0: Eclipse Public License - v 1.0 @@ -462,21 +412,66 @@ any resulting litigation. == -For GreenMail (http://www.icegreen.com/greenmail/): +For Jackson (http://wiki.fasterxml.com/JacksonHome): +This is licensed under the AL 2.0, see above. + +== + +For ClassMate (https://github.com/FasterXML/java-classmate): This is licensed under the AL 2.0, see above. == -For LMAX Disruptor (https://lmax-exchange.github.io/disruptor/): +For Caffeine (https://github.com/ben-manes/caffeine): This is licensed under the AL 2.0, see above. == -For Method Parameter Name Access for Java (https://github.com/paul-hammant/paranamer): +For JNI binding for Zstd (https://github.com/luben/zstd-jni): +This is licensed under the BSD license: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +== + +For RE2/J (https://github.com/google/re2j): This is licensed under the BSD license, see above. == +For H2 (http://www.h2database.com/): +This is licensed under the EPL 1.0, see above. + +== + +For GreenMail (http://www.icegreen.com/greenmail/): +This is licensed under the AL 2.0, see above. + +== + For Scala Logging (https://github.com/lightbend-labs/scala-logging): This is licensed under the AL 2.0, see above. @@ -532,21 +527,11 @@ This is licensed under the EDL 1.0, see above. == -For Metrics (http://dropwizard.github.io/metrics/): -This is licensed under the AL 2.0, see above. - -== - For Micrometer Application Metrics (https://micrometer.io/): This is licensed under the AL 2.0, see above. == -For Netty (https://netty.io/): -This is licensed under the AL 2.0, see above. - -== - For Jakarta Annotations https://projects.eclipse.org/projects/ee4j.ca): This is licensed under the EPL 1.0, see above. @@ -1020,6 +1005,146 @@ This is licensed under the EDL 1.0, see above. For Eclipse Angus - Mail (https://eclipse-ee4j.github.io/angus-mail/): This is licensed under the EDL 1.0, see above. +== + +For HdrHistogram (https://hdrhistogram.github.io/HdrHistogram/): +This is licensed under the CC0 1.0: + +Statement of Purpose + +The laws of most jurisdictions throughout the world +automatically confer exclusive Copyright and Related Rights +(defined below) upon the creator and subsequent owner(s) (each +and all, an "owner") of an original work of authorship and/or +a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights +to a Work for the purpose of contributing to a commons of +creative, cultural and scientific works ("Commons") that the +public can reliably and without fear of later claims of +infringement build upon, modify, incorporate in other works, +reuse and redistribute as freely as possible in any form +whatsoever and for any purposes, including without limitation +commercial purposes. These owners may contribute to the +Commons to promote the ideal of a free culture and the further +production of creative, cultural and scientific works, or to +gain reputation or greater distribution for their Work in part +through the use and efforts of others. + +For these and/or other purposes and motivations, and +without any expectation of additional consideration or +compensation, the person associating CC0 with a Work (the +"Affirmer"), to the extent that he or she is an owner of +Copyright and Related Rights in the Work, voluntarily elects +to apply CC0 to the Work and publicly distribute the Work +under its terms, with knowledge of his or her Copyright and +Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. + +A Work made available under CC0 may be protected by +copyright and related or neighboring rights ("Copyright and + Related Rights"). Copyright and Related Rights include, but +are not limited to, the following: + +i. the right to reproduce, adapt, distribute, perform, +display, communicate, and translate a Work; +ii. moral rights retained by the original author(s) and/or +performer(s); +iii. publicity and privacy rights pertaining to a person's +image or likeness depicted in a Work; +iv. rights protecting against unfair competition in regards +to a Work, subject to the limitations in paragraph 4(a), +below; +v. rights protecting the extraction, dissemination, use and +reuse of data in a Work; +vi. database rights (such as those arising under Directive +96/9/EC of the European Parliament and of the Council of 11 +March 1996 on the legal protection of databases, and under +any national implementation thereof, including any amended +or successor version of such directive); and< +vii. >other similar, equivalent or corresponding rights +throughout the world based on applicable law or treaty, and +any national implementations thereof. + +2. Waiver. + +To the greatest extent +permitted by, but not in contravention of, applicable law, +Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims +and causes of action, whether now known or unknown (including +existing as well as future claims and causes of action), in +the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty +(including future time extensions), (iii) in any current or +future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, +advertising or promotional purposes (the "Waiver"). Affirmer +makes the Waiver for the benefit of each member of the public +at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be +subject to revocation, rescission, cancellation, termination, +or any other legal or equitable action to disrupt the quiet +enjoyment of the Work by the public as contemplated by +Affirmer's express Statement of Purpose. + +3. Public License Fallback. + +Should any +part of the Waiver for any reason be judged legally invalid or +ineffective under applicable law, then the Waiver shall be +preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each +affected person a royalty-free, non transferable, non +sublicensable, non exclusive, irrevocable and unconditional +license to exercise Affirmer's Copyright and Related Rights +in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty +(including future time extensions), (iii) in any current or +future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, +advertising or promotional purposes (the "License"). The +License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or +ineffective under applicable law, such partial invalidity or +ineffectiveness shall not invalidate the remainder of the +License, and in such case Affirmer hereby affirms that he or +she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any +associated claims and causes of action with respect to the +Work, in either case contrary to Affirmer's express Statement +of Purpose. + +4. Limitations and Disclaimers. + +i.No trademark or patent rights held by Affirmer are +waived, abandoned, surrendered, licensed or otherwise +affected by this document. +ii. Affirmer offers the Work as-is and makes no +representations or warranties of any kind concerning the +Work, express, implied, statutory or otherwise, including +without limitation warranties of title, merchantability, +fitness for a particular purpose, non infringement, or the +absence of latent or other defects, accuracy, or the present +or absence of errors, whether or not discoverable, all to +the greatest extent permissible under applicable law. +iii. Affirmer disclaims responsibility for clearing rights of +other persons that may apply to the Work or any use thereof, +including without limitation any person's Copyright and +Related Rights in the Work. Further, Affirmer disclaims +responsibility for obtaining any necessary consents, +permissions or other rights required for any use of the +Work. +iv. Affirmer understands and acknowledges that Creative +Commons is not a party to this document and has no duty or +obligation with respect to this CC0 or use of the Work. + + == For Hibernate Validator (https://hibernate.org/validator/): diff --git a/fit/build-tools/NOTICE b/fit/build-tools/NOTICE index 317712561c2..35428b79210 100644 --- a/fit/build-tools/NOTICE +++ b/fit/build-tools/NOTICE @@ -9,6 +9,11 @@ with which this file is now or was at one time distributed. == +This product includes software developed by Logback +Copyright (C) 1999-2024, QOS.ch. All rights reserved. + +== + This product includes software developed by the Jackson project. == @@ -23,7 +28,8 @@ Copyright (c) 2015-present, Luben Karavelov/ All rights reserved. == -This product includes software developed by Error Prone. +This product includes software developed by RE2/J +Copyright (c) 2009 The Go Authors. All rights reserved. == @@ -32,13 +38,6 @@ Copyright (c) 2006 - 2019, Wael Chatila and GreenMail project contributors == -This product includes software developed by Method Parameter Name Access for Java. -Portions copyright (c) 2006-2018 Paul Hammant & ThoughtWorks Inc -Portions copyright (c) 2000-2007 INRIA, France Telecom -All rights reserved. - -== - This product includes software developed by Scala Logging. Copyright 2014-2021 Lightbend, Inc. @@ -69,21 +68,11 @@ All content is the property of the respective authors or their employers. == -This product includes software developed by the Metrics project. -Copyright (c) 2010-2014 Coda Hale, Yammer.com - -== - This product includes software developed by Micrometer Application Metrics. Copyright (c) 2017-Present VMware, Inc. All Rights Reserved. == -This product includes software developed by Netty -Copyright 2014 The Netty Project - -== - This product includes software produced and maintained by Jakarta Annotations All content is the property of the respective authors or their employers. @@ -161,7 +150,7 @@ Copyright (c) 1997, 2024 Oracle and/or its affiliates. All rights reserved. == This product includes software developed by Hibernate Validator -Copyright: Red Hat Inc. and Hibernate Authors +Copyright 2022, The Hibernate Validator project == diff --git a/fit/build-tools/pom.xml b/fit/build-tools/pom.xml index 8ad0f96c5e4..8c066a0e7e0 100644 --- a/fit/build-tools/pom.xml +++ b/fit/build-tools/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-fit - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope FIT Build Tools @@ -61,11 +61,11 @@ under the License. org.springframework.boot - spring-boot-starter-log4j2 + spring-boot-starter-logback - com.lmax - disruptor + org.slf4j + jcl-over-slf4j @@ -97,7 +97,7 @@ under the License. - com.fasterxml.jackson.jakarta.rs + tools.jackson.jakarta.rs jackson-jakarta-rs-json-provider @@ -105,6 +105,14 @@ under the License. org.springframework.kafka spring-kafka-test + + org.apache.logging.log4j + log4j-core + + + org.junit.jupiter + junit-jupiter + org.junit.jupiter junit-jupiter-api diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/KafkaBrokerStartStopListener.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/KafkaBrokerStartStopListener.java index 985ba9b3e16..8428de1059d 100644 --- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/KafkaBrokerStartStopListener.java +++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/KafkaBrokerStartStopListener.java @@ -23,7 +23,8 @@ import jakarta.servlet.annotation.WebListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.kafka.test.EmbeddedKafkaZKBroker; +import org.springframework.kafka.test.EmbeddedKafkaBroker; +import org.springframework.kafka.test.EmbeddedKafkaKraftBroker; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; @@ -32,21 +33,22 @@ public class KafkaBrokerStartStopListener implements ServletContextListener { private static final Logger LOG = LoggerFactory.getLogger(KafkaBrokerStartStopListener.class); - private EmbeddedKafkaZKBroker embeddedKafkaBroker; + private EmbeddedKafkaBroker embeddedKafkaBroker; @Override public void contextInitialized(final ServletContextEvent sce) { WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext()); - embeddedKafkaBroker = new EmbeddedKafkaZKBroker( + embeddedKafkaBroker = new EmbeddedKafkaKraftBroker( + 1, 1, - false, ctx.getEnvironment().getProperty("kafka.topics", String[].class)). + // this call is useless with EmbeddedKafkaKraftBroker, there is no way to set fixed port kafkaPorts(ctx.getEnvironment().getProperty("kafka.port", Integer.class)); embeddedKafkaBroker.afterPropertiesSet(); - LOG.info("Kafka broker successfully (re)started"); + LOG.info("Kafka broker successfully (re)started on {}", embeddedKafkaBroker.getBrokersAsString()); } @Override diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/LDAPStartStopListener.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/LDAPStartStopListener.java index d7781dd7fce..e4fee191df4 100644 --- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/LDAPStartStopListener.java +++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/LDAPStartStopListener.java @@ -74,6 +74,8 @@ public void contextInitialized(final ServletContextEvent sce) { ldapServer = new InMemoryDirectoryServer(config); ldapServer.importFromLDIF(false, ctx.getResource("classpath:/content.ldif").getFile()); ldapServer.startListening(); + + LOG.info("LDAP server successfully (re)started"); } catch (Exception e) { LOG.error("Fatal error in context init", e); throw new RuntimeException(e); @@ -84,6 +86,8 @@ public void contextInitialized(final ServletContextEvent sce) { public void contextDestroyed(final ServletContextEvent sce) { if (ldapServer != null) { ldapServer.shutDown(true); + + LOG.info("LDAP server successfully stopped"); } } } diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/SyncopeBuildToolsApplication.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/SyncopeBuildToolsApplication.java index 72d42eff1fa..dec0322d4dd 100644 --- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/SyncopeBuildToolsApplication.java +++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/SyncopeBuildToolsApplication.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.fit.buildtools; -import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRegistration; @@ -32,19 +31,21 @@ import org.apache.cxf.jaxws.EndpointImpl; import org.apache.syncope.fit.buildtools.cxf.DateParamConverterProvider; import org.apache.syncope.fit.buildtools.cxf.GreenMailService; +import org.apache.syncope.fit.buildtools.cxf.KafkaService; import org.apache.syncope.fit.buildtools.cxf.ProvisioningImpl; import org.apache.syncope.fit.buildtools.cxf.UserService; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; +import org.springframework.boot.http.converter.autoconfigure.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration; +import org.springframework.boot.webmvc.autoconfigure.error.ErrorMvcAutoConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.jdbc.datasource.DriverManagerDataSource; +import tools.jackson.jakarta.rs.json.JacksonJsonProvider; @SpringBootApplication(exclude = { ErrorMvcAutoConfiguration.class, @@ -93,6 +94,11 @@ public GreenMailService greenMailService() { return new GreenMailService(); } + @Bean + public KafkaService kafkaService() { + return new KafkaService(); + } + @Bean public UserService userService() { return new UserService(); @@ -101,6 +107,7 @@ public UserService userService() { @Bean public Server restProvisioning( final GreenMailService greenMailService, + final KafkaService kafkaService, final UserService userService, final Bus bus, final ApplicationContext ctx) { @@ -110,7 +117,7 @@ public Server restProvisioning( restProvisioning.setBus(bus); restProvisioning.setAddress("/rest"); restProvisioning.setStaticSubresourceResolution(true); - restProvisioning.setServiceBeans(List.of(greenMailService, userService)); + restProvisioning.setServiceBeans(List.of(greenMailService, kafkaService, userService)); restProvisioning.setProviders(List.of(new JacksonJsonProvider(), new DateParamConverterProvider())); return restProvisioning.create(); } diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/KafkaService.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/KafkaService.java new file mode 100644 index 00000000000..77d6a267106 --- /dev/null +++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/KafkaService.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.fit.buildtools.cxf; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import org.springframework.kafka.test.EmbeddedKafkaBroker; + +@Path("kafka") +public class KafkaService { + + @Path("brokers") + @GET + public String getBrokers() { + return System.getProperty(EmbeddedKafkaBroker.SPRING_EMBEDDED_KAFKA_BROKERS); + } +} diff --git a/fit/build-tools/src/main/resources/log4j2.xml b/fit/build-tools/src/main/resources/log4j2.xml deleted file mode 100644 index 759f855758e..00000000000 --- a/fit/build-tools/src/main/resources/log4j2.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - %d{HH:mm:ss.SSS} %-5level %logger - %msg%n - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fit/build-tools/src/main/resources/logback-spring.xml b/fit/build-tools/src/main/resources/logback-spring.xml new file mode 100644 index 00000000000..0ad2aa42dd5 --- /dev/null +++ b/fit/build-tools/src/main/resources/logback-spring.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + DEBUG + + + ${CONSOLE_LOG_PATTERN} + utf8 + + + + + + + + + + + + + + + + + + + + + diff --git a/fit/build-tools/src/main/webapp/WEB-INF/web.xml b/fit/build-tools/src/main/webapp/WEB-INF/web.xml index 8d839fa302e..e3c74846a5c 100644 --- a/fit/build-tools/src/main/webapp/WEB-INF/web.xml +++ b/fit/build-tools/src/main/webapp/WEB-INF/web.xml @@ -20,8 +20,8 @@ under the License. Apache Syncope ${syncope.version} Build Tools diff --git a/fit/console-reference/pom.xml b/fit/console-reference/pom.xml index 6ab6c27b20e..964e8278619 100644 --- a/fit/console-reference/pom.xml +++ b/fit/console-reference/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-fit - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope FIT Console Reference diff --git a/fit/console-reference/src/main/webapp/WEB-INF/web.xml b/fit/console-reference/src/main/webapp/WEB-INF/web.xml index 355a24e1d2e..8e44b7a7a0c 100644 --- a/fit/console-reference/src/main/webapp/WEB-INF/web.xml +++ b/fit/console-reference/src/main/webapp/WEB-INF/web.xml @@ -20,8 +20,8 @@ under the License. Apache Syncope ${syncope.version} Console diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml index b36582678b1..6d4d108a0bf 100644 --- a/fit/core-reference/pom.xml +++ b/fit/core-reference/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-fit - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope FIT Core Reference @@ -69,11 +69,6 @@ under the License. syncope-core-self-keymaster-starter ${project.version} - - org.apache.syncope.core - syncope-core-metrics-starter - ${project.version} - org.apache.syncope.common.keymaster @@ -1106,6 +1101,15 @@ under the License. + + org.springframework.boot + spring-boot-jdbc + + + org.springframework.boot + spring-boot-hibernate + + org.apache.syncope.core syncope-core-persistence-neo4j @@ -1219,12 +1223,20 @@ under the License. payara-it + + embedded,payara + + org.apache.syncope.core syncope-core-starter ${project.version} + + org.springframework.boot + spring-boot-starter-tomcat + org.apache.tomcat.embed tomcat-embed-el @@ -1328,13 +1340,17 @@ under the License. syncope-core-starter ${project.version} + + org.springframework.boot + spring-boot-starter-tomcat + org.apache.tomcat.embed tomcat-embed-el org.springframework.boot - spring-boot-starter-tomcat + spring-boot-starter-micrometer-metrics @@ -1395,7 +1411,7 @@ under the License. wildfly38x - https://github.com/wildfly/wildfly/releases/download/${wildfly.version}/wildfly-${wildfly.version}.zip + https://github.com/wildfly/wildfly/releases/download/${wildfly.version}/wildfly-preview-${wildfly.version}.zip ${settings.localRepository}/org/codehaus/cargo/cargo-container-archives ${project.build.directory}/cargo/extract @@ -1797,6 +1813,12 @@ under the License. + + org.apache.syncope.core + syncope-core-metrics-starter + ${project.version} + + org.apache.syncope.ext.flowable syncope-ext-flowable-rest-cxf diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DebeziumLiveSyncDeltaMapper.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DebeziumLiveSyncDeltaMapper.java index 959a930318e..12d9991e9e3 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DebeziumLiveSyncDeltaMapper.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DebeziumLiveSyncDeltaMapper.java @@ -18,9 +18,6 @@ */ package org.apache.syncope.fit.core.reference; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; -import java.io.IOException; import java.util.Optional; import org.apache.syncope.common.lib.to.OrgUnit; import org.apache.syncope.common.lib.to.Provision; @@ -37,6 +34,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; public class DebeziumLiveSyncDeltaMapper implements LiveSyncDeltaMapper { @@ -69,7 +69,7 @@ public SyncDelta map(final LiveSyncDelta liveSyncDelta, final Provision provisio JsonNode payload; try { payload = JSON_MAPPER.readTree(value).get("payload"); - } catch (IOException e) { + } catch (JacksonException e) { throw new IllegalStateException("Could not parse the received value as JSON", e); } @@ -84,7 +84,7 @@ public SyncDelta map(final LiveSyncDelta liveSyncDelta, final Provision provisio String element = syncDeltaType == SyncDeltaType.DELETE ? "before" : "after"; - Uid uid = new Uid(payload.get(element).get("id").asText()); + Uid uid = new Uid(payload.get(element).get("id").asString()); syncDeltaBuilder.setUid(uid); ConnectorObjectBuilder connObjectBuilder = new ConnectorObjectBuilder(). @@ -94,15 +94,15 @@ public SyncDelta map(final LiveSyncDelta liveSyncDelta, final Provision provisio if (syncDeltaType != SyncDeltaType.DELETE) { connObjectBuilder. addAttribute(AttributeBuilder.build( - "email", payload.get(element).get("email").asText())). + "email", payload.get(element).get("email").asString())). addAttribute(AttributeBuilder.build( - "givenName", payload.get(element).get("first_name").asText())). + "givenName", payload.get(element).get("first_name").asString())). addAttribute(AttributeBuilder.build( - "lastName", payload.get(element).get("last_name").asText())). + "lastName", payload.get(element).get("last_name").asString())). addAttribute(AttributeBuilder.build( "fullname", - payload.get(element).get("first_name").asText() + " " - + payload.get(element).get("last_name").asText())); + payload.get(element).get("first_name").asString() + " " + + payload.get(element).get("last_name").asString())); } syncDeltaBuilder.setObject(connObjectBuilder.build()); diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/SampleReportJobDelegate.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/SampleReportJobDelegate.java index 84c1093310e..cc51d2bebfa 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/SampleReportJobDelegate.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/SampleReportJobDelegate.java @@ -18,10 +18,6 @@ */ package org.apache.syncope.fit.core.reference; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.fasterxml.jackson.dataformat.csv.CsvMapper; -import com.fasterxml.jackson.dataformat.csv.CsvSchema; import java.io.IOException; import java.io.OutputStream; import org.apache.pdfbox.pdmodel.PDDocument; @@ -35,6 +31,10 @@ import org.apache.syncope.core.provisioning.api.job.report.ReportConfClass; import org.apache.syncope.core.provisioning.java.job.report.AbstractReportJobDelegate; import org.springframework.http.MediaType; +import tools.jackson.core.StreamWriteFeature; +import tools.jackson.databind.ObjectWriter; +import tools.jackson.dataformat.csv.CsvMapper; +import tools.jackson.dataformat.csv.CsvSchema; @ReportConfClass(SampleReportConf.class) public class SampleReportJobDelegate extends AbstractReportJobDelegate { @@ -82,8 +82,7 @@ private void generateSamplePdfContent(final OutputStream os) throws IOException } private void generateSampleCsvContent(final OutputStream os) throws IOException { - CsvMapper mapper = new CsvMapper(); - mapper.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true); + CsvMapper mapper = CsvMapper.builder().configure(StreamWriteFeature.IGNORE_UNKNOWN, true).build(); CsvSchema schema = CsvSchema.builder(). setUseHeader(true). diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestLiveSyncDeltaMapper.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestLiveSyncDeltaMapper.java index 679ae0eeefc..7bb08f70cd9 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestLiveSyncDeltaMapper.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestLiveSyncDeltaMapper.java @@ -18,9 +18,6 @@ */ package org.apache.syncope.fit.core.reference; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; -import java.io.IOException; import java.util.Optional; import org.apache.syncope.common.lib.to.OrgUnit; import org.apache.syncope.common.lib.to.Provision; @@ -37,6 +34,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; public class TestLiveSyncDeltaMapper implements LiveSyncDeltaMapper { @@ -69,28 +69,28 @@ public SyncDelta map(final LiveSyncDelta liveSyncDelta, final Provision provisio JsonNode tree; try { tree = JSON_MAPPER.readTree(value); - } catch (IOException e) { + } catch (JacksonException e) { throw new IllegalStateException("Could not parse the received value as JSON", e); } SyncDeltaBuilder builder = new SyncDeltaBuilder(). setToken(new SyncToken(timestamp)). - setDeltaType(SyncDeltaType.valueOf(tree.get("type").asText())); + setDeltaType(SyncDeltaType.valueOf(tree.get("type").asString())); if (ObjectClass.ACCOUNT.equals(liveSyncDelta.getObjectClass())) { - Uid uid = new Uid(tree.get("username").asText()); + Uid uid = new Uid(tree.get("username").asString()); builder.setObject(new ConnectorObjectBuilder(). setObjectClass(liveSyncDelta.getObjectClass()). setUid(uid). setName(uid.getUidValue()). - addAttribute(AttributeBuilder.build("email", tree.get("email").asText())). - addAttribute(AttributeBuilder.build("givenName", tree.get("givenName").asText())). - addAttribute(AttributeBuilder.build("lastName", tree.get("lastName").asText())). + addAttribute(AttributeBuilder.build("email", tree.get("email").asString())). + addAttribute(AttributeBuilder.build("givenName", tree.get("givenName").asString())). + addAttribute(AttributeBuilder.build("lastName", tree.get("lastName").asString())). addAttribute(AttributeBuilder.build( "fullname", - tree.get("givenName").asText() + " " + tree.get("lastName").asText())). + tree.get("givenName").asString() + " " + tree.get("lastName").asString())). build()); } else if (ObjectClass.GROUP.equals(liveSyncDelta.getObjectClass())) { - Uid uid = new Uid(tree.get("name").asText()); + Uid uid = new Uid(tree.get("name").asString()); builder.setObject(new ConnectorObjectBuilder(). setObjectClass(liveSyncDelta.getObjectClass()). setUid(uid). diff --git a/fit/core-reference/src/main/resources/core-embedded.properties b/fit/core-reference/src/main/resources/core-embedded.properties index 42c2f8c1dfa..608291916e9 100644 --- a/fit/core-reference/src/main/resources/core-embedded.properties +++ b/fit/core-reference/src/main/resources/core-embedded.properties @@ -37,7 +37,7 @@ persistence.domain[0].jdbcDriver=org.postgresql.Driver persistence.domain[0].jdbcURL=jdbc:postgresql://localhost:5432/syncope?stringtype=unspecified persistence.domain[0].dbUsername=syncope persistence.domain[0].dbPassword=syncope -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary +persistence.domain[0].databasePlatform=org.apache.syncope.core.persistence.jpa.hibernate.SyncopePostgreSQLDialect persistence.domain[0].poolMaxActive=20 persistence.domain[0].poolMinIdle=5 @@ -46,7 +46,7 @@ persistence.domain[1].jdbcDriver=org.postgresql.Driver persistence.domain[1].jdbcURL=jdbc:postgresql://localhost:5432/syncopetwo?stringtype=unspecified persistence.domain[1].dbUsername=syncopetwo persistence.domain[1].dbPassword=syncopetwo -persistence.domain[1].databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary +persistence.domain[1].databasePlatform=org.apache.syncope.core.persistence.jpa.hibernate.SyncopePostgreSQLDialect persistence.domain[1].poolMaxActive=20 persistence.domain[1].poolMinIdle=5 persistence.domain[1].adminPassword=2AA60A8FF7FCD473D321E0146AFD9E26DF395147 diff --git a/fit/core-reference/src/main/resources/core-mariadb.properties b/fit/core-reference/src/main/resources/core-mariadb.properties index b435aca1042..39b6ed23d1b 100644 --- a/fit/core-reference/src/main/resources/core-mariadb.properties +++ b/fit/core-reference/src/main/resources/core-mariadb.properties @@ -25,7 +25,7 @@ persistence.domain[0].jdbcDriver=org.mariadb.jdbc.Driver persistence.domain[0].jdbcURL=jdbc:mariadb://${DB_CONTAINER_IP}:3306/syncope?characterEncoding=UTF-8 persistence.domain[0].dbUsername=syncope persistence.domain[0].dbPassword=syncope -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3) +persistence.domain[0].databasePlatform=org.hibernate.dialect.MariaDBDialect persistence.domain[0].orm=META-INF/mariadb/spring-orm.xml persistence.domain[0].poolMaxActive=10 persistence.domain[0].poolMinIdle=2 @@ -35,7 +35,7 @@ persistence.domain[1].jdbcDriver=org.mariadb.jdbc.Driver persistence.domain[1].jdbcURL=jdbc:mariadb://${DB_CONTAINER_IP}:3306/syncopetwo?characterEncoding=UTF-8 persistence.domain[1].dbUsername=syncopetwo persistence.domain[1].dbPassword=syncopetwo -persistence.domain[1].databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3) +persistence.domain[1].databasePlatform=org.hibernate.dialect.MariaDBDialect persistence.domain[1].orm=META-INF/mariadb/spring-orm.xml persistence.domain[1].poolMaxActive=20 persistence.domain[1].poolMinIdle=5 diff --git a/fit/core-reference/src/main/resources/core-mysql.properties b/fit/core-reference/src/main/resources/core-mysql.properties index 42299ebddf1..6e1f2ca5a9f 100644 --- a/fit/core-reference/src/main/resources/core-mysql.properties +++ b/fit/core-reference/src/main/resources/core-mysql.properties @@ -25,7 +25,7 @@ persistence.domain[0].jdbcDriver=com.mysql.cj.jdbc.Driver persistence.domain[0].jdbcURL=jdbc:mysql://${DB_CONTAINER_IP}:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8 persistence.domain[0].dbUsername=syncope persistence.domain[0].dbPassword=syncope -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3,useSetStringForClobs=true) +persistence.domain[0].databasePlatform=org.hibernate.dialect.MySQLDialect persistence.domain[0].orm=META-INF/mysql/spring-orm.xml persistence.domain[0].poolMaxActive=20 persistence.domain[0].poolMinIdle=5 @@ -35,7 +35,7 @@ persistence.domain[1].jdbcDriver=com.mysql.cj.jdbc.Driver persistence.domain[1].jdbcURL=jdbc:mysql://${DB_CONTAINER_IP}:3306/syncopetwo?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8 persistence.domain[1].dbUsername=syncopetwo persistence.domain[1].dbPassword=syncopetwo -persistence.domain[1].databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3,useSetStringForClobs=true) +persistence.domain[1].databasePlatform=org.hibernate.dialect.MySQLDialect persistence.domain[1].orm=META-INF/mysql/spring-orm.xml persistence.domain[1].poolMaxActive=20 persistence.domain[1].poolMinIdle=5 diff --git a/fit/core-reference/src/main/resources/core-oracle.properties b/fit/core-reference/src/main/resources/core-oracle.properties index 52aac1e27a4..716d72fa33d 100644 --- a/fit/core-reference/src/main/resources/core-oracle.properties +++ b/fit/core-reference/src/main/resources/core-oracle.properties @@ -26,7 +26,7 @@ persistence.domain[0].jdbcURL=jdbc:oracle:thin:@${DB_CONTAINER_IP}:1521/FREEPDB1 persistence.domain[0].dbSchema=SYNCOPE persistence.domain[0].dbUsername=syncope persistence.domain[0].dbPassword=syncope -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.OracleDictionary +persistence.domain[0].databasePlatform=org.hibernate.dialect.OracleDialect persistence.domain[0].orm=META-INF/oracle/spring-orm.xml persistence.domain[0].poolMaxActive=20 persistence.domain[0].poolMinIdle=5 @@ -37,7 +37,7 @@ persistence.domain[1].jdbcURL=jdbc:oracle:thin:@${DB_CONTAINER_IP}:1521/FREEPDB1 persistence.domain[1].dbSchema=SYNCOPETWO persistence.domain[1].dbUsername=syncopetwo persistence.domain[1].dbPassword=syncopetwo -persistence.domain[1].databasePlatform=org.apache.openjpa.jdbc.sql.OracleDictionary +persistence.domain[1].databasePlatform=org.hibernate.dialect.OracleDialect persistence.domain[1].orm=META-INF/oracle/spring-orm.xml persistence.domain[1].poolMaxActive=20 persistence.domain[1].poolMinIdle=5 diff --git a/fit/core-reference/src/main/resources/core-payara.properties b/fit/core-reference/src/main/resources/core-payara.properties new file mode 100644 index 00000000000..db449da9333 --- /dev/null +++ b/fit/core-reference/src/main/resources/core-payara.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +persistence.cacheProvider=com.hazelcast.cache.HazelcastCachingProvider diff --git a/fit/core-reference/src/main/resources/core-wildfly.properties b/fit/core-reference/src/main/resources/core-wildfly.properties index b1b34b58f15..c1bb639f818 100644 --- a/fit/core-reference/src/main/resources/core-wildfly.properties +++ b/fit/core-reference/src/main/resources/core-wildfly.properties @@ -14,8 +14,6 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -persistence.metaDataFactory=jpa(URLs=vfs:${project.build.directory}/cargo/configurations/wildfly38x/deployments/syncope.war/WEB-INF/lib/syncope-core-persistence-jpa-${syncope.version}.jar; vfs:${project.build.directory}/cargo/configurations/wildfly38x/deployments/syncope.war/WEB-INF/lib/syncope-core-self-keymaster-starter-${syncope.version}.jar, Resources=##orm##) - javadocPaths=/WEB-INF/lib/syncope-common-idrepo-rest-api-${syncope.version}-javadoc.jar,\ /WEB-INF/lib/syncope-common-idm-rest-api-${syncope.version}-javadoc.jar,\ /WEB-INF/lib/syncope-common-am-rest-api-${syncope.version}-javadoc.jar diff --git a/fit/core-reference/src/main/resources/log4j2.xml b/fit/core-reference/src/main/resources/log4j2.xml index 3694e4dddf1..5dc2e4fc08a 100644 --- a/fit/core-reference/src/main/resources/log4j2.xml +++ b/fit/core-reference/src/main/resources/log4j2.xml @@ -81,12 +81,28 @@ under the License. - + - + + + + + + + + + diff --git a/fit/core-reference/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/fit/core-reference/src/main/webapp/WEB-INF/jboss-deployment-structure.xml index 0191a231ac7..8edc352f685 100644 --- a/fit/core-reference/src/main/webapp/WEB-INF/jboss-deployment-structure.xml +++ b/fit/core-reference/src/main/webapp/WEB-INF/jboss-deployment-structure.xml @@ -29,19 +29,11 @@ under the License. - + - - - - - - - - diff --git a/fit/core-reference/src/main/webapp/WEB-INF/web.xml b/fit/core-reference/src/main/webapp/WEB-INF/web.xml index e382bd39d21..ac990a2bf7b 100644 --- a/fit/core-reference/src/main/webapp/WEB-INF/web.xml +++ b/fit/core-reference/src/main/webapp/WEB-INF/web.xml @@ -20,8 +20,8 @@ under the License. Apache Syncope ${syncope.version} Core diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java index 362a6a58987..9a4921965df 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java @@ -21,9 +21,8 @@ import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; import com.nimbusds.jose.JWSAlgorithm; import com.unboundid.ldap.sdk.AddRequest; import com.unboundid.ldap.sdk.Attribute; @@ -47,6 +46,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; +import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -60,7 +60,10 @@ import org.apache.commons.lang3.mutable.Mutable; import org.apache.commons.lang3.mutable.MutableObject; import org.apache.commons.lang3.tuple.Pair; +import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.transport.http.HTTPConduit; +import org.apache.cxf.transport.https.InsecureTrustManager; import org.apache.syncope.client.lib.SyncopeAnonymousClient; import org.apache.syncope.client.lib.SyncopeClient; import org.apache.syncope.client.lib.SyncopeClientFactoryBean; @@ -71,6 +74,8 @@ import org.apache.syncope.common.keymaster.client.zookeeper.ZookeeperKeymasterClientContext; import org.apache.syncope.common.lib.Attr; import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.policy.AccessPolicyTO; import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO; import org.apache.syncope.common.lib.policy.AuthPolicyTO; @@ -91,6 +96,7 @@ import org.apache.syncope.common.lib.to.AuditEventTO; import org.apache.syncope.common.lib.to.ClientAppTO; import org.apache.syncope.common.lib.to.ConnInstanceTO; +import org.apache.syncope.common.lib.to.EntityTO; import org.apache.syncope.common.lib.to.ExecTO; import org.apache.syncope.common.lib.to.GroupTO; import org.apache.syncope.common.lib.to.ImplementationTO; @@ -192,6 +198,8 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.test.context.support.TestPropertySourceUtils; import org.springframework.util.function.ThrowingFunction; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; @SpringJUnitConfig( classes = { CoreITContext.class, SelfKeymasterClientContext.class, ZookeeperKeymasterClientContext.class }, @@ -220,7 +228,7 @@ public void initialize(final ConfigurableApplicationContext ctx) { protected static final Logger LOG = LoggerFactory.getLogger(AbstractITCase.class); - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); protected static final String ADMIN_UNAME = "admin"; @@ -398,11 +406,9 @@ public void initialize(final ConfigurableApplicationContext ctx) { private static int POP3_PORT; - protected static boolean IS_FLOWABLE_ENABLED = false; - - protected static boolean IS_ELASTICSEARCH_ENABLED = false; + protected static boolean IS_DEPLOYED_IN_PAYARA = false; - protected static boolean IS_OPENSEARCH_ENABLED = false; + protected static boolean IS_FLOWABLE_ENABLED = false; protected static boolean IS_EXT_SEARCH_ENABLED = false; @@ -451,6 +457,25 @@ private static void initExtSearch( assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(exec.getStatus())); } + @BeforeAll + public static void jakartaEEContainerCheck() throws IOException { + TLSClientParameters tlsParams = new TLSClientParameters(); + tlsParams.setTrustManagers(InsecureTrustManager.getNoOpX509TrustManagers()); + tlsParams.setDisableCNCheck(true); + + WebClient webClient = WebClient.create("https://localhost:8181/"); + HTTPConduit conduit = WebClient.getConfig(webClient).getHttpConduit(); + conduit.getClient().setConnectionTimeout(2000); + conduit.getClient().setReceiveTimeout(2000); + conduit.setTlsClientParameters(tlsParams); + + try { + IS_DEPLOYED_IN_PAYARA = webClient.get().readEntity(String.class).contains("Payara"); + } catch (Exception e) { + LOG.debug("While checking if Syncope Core is deployed in Payara", e); + } + } + @BeforeAll public static void anonymousSetup() throws IOException { try (InputStream propStream = AbstractITCase.class.getResourceAsStream("/core.properties")) { @@ -493,32 +518,32 @@ public static void anonymousSetup() throws IOException { JsonNode beans = MAPPER.readTree(beansJSON); JsonNode uwfAdapter = beans.findValues("uwfAdapter").getFirst(); - IS_FLOWABLE_ENABLED = uwfAdapter.get("resource").asText().contains("Flowable"); + IS_FLOWABLE_ENABLED = uwfAdapter.get("resource").asString().contains("Flowable"); JsonNode anySearchDAO = beans.findValues("anySearchDAO").getFirst(); - IS_ELASTICSEARCH_ENABLED = anySearchDAO.get("type").asText().contains("Elasticsearch"); - IS_OPENSEARCH_ENABLED = anySearchDAO.get("type").asText().contains("OpenSearch"); - IS_EXT_SEARCH_ENABLED = IS_ELASTICSEARCH_ENABLED || IS_OPENSEARCH_ENABLED; + boolean isElasticsearchEnabled = anySearchDAO.get("type").asString().contains("Elasticsearch"); + boolean isOpenSearchEnabled = anySearchDAO.get("type").asString().contains("OpenSearch"); + IS_EXT_SEARCH_ENABLED = isElasticsearchEnabled || isOpenSearchEnabled; - IS_NEO4J_PERSISTENCE = anySearchDAO.get("type").asText().contains("Neo4j"); + IS_NEO4J_PERSISTENCE = anySearchDAO.get("type").asString().contains("Neo4j"); if (!IS_EXT_SEARCH_ENABLED) { return; } - SyncopeClientFactoryBean masterCF = new SyncopeClientFactoryBean().setAddress(ADDRESS); - SyncopeClientFactoryBean twoCF = new SyncopeClientFactoryBean().setAddress(ADDRESS).setDomain("Two"); - SyncopeClient masterSC = masterCF.create(ADMIN_UNAME, ADMIN_PWD); + SyncopeClient masterSC = new SyncopeClientFactoryBean().setAddress(ADDRESS). + setDomain(SyncopeConstants.MASTER_DOMAIN).create(ADMIN_UNAME, ADMIN_PWD); ImplementationService masterIS = masterSC.getService(ImplementationService.class); TaskService masterTS = masterSC.getService(TaskService.class); - SyncopeClient twoSC = twoCF.create(ADMIN_UNAME, "password2"); + SyncopeClient twoSC = new SyncopeClientFactoryBean().setAddress(ADDRESS). + setDomain("Two").create(ADMIN_UNAME, "password2"); ImplementationService twoIS = twoSC.getService(ImplementationService.class); TaskService twoTS = twoSC.getService(TaskService.class); - if (IS_ELASTICSEARCH_ENABLED) { + if (isElasticsearchEnabled) { initExtSearch(masterIS, masterTS, "org.apache.syncope.core.provisioning.java.job.ElasticsearchReindex"); initExtSearch(twoIS, twoTS, "org.apache.syncope.core.provisioning.java.job.ElasticsearchReindex"); - } else if (IS_OPENSEARCH_ENABLED) { + } else if (isOpenSearchEnabled) { initExtSearch(masterIS, masterTS, "org.apache.syncope.core.provisioning.java.job.OpenSearchReindex"); initExtSearch(twoIS, twoTS, "org.apache.syncope.core.provisioning.java.job.OpenSearchReindex"); } @@ -588,6 +613,12 @@ public static void restSetup() { WA_CONFIG_SERVICE = ADMIN_CLIENT.getService(WAConfigService.class); } + protected static void awaitIfExtSearchEnabled() { + if (IS_EXT_SEARCH_ENABLED) { + await().atMost(Duration.ofSeconds(2)).pollInterval(Duration.ofSeconds(1)).until(() -> true); + } + } + protected static String getUUIDString() { return UUID.randomUUID().toString().substring(0, 8); } @@ -786,6 +817,23 @@ protected static List parseBatchResponse(final Response respo (InputStream) response.getEntity(), response.getMediaType(), new BatchResponseItem()); } + protected static E getEntity(final ProvisioningResult result) { + List failures = result.getPropagationStatuses().stream(). + filter(status -> status.getStatus() != ExecStatus.SUCCESS). + map(status -> "Propagation to " + status.getResource() + + " was not successful: " + status.getFailureReason()).toList(); + if (!failures.isEmpty()) { + fail(String.join("\n", failures)); + } + return result.getEntity(); + } + + protected static void assertSuccessful(final ExecTO execution) { + if (!ExecStatus.SUCCESS.equals(ExecStatus.valueOf(execution.getStatus()))) { + fail(execution.getRefDesc() + " failed:\n" + execution.getMessage()); + } + } + private static T execOnLDAP( final String bindDn, final String bindPassword, @@ -1064,13 +1112,7 @@ protected static TicketExpirationPolicyTO buildTicketExpirationPolicyTO() { } protected static List query(final AuditQuery query, final int maxWaitSeconds) { - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); int i = 0; List results = List.of(); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractUIITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractUIITCase.java index cf1db387307..f5536feb24c 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractUIITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractUIITCase.java @@ -23,8 +23,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.json.JsonMapper; import jakarta.ws.rs.core.MediaType; import java.io.IOException; import java.io.InputStream; @@ -37,6 +35,7 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.rest.api.service.SyncopeService; import org.apache.wicket.Component; import org.apache.wicket.MarkupContainer; @@ -50,12 +49,14 @@ import org.junit.jupiter.api.BeforeAll; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; public abstract class AbstractUIITCase { protected static final Logger LOG = LoggerFactory.getLogger(AbstractUIITCase.class); - protected static final JsonMapper JSON_MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper JSON_MAPPER = new SyncopeJsonMapper(); protected static final String ADMIN_UNAME = "admin"; @@ -108,11 +109,11 @@ public static void anonymousSetup() throws IOException { JsonNode beans = JSON_MAPPER.readTree(beansJSON); JsonNode uwfAdapter = beans.findValues("uwfAdapter").getFirst(); - IS_FLOWABLE_ENABLED = uwfAdapter.get("resource").asText().contains("Flowable"); + IS_FLOWABLE_ENABLED = uwfAdapter.get("resource").asString().contains("Flowable"); JsonNode anySearchDAO = beans.findValues("anySearchDAO").getFirst(); - IS_EXT_SEARCH_ENABLED = anySearchDAO.get("type").asText().contains("Elasticsearch") - || anySearchDAO.get("type").asText().contains("OpenSearch"); + IS_EXT_SEARCH_ENABLED = anySearchDAO.get("type").asString().contains("Elasticsearch") + || anySearchDAO.get("type").asString().contains("OpenSearch"); } protected static Component findComponentByProp( diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyTypeITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyTypeITCase.java index d6498d07955..2d350a06b76 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyTypeITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyTypeITCase.java @@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -104,12 +105,9 @@ public void createInvalidKind() { AnyTypeTO newType = new AnyTypeTO(); newType.setKey("new type"); newType.setKind(AnyTypeKind.USER); - try { - ANY_TYPE_SERVICE.create(newType); - fail("This should not happen"); - } catch (SyncopeClientException e) { - assertEquals(ClientExceptionType.InvalidAnyType, e.getType()); - } + + SyncopeClientException e = assertThrows(SyncopeClientException.class, () -> ANY_TYPE_SERVICE.create(newType)); + assertEquals(ClientExceptionType.InvalidAnyType, e.getType()); } @Test @@ -117,12 +115,9 @@ public void createInvalidName() { AnyTypeTO newType = new AnyTypeTO(); newType.setKey("GROUP"); newType.setKind(AnyTypeKind.GROUP); - try { - ANY_TYPE_SERVICE.create(newType); - fail("This should not happen"); - } catch (SyncopeClientException e) { - assertEquals(ClientExceptionType.EntityExists, e.getType()); - } + + SyncopeClientException e = assertThrows(SyncopeClientException.class, () -> ANY_TYPE_SERVICE.create(newType)); + assertEquals(ClientExceptionType.EntityExists, e.getType()); } @Test @@ -194,7 +189,7 @@ public void issueSYNCOPE1472() { anyTypeTO.getClasses().remove("csv"); ANY_TYPE_SERVICE.update(anyTypeTO); - assertFalse(ANY_TYPE_SERVICE.read(PRINTER).getClasses().contains("csv"), + assertFalse(ANY_TYPE_SERVICE.read(PRINTER).getClasses().contains("csv"), "Should not contain removed any type classes"); } } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java index 36598d367cb..89dc686a59a 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java @@ -27,7 +27,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import com.fasterxml.jackson.core.JsonProcessingException; import jakarta.ws.rs.core.Response; import java.io.File; import java.io.IOException; @@ -224,7 +223,7 @@ public void anyObjectReadAndSearchYieldsNoAudit() { } @Test - public void findByConnector() throws JsonProcessingException { + public void findByConnector() { String connectorKey = "74141a3b-0762-4720-a4aa-fc3e374ef3ef"; AuditQuery query = new AuditQuery.Builder(). @@ -242,13 +241,13 @@ public void findByConnector() throws JsonProcessingException { String originalDisplayName = ldapConn.getDisplayName(); Set originalCapabilities = new HashSet<>(ldapConn.getCapabilities()); ConnConfProperty originalConfProp = SerializationUtils.clone( - ldapConn.getConf("maintainPosixGroupMembership").get()); + ldapConn.getConf("maintainPosixGroupMembership").orElseThrow()); assertEquals(1, originalConfProp.getValues().size()); assertEquals("false", originalConfProp.getValues().getFirst()); ldapConn.setDisplayName(originalDisplayName + " modified"); ldapConn.getCapabilities().clear(); - ldapConn.getConf("maintainPosixGroupMembership").get().getValues().set(0, "true"); + ldapConn.getConf("maintainPosixGroupMembership").orElseThrow().getValues().set(0, "true"); CONNECTOR_SERVICE.update(ldapConn); ldapConn = CONNECTOR_SERVICE.read(connectorKey, null); @@ -265,7 +264,7 @@ public void findByConnector() throws JsonProcessingException { ldapConn = CONNECTOR_SERVICE.read(connectorKey, null); assertEquals(originalDisplayName, ldapConn.getDisplayName()); assertEquals(originalCapabilities, ldapConn.getCapabilities()); - assertEquals(originalConfProp, ldapConn.getConf("maintainPosixGroupMembership").get()); + assertEquals(originalConfProp, ldapConn.getConf("maintainPosixGroupMembership").orElseThrow()); } @Test @@ -391,13 +390,7 @@ public void saveAuditEvent() { auditEvent.setOutput(UUID.randomUUID().toString()); assertDoesNotThrow(() -> AUDIT_SERVICE.create(auditEvent)); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult events = AUDIT_SERVICE.search(new AuditQuery.Builder(). size(1). @@ -426,13 +419,7 @@ public void saveAuthEvent() { auditEvent.setOutput(UUID.randomUUID().toString()); assertDoesNotThrow(() -> AUDIT_SERVICE.create(auditEvent)); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult events = AUDIT_SERVICE.search(new AuditQuery.Builder(). size(1). @@ -598,13 +585,7 @@ public void issueSYNCOPE1695() { build()).getEntity(); // search by empty type and category events and get both events on testfromLDAP - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); assertEquals(1, AUDIT_SERVICE.search(new AuditQuery.Builder(). entityKey(pullFromLDAP.getKey()). diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java index 30b4773b750..4577740d23a 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java @@ -179,13 +179,7 @@ public void userSearch() { UserService userService2 = CLIENT_FACTORY.create(userTO.getUsername(), "password123"). getService(UserService.class); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult matchingUsers = userService2.search(new AnyQuery.Builder(). realm(SyncopeConstants.ROOT_REALM). @@ -481,13 +475,7 @@ public void asGroupOwner() { assertEquals(2, member.getMemberships().size()); String memberKey = member.getKey(); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult matching = USER_SERVICE.search( new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BatchITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BatchITCase.java index 6d512635649..9a511f3517c 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BatchITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BatchITCase.java @@ -24,8 +24,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.ws.rs.HttpMethod; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; @@ -62,10 +60,11 @@ import org.apache.syncope.common.rest.api.service.UserService; import org.apache.syncope.fit.AbstractITCase; import org.junit.jupiter.api.Test; +import tools.jackson.core.type.TypeReference; public class BatchITCase extends AbstractITCase { - private static String requestBody(final String boundary) throws JsonProcessingException { + private static String requestBody(final String boundary) { List reqItems = new ArrayList<>(); // 1. create user diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/CommandITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/CommandITCase.java index 35799f7b45c..1b4e311f187 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/CommandITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/CommandITCase.java @@ -25,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.fail; import jakarta.ws.rs.core.Response; +import java.util.Optional; import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.command.CommandOutput; import org.apache.syncope.common.lib.command.CommandTO; @@ -112,9 +113,10 @@ public void runCommand() { assertFalse(REALM_SERVICE.search( new RealmQuery.Builder().base(printer.getRealm()).build()).getResult().isEmpty()); } finally { - if (printer != null) { - ANY_OBJECT_SERVICE.delete(printer.getKey()); - } + Optional.ofNullable(printer).ifPresent(p -> ANY_OBJECT_SERVICE.delete(p.getKey())); + + awaitIfExtSearchEnabled(); + REALM_SERVICE.delete(args.getParentRealm() + "/" + args.getRealmName()); } } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java index 45a560198a0..8e610c7b27c 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java @@ -24,7 +24,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import com.fasterxml.jackson.databind.node.ArrayNode; import jakarta.ws.rs.core.GenericType; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; @@ -61,6 +60,7 @@ import org.apache.syncope.common.rest.api.service.UserService; import org.apache.syncope.fit.AbstractITCase; import org.junit.jupiter.api.Test; +import tools.jackson.databind.node.ArrayNode; public class DynRealmITCase extends AbstractITCase { @@ -122,32 +122,30 @@ public void misc() { @Test public void delegatedAdmin() { - DynRealmTO dynRealm = null; - RoleTO role = null; - try { - // 1. create dynamic realm for all users and groups having resource-ldap assigned - dynRealm = new DynRealmTO(); - dynRealm.setKey("LDAPLovers" + getUUIDString()); - dynRealm.getDynMembershipConds().put(AnyTypeKind.USER.name(), "$resources==resource-ldap"); - dynRealm.getDynMembershipConds().put(AnyTypeKind.GROUP.name(), "$resources==resource-ldap"); - - Response response = DYN_REALM_SERVICE.create(dynRealm); - dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class); - assertNotNull(dynRealm); - - // 2. create role for such dynamic realm - role = new RoleTO(); - role.setKey("Administer LDAP" + getUUIDString()); - role.getEntitlements().add(IdRepoEntitlement.USER_SEARCH); - role.getEntitlements().add(IdRepoEntitlement.USER_READ); - role.getEntitlements().add(IdRepoEntitlement.USER_UPDATE); - role.getEntitlements().add(IdRepoEntitlement.GROUP_READ); - role.getEntitlements().add(IdRepoEntitlement.GROUP_UPDATE); - role.getDynRealms().add(dynRealm.getKey()); - - role = createRole(role); - assertNotNull(role); + // 1. create dynamic realm for all users and groups having resource-ldap assigned + DynRealmTO dynRealm = new DynRealmTO(); + dynRealm.setKey("LDAPLovers" + getUUIDString()); + dynRealm.getDynMembershipConds().put(AnyTypeKind.USER.name(), "$resources==resource-ldap"); + dynRealm.getDynMembershipConds().put(AnyTypeKind.GROUP.name(), "$resources==resource-ldap"); + + Response response = DYN_REALM_SERVICE.create(dynRealm); + dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class); + assertNotNull(dynRealm); + + // 2. create role for such dynamic realm + RoleTO role = new RoleTO(); + role.setKey("Administer LDAP" + getUUIDString()); + role.getEntitlements().add(IdRepoEntitlement.USER_SEARCH); + role.getEntitlements().add(IdRepoEntitlement.USER_READ); + role.getEntitlements().add(IdRepoEntitlement.USER_UPDATE); + role.getEntitlements().add(IdRepoEntitlement.GROUP_READ); + role.getEntitlements().add(IdRepoEntitlement.GROUP_UPDATE); + role.getDynRealms().add(dynRealm.getKey()); + + role = createRole(role); + assertNotNull(role); + try { // 3. create new user and assign the new role UserCR dynRealmAdmin = UserITCase.getUniqueSample("dynRealmAdmin@apache.org"); dynRealmAdmin.setPassword("password123"); @@ -171,13 +169,7 @@ public void delegatedAdmin() { assertNotNull(group); final String groupKey = group.getKey(); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); // 5. verify that the new user and group are found when searching by dynamic realm PagedResult matchingUsers = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql( @@ -234,12 +226,8 @@ public void delegatedAdmin() { assertNotNull(group); assertEquals("modified", group.getPlainAttr("icon").get().getValues().getFirst()); } finally { - if (role != null) { - ROLE_SERVICE.delete(role.getKey()); - } - if (dynRealm != null) { - DYN_REALM_SERVICE.delete(dynRealm.getKey()); - } + ROLE_SERVICE.delete(role.getKey()); + DYN_REALM_SERVICE.delete(dynRealm.getKey()); } } @@ -271,15 +259,11 @@ public void issueSYNCOPE1480() throws Exception { // 4a. check that Elasticsearch index was updated correctly if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } + awaitIfExtSearchEnabled(); ArrayNode dynRealms = fetchDynRealmsFromElasticsearch(user.getKey()); assertEquals(1, dynRealms.size()); - assertEquals(dynRealm.getKey(), dynRealms.get(0).asText()); + assertEquals(dynRealm.getKey(), dynRealms.get(0).asString()); } // 4b. now there is 1 realm member @@ -293,11 +277,7 @@ public void issueSYNCOPE1480() throws Exception { // 6a. check that Elasticsearch index was updated correctly if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } + awaitIfExtSearchEnabled(); ArrayNode dynRealms = fetchDynRealmsFromElasticsearch(user.getKey()); assertTrue(dynRealms.isEmpty()); @@ -333,13 +313,7 @@ public void issueSYNCOPE1806() { assertNotNull(realm2); // 2. verify that dynamic members are the same - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult matching1 = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql( SyncopeClient.getUserSearchConditionBuilder().inDynRealms(realm1.getKey()).query()).build()); @@ -358,13 +332,7 @@ public void issueSYNCOPE1806() { updateUser(userUR); // 4. verify that dynamic members are still the same - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); matching1 = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql( SyncopeClient.getUserSearchConditionBuilder().inDynRealms(realm1.getKey()).query()).build()); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java index 585c5d61c94..85c838ffc08 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java @@ -240,9 +240,10 @@ public void update() { @Test public void patch() { GroupCR createReq = getBasicSample("patch"); - createReq.setUDynMembershipCond( + createReq.getDynMembershipConds().put( + "USER", "(($groups==ebf97068-aa4b-4a85-9f01-680e8c4cf227;$resources!=ws-target-resource-1);aLong==1)"); - createReq.getADynMembershipConds().put( + createReq.getDynMembershipConds().put( PRINTER, "(($groups==ece66293-8f31-4a84-8e8d-23da36e70846;cool==ss);$resources==ws-target-resource-2);" + "$type==PRINTER"); @@ -689,7 +690,7 @@ public void uDynMembership() { assertTrue(USER_SERVICE.read("c9b2dec2-00a7-4855-97c0-d854842b4b24").getDynMemberships().isEmpty()); GroupCR groupCR = getBasicSample("uDynMembership"); - groupCR.setUDynMembershipCond("cool==true"); + groupCR.getDynMembershipConds().put("USER", "cool==true"); GroupTO group = null; try { group = createGroup(groupCR).getEntity(); @@ -701,7 +702,7 @@ public void uDynMembership() { assertTrue(memberships.stream().anyMatch(m -> m.getGroupKey().equals(groupKey))); assertEquals(1, GROUP_SERVICE.read(group.getKey()).getDynamicUserMembershipCount()); - GROUP_SERVICE.update(new GroupUR.Builder(group.getKey()).udynMembershipCond("cool==false").build()); + GROUP_SERVICE.update(new GroupUR.Builder(group.getKey()).dynMembershipCond("USER", "cool==false").build()); assertTrue(USER_SERVICE.read("c9b2dec2-00a7-4855-97c0-d854842b4b24").getDynMemberships().isEmpty()); assertEquals(0, GROUP_SERVICE.read(group.getKey()).getDynamicUserMembershipCount()); @@ -718,21 +719,15 @@ public void aDynMembership() { // 1. create group with a given aDynMembership condition GroupCR groupCR = getBasicSample("aDynMembership"); - groupCR.getADynMembershipConds().put(PRINTER, fiql); + groupCR.getDynMembershipConds().put(PRINTER, fiql); GroupTO group = createGroup(groupCR).getEntity(); - assertEquals(fiql, group.getADynMembershipConds().get(PRINTER)); + assertEquals(fiql, group.getDynMembershipConds().get(PRINTER)); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); group = GROUP_SERVICE.read(group.getKey()); String groupKey = group.getKey(); - assertEquals(fiql, group.getADynMembershipConds().get(PRINTER)); + assertEquals(fiql, group.getDynMembershipConds().get(PRINTER)); // verify that the condition is dynamically applied AnyObjectCR newAnyCR = AnyObjectITCase.getSample("aDynMembership"); @@ -755,13 +750,13 @@ public void aDynMembership() { GroupUR groupUR = new GroupUR(); groupUR.setKey(group.getKey()); - groupUR.getADynMembershipConds().put(PRINTER, fiql); + groupUR.getDynMembershipConds().put(PRINTER, fiql); group = updateGroup(groupUR).getEntity(); - assertEquals(fiql, group.getADynMembershipConds().get(PRINTER)); + assertEquals(fiql, group.getDynMembershipConds().get(PRINTER)); group = GROUP_SERVICE.read(group.getKey()); - assertEquals(fiql, group.getADynMembershipConds().get(PRINTER)); + assertEquals(fiql, group.getDynMembershipConds().get(PRINTER)); // verify that the condition is dynamically applied AnyObjectUR anyObjectUR = new AnyObjectUR(); @@ -787,7 +782,7 @@ public void aDynMembershipCount() { // Create a new printer as a dynamic member of a new group GroupCR groupCR = getBasicSample("aDynamicMembership"); String fiql = SyncopeClient.getAnyObjectSearchConditionBuilder(PRINTER).is("location").equalTo("home").query(); - groupCR.getADynMembershipConds().put(PRINTER, fiql); + groupCR.getDynMembershipConds().put(PRINTER, fiql); GroupTO group = createGroup(groupCR).getEntity(); AnyObjectCR printerCR = new AnyObjectCR(); @@ -845,22 +840,12 @@ public void capabilitiesOverride() { groupCR.getPlainAttrs().add(attr("title", "first")); groupCR.getResources().add(RESOURCE_NAME_LDAP); - ProvisioningResult result = createGroup(groupCR); - assertNotNull(result); - assertEquals(1, result.getPropagationStatuses().size()); - assertEquals(RESOURCE_NAME_LDAP, result.getPropagationStatuses().getFirst().getResource()); - assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().getFirst().getStatus()); - GroupTO group = result.getEntity(); + GroupTO group = getEntity(createGroup(groupCR)); // 2. update succeeds - result = updateGroup(new GroupUR.Builder(group.getKey()). + group = getEntity(updateGroup(new GroupUR.Builder(group.getKey()). plainAttr(new AttrPatch.Builder(attr("title", "second")).build()). - build()); - assertNotNull(result); - assertEquals(1, result.getPropagationStatuses().size()); - assertEquals(RESOURCE_NAME_LDAP, result.getPropagationStatuses().getFirst().getResource()); - assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().getFirst().getStatus()); - group = result.getEntity(); + build())); // 3. enable capability override with only search allowed ldap.setCapabilitiesOverride(Optional.of(Set.of(ConnectorCapability.SEARCH))); @@ -871,10 +856,9 @@ public void capabilitiesOverride() { assertTrue(ldap.getCapabilitiesOverride().orElseThrow().contains(ConnectorCapability.SEARCH)); // 4. update now fails - result = updateGroup(new GroupUR.Builder(group.getKey()). + ProvisioningResult result = updateGroup(new GroupUR.Builder(group.getKey()). plainAttr(new AttrPatch.Builder(attr("title", "fourth")).build()). build()); - assertNotNull(result); assertEquals(1, result.getPropagationStatuses().size()); assertEquals(RESOURCE_NAME_LDAP, result.getPropagationStatuses().getFirst().getResource()); assertEquals(ExecStatus.NOT_ATTEMPTED, result.getPropagationStatuses().getFirst().getStatus()); @@ -1073,9 +1057,9 @@ public void issueSYNCOPE632() { // 4. check that a single group exists in LDAP for the group created and updated above assertEquals(1, ldapSearch("ou=groups,o=isp", "(description=" + groupTO.getKey() + ')').getEntryCount()); } finally { - SCHEMA_SERVICE.update(SchemaType.DERIVED, orig); - Optional.ofNullable(groupTO).ifPresent(g -> GROUP_SERVICE.delete(g.getKey())); - RESOURCE_SERVICE.delete("new-ldap"); +// SCHEMA_SERVICE.update(SchemaType.DERIVED, orig); +// Optional.ofNullable(groupTO).ifPresent(g -> GROUP_SERVICE.delete(g.getKey())); +// RESOURCE_SERVICE.delete("new-ldap"); } } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java index c891ca0b321..c1c945341d2 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java @@ -277,13 +277,7 @@ public void domainCRUD() throws Exception { assertNotNull(user); assertEquals("monteverdi", user.getUsername()); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); users = USER_SERVICE.search( new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).page(1).size(1).build()); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java index 6b083d47feb..a6ead31375e 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java @@ -28,7 +28,6 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeFalse; -import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -77,6 +76,7 @@ import org.apache.syncope.fit.core.reference.LinkedAccountSampleInboundCorrelationRuleConf; import org.identityconnectors.framework.common.objects.OperationalAttributes; import org.junit.jupiter.api.Test; +import tools.jackson.databind.node.ObjectNode; public class LinkedAccountITCase extends AbstractITCase { @@ -524,36 +524,35 @@ public void pull() { accounts = USER_SERVICE.read("vivaldi").getLinkedAccounts(); assertEquals(3, accounts.size()); - Optional firstAccount = accounts.stream(). + LinkedAccountTO account1 = accounts.stream(). filter(account -> user1Key.equals(account.getConnObjectKeyValue())). - findFirst(); - assertTrue(firstAccount.isPresent()); - assertFalse(firstAccount.orElseThrow().isSuspended()); - assertEquals(RESOURCE_NAME_REST, firstAccount.orElseThrow().getResource()); - assertEquals("linkedaccount1", firstAccount.orElseThrow().getUsername()); + findFirst().orElseThrow(); + assertFalse(account1.isSuspended()); + assertEquals(RESOURCE_NAME_REST, account1.getResource()); + assertEquals("linkedaccount1", account1.getUsername()); assertEquals( "Pasquale", - firstAccount.orElseThrow().getPlainAttr("firstname").orElseThrow().getValues().getFirst()); + account1.getPlainAttr("firstname").orElseThrow().getValues().getFirst()); - Optional secondAccount = accounts.stream(). + LinkedAccountTO account2 = accounts.stream(). filter(account -> user2Key.equals(account.getConnObjectKeyValue())). - findFirst(); - assertTrue(secondAccount.isPresent()); - assertFalse(secondAccount.orElseThrow().isSuspended()); - assertEquals(RESOURCE_NAME_REST, secondAccount.orElseThrow().getResource()); - assertNull(secondAccount.orElseThrow().getUsername()); + findFirst().orElseThrow(); + assertFalse(account2.isSuspended()); + assertEquals(RESOURCE_NAME_REST, account2.getResource()); + assertNull(account2.getUsername()); assertEquals( "Giovannino", - secondAccount.orElseThrow().getPlainAttr("firstname").orElseThrow().getValues().getFirst()); + account2.getPlainAttr("firstname").orElseThrow().getValues().getFirst()); - Optional thirdAccount = accounts.stream(). + LinkedAccountTO account3 = accounts.stream(). filter(account -> user3Key.equals(account.getConnObjectKeyValue())). - filter(account -> "not.vivaldi".equals(account.getUsername())). - findFirst(); - assertTrue(thirdAccount.isPresent()); - assertFalse(thirdAccount.orElseThrow().isSuspended()); - assertEquals(RESOURCE_NAME_REST, thirdAccount.orElseThrow().getResource()); - assertEquals("not.vivaldi", thirdAccount.orElseThrow().getUsername()); + findFirst().orElseThrow(); + assertFalse(account3.isSuspended()); + assertEquals(RESOURCE_NAME_REST, account3.getResource()); + assertEquals("not.vivaldi", account3.getUsername()); + assertEquals( + "not.vivaldi@syncope.org", + account3.getPlainAttr("email").orElseThrow().getValues().getFirst()); // 3. update / remove REST users response = webClient.path(user1Key).delete(); @@ -579,25 +578,22 @@ public void pull() { accounts = USER_SERVICE.read("vivaldi").getLinkedAccounts(); assertEquals(2, accounts.size()); - firstAccount = accounts.stream(). + assertTrue(accounts.stream(). filter(account -> user1Key.equals(account.getConnObjectKeyValue())). - findFirst(); - assertFalse(firstAccount.isPresent()); + findFirst().isEmpty()); - secondAccount = accounts.stream(). + account2 = accounts.stream(). filter(account -> user2Key.equals(account.getConnObjectKeyValue())). - findFirst(); - assertTrue(secondAccount.isPresent()); - assertFalse(secondAccount.orElseThrow().isSuspended()); - assertEquals(user2Key, secondAccount.orElseThrow().getConnObjectKeyValue()); - assertEquals("linkedaccount2", secondAccount.orElseThrow().getUsername()); - - thirdAccount = accounts.stream(). - filter(account -> "not.vivaldi".equals(account.getUsername())). - findFirst(); - assertTrue(thirdAccount.isPresent()); - assertTrue(thirdAccount.orElseThrow().isSuspended()); - assertEquals(user3Key, thirdAccount.orElseThrow().getConnObjectKeyValue()); + findFirst().orElseThrow(); + assertFalse(account2.isSuspended()); + assertEquals(user2Key, account2.getConnObjectKeyValue()); + assertEquals("linkedaccount2", account2.getUsername()); + + account3 = accounts.stream(). + filter(account -> user3Key.equals(account.getConnObjectKeyValue())). + findFirst().orElseThrow(); + assertEquals("not.vivaldi", account3.getUsername()); + assertTrue(account3.isSuspended()); } finally { // clean up UserUR patch = new UserUR(); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LiveSyncITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LiveSyncITCase.java index 68eec54e017..db755b85fb7 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LiveSyncITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LiveSyncITCase.java @@ -24,9 +24,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import com.fasterxml.jackson.databind.JsonNode; import jakarta.ws.rs.core.Response; -import java.io.IOException; import java.time.Duration; import java.util.HashMap; import java.util.List; @@ -36,6 +34,7 @@ import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.mutable.Mutable; import org.apache.commons.lang3.mutable.MutableObject; +import org.apache.cxf.jaxrs.client.WebClient; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.producer.KafkaProducer; @@ -47,8 +46,10 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.request.UserCR; import org.apache.syncope.common.lib.request.UserUR; +import org.apache.syncope.common.lib.to.ConnInstanceTO; import org.apache.syncope.common.lib.to.ImplementationTO; import org.apache.syncope.common.lib.to.LiveSyncTaskTO; +import org.apache.syncope.common.lib.to.PagedResult; import org.apache.syncope.common.lib.to.ProvisioningResult; import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.ExecStatus; @@ -58,13 +59,15 @@ import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.common.rest.api.RESTHeaders; import org.apache.syncope.common.rest.api.beans.ExecSpecs; -import org.apache.syncope.common.rest.api.service.TaskService; +import org.apache.syncope.common.rest.api.beans.TaskQuery; import org.apache.syncope.core.provisioning.java.pushpull.KafkaInboundActions; import org.apache.syncope.fit.AbstractITCase; import org.apache.syncope.fit.core.reference.TestLiveSyncDeltaMapper; import org.identityconnectors.framework.common.objects.SyncDeltaType; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; public class LiveSyncITCase extends AbstractITCase { @@ -72,6 +75,22 @@ public class LiveSyncITCase extends AbstractITCase { private static final String GROUP_TOPIC = "group-provisioning"; + private static String KAFKA_BROKERS; + + @BeforeAll + public static void configureKafkaBrokers() { + KAFKA_BROKERS = WebClient.create(BUILD_TOOLS_ADDRESS + "/rest/kafka/brokers").get().readEntity(String.class); + assertNotNull(KAFKA_BROKERS); + + ConnInstanceTO kafkaConn = CONNECTOR_SERVICE.read("01938bdf-7ac6-7149-a103-3ec9e74cc824", null); + kafkaConn.getConf().stream().filter(p -> "bootstrapServers".equals(p.getSchema().getName()) + && !KAFKA_BROKERS.equals(p.getValues().getFirst())).findFirst(). + ifPresent(p -> { + p.getValues().set(0, KAFKA_BROKERS); + CONNECTOR_SERVICE.update(kafkaConn); + }); + } + @BeforeAll public static void testLiveSyncImplementationSetup() { ImplementationTO liveSDM = null; @@ -115,7 +134,7 @@ public static void testLiveSyncImplementationSetup() { private static KafkaProducer createProducer() { Map props = new HashMap<>(); - props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:19092"); + props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_BROKERS); props.put(ProducerConfig.CLIENT_ID_CONFIG, LiveSyncITCase.class.getSimpleName()); props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); @@ -125,7 +144,7 @@ private static KafkaProducer createProducer() { private static KafkaConsumer createConsumer() { Map props = new HashMap<>(); - props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:19092"); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_BROKERS); props.put(ConsumerConfig.CLIENT_ID_CONFIG, LiveSyncITCase.class.getSimpleName()); props.put(ConsumerConfig.GROUP_ID_CONFIG, LiveSyncITCase.class.getSimpleName()); props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); @@ -148,12 +167,12 @@ private static boolean found(final SyncDeltaType syncDeltaType, final String use try { JsonNode syncDelta = MAPPER.readTree(record.value()); if (syncDelta.has("deltaType")) { - sdt = SyncDeltaType.valueOf(syncDelta.get("deltaType").asText()); + sdt = SyncDeltaType.valueOf(syncDelta.get("deltaType").asString()); } if (syncDelta.has("uid") && syncDelta.get("uid").has("value")) { - uid = syncDelta.get("uid").get("value").iterator().next().asText(); + uid = syncDelta.get("uid").get("value").iterator().next().asString(); } - } catch (IOException e) { + } catch (JacksonException e) { fail(e.getMessage(), e); } @@ -191,27 +210,27 @@ public void crud() { @Test public void liveSync() { // 1. create and execute the live sync task - LiveSyncTaskTO task = new LiveSyncTaskTO(); - task.setName("Test LiveSync"); - task.setDestinationRealm(SyncopeConstants.ROOT_REALM); - task.setResource(RESOURCE_NAME_KAFKA); - task.setLiveSyncDeltaMapper(TestLiveSyncDeltaMapper.class.getSimpleName()); - task.getActions().add(KafkaInboundActions.class.getSimpleName()); - task.setPerformCreate(true); - task.setPerformUpdate(true); - task.setPerformDelete(true); - - Response response = TASK_SERVICE.create(TaskType.LIVE_SYNC, task); - LiveSyncTaskTO actual = getObject(response.getLocation(), TaskService.class, LiveSyncTaskTO.class); - assertNotNull(actual); - - task = TASK_SERVICE.read(TaskType.LIVE_SYNC, actual.getKey(), true); - assertNotNull(task); - assertEquals(actual.getKey(), task.getKey()); - assertNotNull(actual.getJobDelegate()); - assertEquals(actual.getLiveSyncDeltaMapper(), task.getLiveSyncDeltaMapper()); - - TASK_SERVICE.execute(new ExecSpecs.Builder().key(task.getKey()).build()); + String taskKey; + PagedResult found = + TASK_SERVICE.search(new TaskQuery.Builder(TaskType.LIVE_SYNC).resource(RESOURCE_NAME_KAFKA).build()); + if (found.getResult().isEmpty()) { + LiveSyncTaskTO task = new LiveSyncTaskTO(); + task.setName("Test LiveSync"); + task.setDestinationRealm(SyncopeConstants.ROOT_REALM); + task.setResource(RESOURCE_NAME_KAFKA); + task.setLiveSyncDeltaMapper(TestLiveSyncDeltaMapper.class.getSimpleName()); + task.getActions().add(KafkaInboundActions.class.getSimpleName()); + task.setPerformCreate(true); + task.setPerformUpdate(true); + task.setPerformDelete(true); + + Response response = TASK_SERVICE.create(TaskType.LIVE_SYNC, task); + taskKey = response.getHeaderString(RESTHeaders.RESOURCE_KEY); + } else { + taskKey = found.getResult().getFirst().getKey(); + } + + TASK_SERVICE.execute(new ExecSpecs.Builder().key(taskKey).build()); try { // 2. send event to the queue @@ -248,17 +267,17 @@ public void liveSync() { assertEquals("LiveSync LiveSync", user.getPlainAttr("fullname").orElseThrow().getValues().getFirst()); await().atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).until( - () -> TASK_SERVICE.read(TaskType.LIVE_SYNC, actual.getKey(), true).getExecutions(), + () -> TASK_SERVICE.read(TaskType.LIVE_SYNC, taskKey, true).getExecutions(), execs -> execs.size() == 1 && execs.stream().allMatch(exec -> ExecStatus.SUCCESS.name().equals(exec.getStatus()))); // 4. stop live syncing - assertTrue(TASK_SERVICE.getJob(task.getKey()).isRunning()); + assertTrue(TASK_SERVICE.getJob(taskKey).isRunning()); - TASK_SERVICE.actionJob(task.getKey(), JobAction.STOP); + TASK_SERVICE.actionJob(taskKey, JobAction.STOP); await().atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS). - until(() -> !TASK_SERVICE.getJob(actual.getKey()).isRunning()); + until(() -> !TASK_SERVICE.getJob(taskKey).isRunning()); // 5. send new event to the queue, but no further executions String groupName = "liveSync" + getUUIDString(); @@ -276,7 +295,7 @@ public void liveSync() { } // 6. start again live syncing and find the new group in Syncope - TASK_SERVICE.actionJob(task.getKey(), JobAction.START); + TASK_SERVICE.actionJob(taskKey, JobAction.START); await().atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).until( () -> { @@ -288,12 +307,12 @@ public void liveSync() { }, Objects::nonNull); await().atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).until( - () -> TASK_SERVICE.read(TaskType.LIVE_SYNC, actual.getKey(), true).getExecutions(), + () -> TASK_SERVICE.read(TaskType.LIVE_SYNC, taskKey, true).getExecutions(), execs -> execs.size() == 2 && execs.stream().allMatch(exec -> ExecStatus.SUCCESS.name().equals(exec.getStatus()))); } finally { // finally stop live syncing - TASK_SERVICE.actionJob(task.getKey(), JobAction.STOP); + TASK_SERVICE.actionJob(taskKey, JobAction.STOP); } } } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MacroTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MacroTaskITCase.java index cae408ab068..6cc91ad7e45 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MacroTaskITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MacroTaskITCase.java @@ -76,8 +76,11 @@ public class MacroTaskITCase extends AbstractITCase { TCA.setPrinterName("aprinter112"); } - private static void createMacroActionsIfNeeded(final String key, final ImplementationEngine engine, + private static void createMacroActionsIfNeeded( + final String key, + final ImplementationEngine engine, final String body) { + ImplementationTO macroActions = null; try { macroActions = IMPLEMENTATION_SERVICE.read(IdRepoImplementationType.MACRO_ACTIONS, key); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java index dac96a857b8..a7a6e8c8d01 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java @@ -25,7 +25,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import com.fasterxml.jackson.core.JsonProcessingException; import jakarta.ws.rs.core.Response; import org.apache.syncope.client.lib.SyncopeClient; import org.apache.syncope.common.lib.Attr; @@ -64,7 +63,7 @@ public class MembershipITCase extends AbstractITCase { @Test - public void misc() throws JsonProcessingException { + public void misc() { UserCR userCR = UserITCase.getUniqueSample("memb@apache.org"); userCR.setRealm("/even/two"); userCR.getPlainAttrs().add(new Attr.Builder("aLong").value("1976").build()); @@ -276,13 +275,7 @@ public void pull() { assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); // 5. verify that pulled user has - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult users = USER_SERVICE.search(new AnyQuery.Builder(). realm("/"). fiql(SyncopeClient.getUserSearchConditionBuilder(). diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MultitenancyITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MultitenancyITCase.java index 9f2d5b0fa33..0e062f5e1c9 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MultitenancyITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MultitenancyITCase.java @@ -202,13 +202,8 @@ public void createResourceAndPull() { assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(status)); // verify that pulled user is found - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); + PagedResult matchingUsers = ADMIN_CLIENT.getService(UserService.class). search(new AnyQuery.Builder(). realm(SyncopeConstants.ROOT_REALM). diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OpenAPIITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OpenAPIITCase.java index 8873c6d28f2..0f30bbe2485 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OpenAPIITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OpenAPIITCase.java @@ -22,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.fasterxml.jackson.databind.JsonNode; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.io.IOException; @@ -30,6 +29,7 @@ import org.apache.cxf.jaxrs.client.WebClient; import org.apache.syncope.fit.AbstractITCase; import org.junit.jupiter.api.Test; +import tools.jackson.databind.JsonNode; public class OpenAPIITCase extends AbstractITCase { @@ -43,14 +43,14 @@ public void openapi() throws IOException { assertNotNull(tree); JsonNode info = tree.get("info"); - assertEquals("Apache Syncope", info.get("title").asText()); + assertEquals("Apache Syncope", info.get("title").asString()); JsonNode paths = tree.get("paths"); assertNotNull(paths); - assertTrue(paths.isContainerNode()); + assertTrue(paths.isContainer()); JsonNode components = tree.get("components"); assertNotNull(components); - assertTrue(components.isContainerNode()); + assertTrue(components.isContainer()); } } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java index 865ef55ff21..8018ad9ede4 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java @@ -287,7 +287,7 @@ public void fromCSV() throws Exception { assertNotNull(usersPre); ExecTO exec = execSchedTask(TASK_SERVICE, TaskType.PULL, PULL_TASK_KEY, MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(exec.getStatus())); + assertSuccessful(exec); LOG.debug("Execution of task {}:\n{}", PULL_TASK_KEY, exec); @@ -381,7 +381,7 @@ public void reconcileFromDB() { try { ExecTO execution = execSchedTask( TASK_SERVICE, TaskType.PULL, "83f7e85d-9774-43fe-adba-ccd856312994", MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertSuccessful(execution); userTO = USER_SERVICE.read("testuser1"); assertNotNull(userTO); @@ -396,7 +396,7 @@ public void reconcileFromDB() { // re-execute the same PullTask: now user must be active execution = execSchedTask( TASK_SERVICE, TaskType.PULL, "83f7e85d-9774-43fe-adba-ccd856312994", MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertSuccessful(execution); userTO = USER_SERVICE.read("testuser1"); assertNotNull(userTO); @@ -420,19 +420,13 @@ public void reconcileFromLDAP() { ExecTO execution = execSchedTask(TASK_SERVICE, TaskType.PULL, LDAP_PULL_TASK, MAX_WAIT_SECONDS, false); // 1. verify execution status - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertSuccessful(execution); // SYNCOPE-898 PullTaskTO task = TASK_SERVICE.read(TaskType.PULL, LDAP_PULL_TASK, false); assertEquals(SyncopeConstants.ROOT_REALM, task.getDestinationRealm()); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); // 2. verify that pulled group is found PagedResult matchingGroups = GROUP_SERVICE.search(new AnyQuery.Builder().realm( @@ -465,7 +459,7 @@ public void reconcileFromLDAP() { PagedResult matchByLastChangeContext = USER_SERVICE.search( new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). fiql(SyncopeClient.getUserSearchConditionBuilder().is("lastChangeContext"). - equalTo("*PullTask " + task.getKey() + "*").query()). + equalTo("PULL Task " + task.getKey() + "*").query()). build()); assertNotNull(matchByLastChangeContext); assertNotEquals(0, matchByLastChangeContext.getTotalCount()); @@ -594,13 +588,7 @@ public void reconcileFromScriptedSQL() throws IOException { assertTrue(connObjectTO.getAttr("LOCATION").orElseThrow().getValues().getFirst().startsWith(prefix)); // 3. unlink any existing printer and delete from Syncope (printer is now only on external resource) - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult matchingPrinters = ANY_OBJECT_SERVICE.search( new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). @@ -621,23 +609,11 @@ public void reconcileFromScriptedSQL() throws IOException { // 4. pull execSchedTask(TASK_SERVICE, TaskType.PULL, pullTask.getKey(), MAX_WAIT_SECONDS, false); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); // 5. verify that printer was re-created in Syncope (implies that location does not start with given prefix, // hence PrefixItemTransformer was applied during pull) - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); matchingPrinters = ANY_OBJECT_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). fiql(SyncopeClient.getAnyObjectSearchConditionBuilder(PRINTER). @@ -689,9 +665,8 @@ public void filteredReconciliation() throws IOException { assertEquals(reconFilterBuilder.getKey(), task.getReconFilterBuilder()); // 3. exec task - ExecTO execution = execSchedTask( - TASK_SERVICE, TaskType.PULL, task.getKey(), MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + ExecTO execution = execSchedTask(TASK_SERVICE, TaskType.PULL, task.getKey(), MAX_WAIT_SECONDS, false); + assertSuccessful(execution); // 4. verify that only enabled user was pulled userTO = USER_SERVICE.read("user2"); @@ -773,7 +748,7 @@ public void syncTokenWithErrors() { + "false, 'syncTokenWithErrors1@syncope.apache.org', 'true', '2015-05-23 13:53:24.293')"); ExecTO exec = execSchedTask(TASK_SERVICE, TaskType.PULL, pullTask.getKey(), MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(exec.getStatus())); + assertSuccessful(exec); resForTest = RESOURCE_SERVICE.read(resForTest.getKey()); assertTrue(resForTest.getProvision(AnyTypeKind.USER.name()).orElseThrow(). @@ -784,7 +759,7 @@ public void syncTokenWithErrors() { + "WHERE ID=1041"); exec = execSchedTask(TASK_SERVICE, TaskType.PULL, pullTask.getKey(), MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(exec.getStatus())); + assertSuccessful(exec); resForTest = RESOURCE_SERVICE.read(resForTest.getKey()); assertTrue(resForTest.getProvision(AnyTypeKind.USER.name()).orElseThrow(). @@ -838,7 +813,7 @@ public void remediation() { // 3. execute the pull task and verify that: ExecTO execution = execSchedTask( TASK_SERVICE, TaskType.PULL, pullTask.getKey(), MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertSuccessful(execution); // 3a. user was not pulled try { @@ -1000,7 +975,7 @@ public void concurrentPull() throws InterruptedException { TASK_SERVICE, TaskType.PULL, pullTaskKey, 2 * MAX_WAIT_SECONDS, false); // 3. verify execution status - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertSuccessful(execution); // 4. verify that the given number of users was effectively pulled result = USER_SERVICE.search(new AnyQuery.Builder().fiql( @@ -1072,7 +1047,7 @@ public void issueSYNCOPE68() { ExecTO execution = execSchedTask( TASK_SERVICE, TaskType.PULL, actual.getKey(), MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertSuccessful(execution); userTO = USER_SERVICE.read("testuser2"); assertNotNull(userTO); @@ -1214,7 +1189,7 @@ public void issueSYNCOPE272() { TASK_SERVICE, TaskType.PULL, "986867e2-993b-430e-8feb-aa9abb4c1dcd", MAX_WAIT_SECONDS, false); assertNotNull(taskExecTO.getStatus()); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(taskExecTO.getStatus())); + assertSuccessful(taskExecTO); userTO = USER_SERVICE.read(userTO.getKey()); assertNotNull(userTO); @@ -1267,7 +1242,7 @@ public void issueSYNCOPE313DB() throws Exception { ExecTO execution = execSchedTask( TASK_SERVICE, TaskType.PULL, pullTask.getKey(), MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertSuccessful(execution); // 5. Test the pulled user SyncopeClient.Self self = CLIENT_FACTORY.create(user.getUsername(), newCleanPassword).self(); @@ -1293,7 +1268,7 @@ public void issueSYNCOPE313LDAP() throws Exception { UserCR userCR = UserITCase.getUniqueSample("syncope313-ldap@syncope.apache.org"); userCR.setPassword(oldCleanPassword); userCR.getResources().add(RESOURCE_NAME_LDAP); - user = createUser(userCR).getEntity(); + user = getEntity(createUser(userCR)); assertNotNull(user); assertFalse(user.getResources().isEmpty()); @@ -1302,15 +1277,15 @@ public void issueSYNCOPE313LDAP() throws Exception { UserUR userUR = new UserUR(); userUR.setKey(user.getKey()); userUR.setPassword(new PasswordPatch.Builder().value(newCleanPassword).build()); - user = updateUser(userUR).getEntity(); + user = getEntity(updateUser(userUR)); // 3. Check that the Syncope user now has the changed password SyncopeClient.Self self = CLIENT_FACTORY.create(user.getUsername(), newCleanPassword).self(); assertNotNull(self); // 4. Check that the LDAP resource has the old password - ConnObject connObject = - RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), user.getKey()); + ConnObject connObject = RESOURCE_SERVICE.readConnObject( + RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), user.getKey()); assertNotNull(getLdapRemoteObject( connObject.getAttr(Name.NAME).orElseThrow().getValues().getFirst(), oldCleanPassword, @@ -1352,7 +1327,7 @@ public void issueSYNCOPE313LDAP() throws Exception { ExecTO execution = execSchedTask( TASK_SERVICE, TaskType.PULL, pullTask.getKey(), MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertSuccessful(execution); // 7. Test the pulled user self = CLIENT_FACTORY.create(user.getUsername(), oldCleanPassword).self(); @@ -1415,7 +1390,7 @@ public void issueSYNCOPE1062() { // 3. exec the pull task ExecTO execution = execSchedTask( TASK_SERVICE, TaskType.PULL, pullTask.getKey(), MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertSuccessful(execution); // the user is successfully pulled... user = USER_SERVICE.read("pullFromLDAP"); @@ -1426,8 +1401,8 @@ public void issueSYNCOPE1062() { group = GROUP_SERVICE.read("testLDAPGroup"); assertNotNull(group); - ConnObject connObject = - RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), user.getKey()); + ConnObject connObject = RESOURCE_SERVICE.readConnObject( + RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), user.getKey()); assertNotNull(connObject); assertEquals( "pullFromLDAP@syncope.apache.org", @@ -1455,7 +1430,7 @@ public void issueSYNCOPE1062() { // 5. exec the pull task again execution = execSchedTask(TASK_SERVICE, TaskType.PULL, pullTask.getKey(), MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertSuccessful(execution); // the internal is updated... user = USER_SERVICE.read("pullFromLDAP"); @@ -1516,7 +1491,7 @@ public void issueSYNCOPE1656() { assertNotNull(pullTask); ExecTO execution = execSchedTask(TASK_SERVICE, TaskType.PULL, pullTask.getKey(), MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertSuccessful(execution); pullFromLDAP4issue1656 = USER_SERVICE.read("pullFromLDAP_issue1656"); assertEquals("pullFromLDAP_issue1656@syncope.apache.org", @@ -1533,7 +1508,7 @@ public void issueSYNCOPE1656() { updateLdapRemoteObject(userDn.getValues().getFirst(), Map.of("mail", "pullFromLDAP_issue1656@")); // 3. Pull again from resource-ldap execution = execSchedTask(TASK_SERVICE, TaskType.PULL, pullTask.getKey(), MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertSuccessful(execution); assertTrue(execution.getMessage().contains("UPDATE FAILURE")); pullFromLDAP4issue1656 = USER_SERVICE.read("pullFromLDAP_issue1656"); assertEquals("pullFromLDAP_issue1656@syncope.apache.org", @@ -1608,7 +1583,7 @@ public void issueSYNCOPE1864() throws Exception { assertNotNull(pullTask); ExecTO execution = execSchedTask(TASK_SERVICE, TaskType.PULL, pullTask.getKey(), MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertSuccessful(execution); // 3. Test if password is not present in the propagation task for DB PagedResult propagationTasks = TASK_SERVICE.search( @@ -1643,7 +1618,6 @@ public void issueSYNCOPE1864() throws Exception { @Test public void issueSYNCOPE1905() { - ldapCleanup(); PullTaskTO pullTaskTO = new PullTaskTO(); @@ -1656,13 +1630,12 @@ public void issueSYNCOPE1905() { ProvisioningReport result = null; try { - List results = - RECONCILIATION_SERVICE.pull(new ReconQuery.Builder(AnyTypeKind.GROUP.name(), - RESOURCE_NAME_LDAP).fiql( - SyncopeClient.getConnObjectTOFiqlSearchConditionBuilder() - .is(Uid.NAME) - .equalToIgnoreCase("testLDAPGroup") - .query()).build(), pullTaskTO); + List results = RECONCILIATION_SERVICE.pull( + new ReconQuery.Builder(AnyTypeKind.GROUP.name(), RESOURCE_NAME_LDAP).fiql( + SyncopeClient.getConnObjectTOFiqlSearchConditionBuilder(). + is(Uid.NAME).equalToIgnoreCase("testLDAPGroup").query()). + build(), + pullTaskTO); assertNotNull(results.getFirst()); result = results.getFirst(); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PushTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PushTaskITCase.java index ac00ef43532..066080def38 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PushTaskITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PushTaskITCase.java @@ -48,7 +48,6 @@ import org.apache.syncope.common.lib.to.ResourceTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.AttrSchemaType; -import org.apache.syncope.common.lib.types.ExecStatus; import org.apache.syncope.common.lib.types.IdMImplementationType; import org.apache.syncope.common.lib.types.MappingPurpose; import org.apache.syncope.common.lib.types.MatchingRule; @@ -346,7 +345,7 @@ public void orgUnit() { assertNotNull(pushTask); ExecTO exec = execSchedTask(TASK_SERVICE, TaskType.PUSH, pushTask.getKey(), MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(exec.getStatus())); + assertSuccessful(exec); // 2. check assertNotNull(getLdapRemoteObject("ou=odd,o=isp")); @@ -375,7 +374,7 @@ public void concurrentPush() { ExecTO execution = execSchedTask(TASK_SERVICE, TaskType.PUSH, pushTaskKey, MAX_WAIT_SECONDS, false); // 3. verify execution status - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertSuccessful(execution); } @Test @@ -458,7 +457,7 @@ public void issueSYNCOPE598() { // execute the new task ExecTO exec = execSchedTask(TASK_SERVICE, TaskType.PUSH, push.getKey(), MAX_WAIT_SECONDS, false); - assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(exec.getStatus())); + assertSuccessful(exec); } finally { GROUP_SERVICE.delete(groupTO.getKey()); if (newResourceTO != null) { @@ -516,12 +515,11 @@ public void issueSYNCOPE648() { void issueSYNCOPE1918() throws Exception { // update group citizen GroupUR groupUR = new GroupUR.Builder("29f96485-729e-4d31-88a1-6fc60e4677f3"). - udynMembershipCond("username=~ros*"). - adynMembershipCond(PRINTER, "name=~hp*"). + dynMembershipCond("USER", "username=~ros*"). + dynMembershipCond(PRINTER, "name=~hp*"). build(); GroupTO citizen = updateGroup(groupUR).getEntity(); - assertNotNull(citizen.getUDynMembershipCond()); - assertFalse(citizen.getADynMembershipConds().isEmpty()); + assertFalse(citizen.getDynMembershipConds().isEmpty()); try { execProvisioningTasks( @@ -531,16 +529,13 @@ void issueSYNCOPE1918() throws Exception { MAX_WAIT_SECONDS, false); citizen = GROUP_SERVICE.read("29f96485-729e-4d31-88a1-6fc60e4677f3"); - assertNotNull(citizen.getUDynMembershipCond()); - assertFalse(citizen.getADynMembershipConds().isEmpty()); + assertEquals(2, citizen.getDynMembershipConds().size()); } finally { // restore group citizen - groupUR.setUDynMembershipCond(null); - groupUR.getADynMembershipConds().clear(); + groupUR.getDynMembershipConds().clear(); citizen = updateGroup(groupUR).getEntity(); - assertNull(citizen.getUDynMembershipCond()); - assertTrue(citizen.getADynMembershipConds().isEmpty()); + assertTrue(citizen.getDynMembershipConds().isEmpty()); } } } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReconciliationITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReconciliationITCase.java index a1891ca6915..2e69919fd9a 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReconciliationITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReconciliationITCase.java @@ -25,9 +25,6 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.fasterxml.jackson.databind.MappingIterator; -import com.fasterxml.jackson.dataformat.csv.CsvMapper; -import com.fasterxml.jackson.dataformat.csv.CsvSchema; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.Response; import java.io.IOException; @@ -64,6 +61,9 @@ import org.identityconnectors.framework.common.objects.Uid; import org.junit.jupiter.api.Test; import org.springframework.jdbc.core.JdbcTemplate; +import tools.jackson.databind.MappingIterator; +import tools.jackson.dataformat.csv.CsvMapper; +import tools.jackson.dataformat.csv.CsvSchema; public class ReconciliationITCase extends AbstractITCase { @@ -131,7 +131,7 @@ public void pull() { printerCR.getResources().clear(); AnyObjectTO printer = createAnyObject(printerCR).getEntity(); assertNotNull(printer.getKey()); - assertNotEquals("Nowhere", printer.getPlainAttr("location").get().getValues().getFirst()); + assertNotEquals("Nowhere", printer.getPlainAttr("location").orElseThrow().getValues().getFirst()); // 2. add row into the external resource's table, with same name JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource); @@ -156,7 +156,7 @@ public void pull() { // 5. verify reconciliation result (and resource is still not assigned) printer = ANY_OBJECT_SERVICE.read(printer.getKey()); - assertEquals("Nowhere", printer.getPlainAttr("location").get().getValues().getFirst()); + assertEquals("Nowhere", printer.getPlainAttr("location").orElseThrow().getValues().getFirst()); assertTrue(printer.getResources().isEmpty()); } @@ -180,8 +180,8 @@ public void importSingle() { assertNull(status.getMatchType()); assertNull(status.getOnSyncope()); assertNotNull(status.getOnResource()); - assertEquals(externalKey, status.getOnResource().getAttr(Uid.NAME).get().getValues().getFirst()); - assertEquals(externalName, status.getOnResource().getAttr("PRINTERNAME").get().getValues().getFirst()); + assertEquals(externalKey, status.getOnResource().getAttr(Uid.NAME).orElseThrow().getValues().getFirst()); + assertEquals(externalName, status.getOnResource().getAttr("PRINTERNAME").orElseThrow().getValues().getFirst()); // 3. pull PullTaskTO pullTask = new PullTaskTO(); @@ -214,13 +214,13 @@ public void importCSV() { UserTO donizetti = USER_SERVICE.read(results.getFirst().getKey()); assertNotNull(donizetti); - assertEquals("Gaetano", donizetti.getPlainAttr("firstname").get().getValues().getFirst()); - assertEquals(1, donizetti.getPlainAttr("loginDate").get().getValues().size()); + assertEquals("Gaetano", donizetti.getPlainAttr("firstname").orElseThrow().getValues().getFirst()); + assertEquals(1, donizetti.getPlainAttr("loginDate").orElseThrow().getValues().size()); UserTO cimarosa = USER_SERVICE.read(results.get(1).getKey()); assertNotNull(cimarosa); - assertEquals("Domenico Cimarosa", cimarosa.getPlainAttr("fullname").get().getValues().getFirst()); - assertEquals(2, cimarosa.getPlainAttr("loginDate").get().getValues().size()); + assertEquals("Domenico Cimarosa", cimarosa.getPlainAttr("fullname").orElseThrow().getValues().getFirst()); + assertEquals(2, cimarosa.getPlainAttr("loginDate").orElseThrow().getValues().size()); } @Test @@ -265,23 +265,23 @@ public void exportCSV() throws IOException { assertEquals(users.getResult().get(rows).getStatus(), row.get("status")); switch (row.get("username")) { - case "rossini": + case "rossini" -> { assertEquals(spec.getNullValue(), row.get("email")); assertTrue(row.get("loginDate").contains(spec.getArrayElementSeparator())); - break; + } - case "verdi": + case "verdi" -> { assertEquals("verdi@syncope.org", row.get("email")); assertEquals(spec.getNullValue(), row.get("loginDate")); - break; + } - case "bellini": + case "bellini" -> { assertEquals(spec.getNullValue(), row.get("email")); assertFalse(row.get("loginDate").contains(spec.getArrayElementSeparator())); - break; + } - default: - break; + default -> { + } } } assertEquals(rows, users.getTotalCount()); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java index 27d989bf354..5820704c9a4 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java @@ -27,12 +27,6 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import jakarta.ws.rs.HttpMethod; import jakarta.ws.rs.core.GenericType; import jakarta.ws.rs.core.HttpHeaders; @@ -88,6 +82,11 @@ import org.apache.syncope.fit.AbstractITCase; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.ObjectNode; +import tools.jackson.jakarta.rs.json.JacksonJsonProvider; public class SCIMITCase extends AbstractITCase { @@ -186,8 +185,8 @@ public static void isSCIMAvailable() { private static WebClient webClient() { return WebClient.create( SCIM_ADDRESS, - List.of(new JacksonJsonProvider(JsonMapper.builder(). - findAndAddModules().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).build()))). + List.of(new JacksonJsonProvider(JsonMapper.builder().findAndAddModules(). + enable(MapperFeature.USE_GETTERS_AS_SETTERS).build()))). accept(SCIMConstants.APPLICATION_SCIM_JSON_TYPE). type(SCIMConstants.APPLICATION_SCIM_JSON_TYPE). header(HttpHeaders.AUTHORIZATION, "Bearer " + ADMIN_CLIENT.jwtInfo().orElseThrow().value()); @@ -272,21 +271,21 @@ public void schemas() { ObjectNode enterpriseUser = response.readEntity(ObjectNode.class); assertNotNull(enterpriseUser); - assertEquals(Resource.EnterpriseUser.schema(), enterpriseUser.get("id").textValue()); + assertEquals(Resource.EnterpriseUser.schema(), enterpriseUser.get("id").stringValue()); response = webClient().path("Schemas").path(Resource.ExtensionUser.schema()).get(); assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); ObjectNode extensionUser = response.readEntity(ObjectNode.class); assertNotNull(extensionUser); - assertEquals(Resource.ExtensionUser.schema(), extensionUser.get("id").textValue()); + assertEquals(Resource.ExtensionUser.schema(), extensionUser.get("id").stringValue()); response = webClient().path("Schemas").path(Resource.ExtensionGroup.schema()).get(); assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); ObjectNode extensionGroup = response.readEntity(ObjectNode.class); assertNotNull(extensionGroup); - assertEquals(Resource.ExtensionGroup.schema(), extensionGroup.get("id").textValue()); + assertEquals(Resource.ExtensionGroup.schema(), extensionGroup.get("id").stringValue()); response = webClient().path("Schemas").path("urn:ietf:params:scim:schemas:extension:syncope:2.0:PRINTER").get(); assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); @@ -294,7 +293,7 @@ public void schemas() { ObjectNode extensionPrinter = response.readEntity(ObjectNode.class); assertNotNull(extensionPrinter); assertEquals("urn:ietf:params:scim:schemas:extension:syncope:2.0:PRINTER", - extensionPrinter.get("id").textValue()); + extensionPrinter.get("id").stringValue()); CONF.setExtensionUserConf(null); CONF.setExtensionGroupConf(null); @@ -582,7 +581,7 @@ public void search() { } @Test - public void createUser() throws JsonProcessingException { + public void createUser() { SCIM_CONF_SERVICE.set(CONF); SCIMUser user = getSampleUser(UUID.randomUUID().toString(), List.of(Resource.User.schema())); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java index ac40b32bbd3..87f0ce46176 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java @@ -207,17 +207,11 @@ public void searchByGroup() { @Test public void searchByDynGroup() { GroupCR groupCR = GroupITCase.getBasicSample("dynMembership"); - groupCR.setUDynMembershipCond("cool==true"); + groupCR.getDynMembershipConds().put("USER", "cool==true"); GroupTO group = createGroup(groupCR).getEntity(); assertNotNull(group); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult matchingGroups = GROUP_SERVICE.search( new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). @@ -257,13 +251,7 @@ public void searchByDynRole() { role = getObject(response.getLocation(), RoleService.class, RoleTO.class); assertNotNull(role); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult matchingUsers = USER_SERVICE.search( new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). @@ -352,13 +340,7 @@ public void searchByBooleanAnyCond() { @Test public void searchByDate() { - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult users = USER_SERVICE.search(new AnyQuery.Builder(). realm(SyncopeConstants.ROOT_REALM). @@ -496,13 +478,7 @@ public void member() { membership(new MembershipTO.Builder("29f96485-729e-4d31-88a1-6fc60e4677f3").build()). build()).getEntity().getKey(); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); groups = GROUP_SERVICE.search(new AnyQuery.Builder().realm("/"). fiql(SyncopeClient.getGroupSearchConditionBuilder().withMembers(printer).query()). @@ -654,13 +630,7 @@ public void userByMembershipAttribute() { updateGroup(new GroupUR.Builder(employee.getKey()).typeExtension(typeExtensionTO).build()); } - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult matching = USER_SERVICE.search( new AnyQuery.Builder().fiql(SyncopeClient.getUserSearchConditionBuilder(). @@ -680,13 +650,7 @@ public void userByMembershipAttribute() { .plainAttrs(attr("ctype", "additionalemployeectype")).build()) .build()); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); matching = USER_SERVICE.search( new AnyQuery.Builder().fiql(SyncopeClient.getUserSearchConditionBuilder(). @@ -715,13 +679,7 @@ public void anyObjectByMembershipAttribute() { plainAttrs(attr("ctype", "otherchildctype")). build()).build()); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); matching = ANY_OBJECT_SERVICE.search( new AnyQuery.Builder().fiql(SyncopeClient.getAnyObjectSearchConditionBuilder(PRINTER) @@ -783,13 +741,7 @@ public void issueSYNCOPE980() { build(); updateAnyObject(anyObjectUR); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult matching = ANY_OBJECT_SERVICE.search(new AnyQuery.Builder().fiql( SyncopeClient.getAnyObjectSearchConditionBuilder(service.getKey()). @@ -822,13 +774,7 @@ public void issueSYNCOPE1223() { req.getPlainAttrs().add(new AttrPatch.Builder(attr("ctype", "ou=sample,o=isp")).build()); USER_SERVICE.update(req); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); try { PagedResult users = USER_SERVICE.search(new AnyQuery.Builder().fiql( @@ -908,13 +854,7 @@ public void issueSYNCOPE1419() { assertNotNull(rossini); assertEquals("2009-05-26", rossini.getPlainAttr("loginDate").orElseThrow().getValues().getFirst()); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult total = USER_SERVICE.search( new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).page(1).size(1).build()); @@ -975,13 +915,7 @@ public void issueSYNCOPE1727() { UserTO user = createUser(userCR).getEntity(); // 3. search for user - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult users = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). fiql(SyncopeClient.getUserSearchConditionBuilder().is("realm"). @@ -1013,13 +947,7 @@ public void issueSYNCOPE1779() { new AnyObjectCR.Builder(SyncopeConstants.ROOT_REALM, PRINTER, "_syncope1779").build()).getEntity(); // 4. search - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); try { // Search by username @@ -1073,13 +1001,7 @@ public void issueSYNCOPE1790() { assertNotNull(user); // 2. search again - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); users = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). fiql(SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("verdi@syncope.org").query()). @@ -1103,13 +1025,7 @@ public void issueSYNCOPE1800() { assertEquals("D'Amico", user.getPlainAttr("surname").orElseThrow().getValues().getFirst()); // 2. search for user - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); PagedResult users = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). fiql("surname=~D'*").build()); @@ -1135,13 +1051,7 @@ void issueSYNCOPE1826() { userCR.setUsername("user test 182"); createUser(userCR); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); try { assertFalse(USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).details(false) diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java index ca45f70c825..8451405c253 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java @@ -19,6 +19,7 @@ package org.apache.syncope.fit.core; import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -29,7 +30,6 @@ import static org.junit.jupiter.api.Assumptions.assumeFalse; import jakarta.ws.rs.HttpMethod; -import jakarta.ws.rs.core.GenericType; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.io.IOException; @@ -154,7 +154,6 @@ public void issue186() { userCR.getPlainAttrs().add(attr("surname", userId)); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); assertTrue(userTO.getResources().isEmpty()); // 2. update assigning a resource forcing mandatory constraints: must fail with RequiredValuesMissing @@ -181,12 +180,11 @@ public void issue186() { ProvisioningResult result = updateUser(userUR); assertNotNull(result.getPropagationStatuses().getFirst().getFailureReason()); - userTO = result.getEntity(); // 4. update assigning a resource NOT forcing mandatory constraints // BUT not priority: must succeed userUR = new UserUR(); - userUR.setKey(userTO.getKey()); + userUR.setKey(result.getEntity().getKey()); userUR.setPassword(new PasswordPatch.Builder().value("newPassword123456").build()); userUR.getResources().add(new StringPatchItem.Builder(). operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_CSV).build()); @@ -200,7 +198,6 @@ public void issue213() { userCR.getResources().add(RESOURCE_NAME_TESTDB); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); assertEquals(1, userTO.getResources().size()); JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource); @@ -231,15 +228,12 @@ public void issue234() { inUserTO.getResources().add(RESOURCE_NAME_LDAP); UserTO userTO = createUser(inUserTO).getEntity(); - assertNotNull(userTO); UserUR userUR = new UserUR(); - userUR.setKey(userTO.getKey()); userUR.setUsername(new StringReplacePatchItem.Builder().value('1' + userTO.getUsername()).build()); userTO = updateUser(userUR).getEntity(); - assertNotNull(userTO); assertEquals('1' + inUserTO.getUsername(), userTO.getUsername()); } @@ -250,7 +244,6 @@ public void issue280() { userCR.getMemberships().clear(); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); UserUR userUR = new UserUR(); userUR.setKey(userTO.getKey()); @@ -317,7 +310,6 @@ public void issueSYNCOPE108() { userCR.getResources().add(RESOURCE_NAME_CSV); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); assertEquals(2, userTO.getMemberships().size()); assertEquals(1, userTO.getResources().size()); @@ -334,7 +326,6 @@ public void issueSYNCOPE108() { build(); userTO = updateUser(userUR).getEntity(); - assertNotNull(userTO); assertEquals(1, userTO.getMemberships().size()); connObjectTO = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), userTO.getKey()); @@ -351,7 +342,6 @@ public void issueSYNCOPE108() { value(userTO.getResources().iterator().next()).build()); userTO = updateUser(userUR).getEntity(); - assertNotNull(userTO); assertEquals(1, userTO.getMemberships().size()); assertFalse(userTO.getResources().isEmpty()); @@ -368,7 +358,6 @@ public void issueSYNCOPE108() { build(); userTO = updateUser(userUR).getEntity(); - assertNotNull(userTO); assertTrue(userTO.getMemberships().isEmpty()); assertTrue(userTO.getResources().isEmpty()); @@ -431,7 +420,6 @@ public void issueSYNCOPE266() { userCR.getResources().clear(); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); UserUR userUR = new UserUR(); userUR.setKey(userTO.getKey()); @@ -440,12 +428,14 @@ public void issueSYNCOPE266() { userUR.getResources().add(new StringPatchItem.Builder(). operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_UPDATE).build()); - userTO = updateUser(userUR).getEntity(); - assertNotNull(userTO); + assertDoesNotThrow(() -> updateUser(userUR).getEntity()); } @Test public void issueSYNCOPE279() { + // see https://github.com/payara/Payara/issues/7203 + assumeFalse(IS_DEPLOYED_IN_PAYARA); + UserCR userCR = UserITCase.getUniqueSample("syncope279@apache.org"); userCR.getResources().clear(); userCR.getResources().add(RESOURCE_NAME_TIMEOUT); @@ -465,7 +455,6 @@ public void issueSYNCOPE122() { userCR.getResources().add(RESOURCE_NAME_TESTDB2); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); assertTrue(userTO.getResources().contains(RESOURCE_NAME_TESTDB)); assertTrue(userTO.getResources().contains(RESOURCE_NAME_TESTDB2)); @@ -537,7 +526,6 @@ public void issueSYNCOPE136AES() { userCR.getResources().clear(); userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); // 4. update user, assign a propagation priority resource but don't provide any password UserUR userUR = new UserUR(); @@ -547,9 +535,6 @@ public void issueSYNCOPE136AES() { userUR.setPassword(new PasswordPatch.Builder().onSyncope(false).resource(RESOURCE_NAME_LDAP).build()); ProvisioningResult result = updateUser(userUR); - assertNotNull(result); - userTO = result.getEntity(); - assertNotNull(userTO); // 5. verify that propagation was successful List props = result.getPropagationStatuses(); @@ -575,7 +560,6 @@ public void issueSYNCOPE136Random() { UserCR userCR = UserITCase.getUniqueSample("syncope136_Random@apache.org"); userCR.getResources().clear(); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); // 2. update user, assign a propagation priority resource but don't provide any password UserUR userUR = new UserUR(); @@ -728,7 +712,6 @@ public void issueSYNCOPE383() { UserCR userCR = UserITCase.getUniqueSample("syncope383@apache.org"); userCR.getResources().clear(); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); // 2. assign resource without specifying a new pwd and check propagation failure UserUR userUR = new UserUR(); @@ -770,7 +753,6 @@ public void issueSYNCOPE402() { userCR.getPlainAttrs().add(attr("surname", userId)); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); assertTrue(userTO.getResources().isEmpty()); // 2. update assigning a resource NOT forcing mandatory constraints @@ -836,14 +818,11 @@ public void issueSYNCOPE420() throws IOException { public void issueSYNCOPE426() { UserCR userCR = UserITCase.getUniqueSample("syncope426@syncope.apache.org"); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); UserUR userUR = new UserUR(); userUR.setKey(userTO.getKey()); userUR.setPassword(new PasswordPatch.Builder().value("anotherPassword123").build()); - userTO = USER_SERVICE.update(userUR).readEntity(new GenericType>() { - }).getEntity(); - assertNotNull(userTO); + assertDoesNotThrow(() -> updateUser(userUR)); } @Test @@ -853,7 +832,6 @@ public void issueSYNCOPE435() { userCR.setPassword(null); userCR.setStorePassword(false); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); // 2. try to update user by subscribing a resource - works but propagation is not even attempted UserUR userUR = new UserUR(); @@ -876,7 +854,6 @@ public void issueSYNCOPE454() { UserCR userCR = UserITCase.getUniqueSample("syncope454@syncope.apache.org"); userCR.getResources().add(RESOURCE_NAME_LDAP); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); // 2. read resource configuration for LDAP binding ConnObject connObject = @@ -948,7 +925,6 @@ public void issueSYNCOPE493() { userUR.getPlainAttrs().add(attrAddReplacePatch("firstname", "firstnameNew")); result = updateUser(userUR); - assertNotNull(userTO); assertEquals(1, result.getPropagationStatuses().size()); assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().getFirst().getStatus()); userTO = result.getEntity(); @@ -1078,7 +1054,6 @@ public void issueSYNCOPE391() { userCR.setStorePassword(false); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); assertNull(userTO.getPassword()); // 2. create existing user on csv and check that password on Syncope is null and that password on resource @@ -1097,16 +1072,15 @@ public void issueSYNCOPE391() { userCR.getResources().add(RESOURCE_NAME_CSV); userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); - ConnObject connObjectTO = + ConnObject connObject = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), userTO.getKey()); - assertNotNull(connObjectTO); + assertNotNull(connObject); // check if password has not changed assertEquals( "password0", - connObjectTO.getAttr(OperationalAttributes.PASSWORD_NAME).orElseThrow().getValues().getFirst()); + connObject.getAttr(OperationalAttributes.PASSWORD_NAME).orElseThrow().getValues().getFirst()); assertNull(userTO.getPassword()); // 3. create user with not null password and propagate onto resource-csv, specify not to save password on @@ -1118,16 +1092,15 @@ public void issueSYNCOPE391() { userCR.getResources().add(RESOURCE_NAME_CSV); userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); - connObjectTO = + connObject = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), userTO.getKey()); - assertNotNull(connObjectTO); + assertNotNull(connObject); // check if password has been propagated and that saved userTO's password is null assertEquals( "passwordTESTNULL1", - connObjectTO.getAttr(OperationalAttributes.PASSWORD_NAME).orElseThrow().getValues().getFirst()); + connObject.getAttr(OperationalAttributes.PASSWORD_NAME).orElseThrow().getValues().getFirst()); assertNull(userTO.getPassword()); // 4. create user and propagate password on resource-csv and on Syncope local storage @@ -1138,15 +1111,14 @@ public void issueSYNCOPE391() { // storePassword true by default userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); - connObjectTO = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), userTO.getKey()); - assertNotNull(connObjectTO); + connObject = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), userTO.getKey()); + assertNotNull(connObject); // check if password has been correctly propagated on Syncope and resource-csv as usual assertEquals( "passwordTESTNULL1", - connObjectTO.getAttr(OperationalAttributes.PASSWORD_NAME).orElseThrow().getValues().getFirst()); + connObject.getAttr(OperationalAttributes.PASSWORD_NAME).orElseThrow().getValues().getFirst()); SyncopeClient.Self self = CLIENT_FACTORY.create(userTO.getUsername(), "passwordTESTNULL1").self(); assertNotNull(self); @@ -1279,7 +1251,6 @@ public void issueSYNCOPE686() { userCR.getResources().clear(); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); // 5. update user with the new group, and don't provide any password UserUR userUR = new UserUR(); @@ -1376,7 +1347,7 @@ public void issueSYNCOPE1099() { GroupCR groupCR = GroupITCase.getSample("syncope1099G"); groupCR.getResources().clear(); groupCR.getResources().add(RESOURCE_NAME_TESTDB); - groupCR.setUDynMembershipCond("firstname==issueSYNCOPE1099"); + groupCR.getDynMembershipConds().put("USER", "firstname==issueSYNCOPE1099"); GroupTO group = createGroup(groupCR).getEntity(); assertNotNull(group); @@ -1405,7 +1376,6 @@ public void issueSYNCOPE1166() { UserCR userCR = UserITCase.getUniqueSample("syncope1166@apache.org"); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); UserUR userUR = new UserUR(); userUR.setKey(userTO.getKey()); @@ -1429,7 +1399,8 @@ public void issueSYNCOPE1166() { public void issueSYNCOPE1206() { // 1. create group with dynamic user condition 'cool==true' GroupCR dynGroupCR = GroupITCase.getSample("syncope1206"); - dynGroupCR.setUDynMembershipCond( + dynGroupCR.getDynMembershipConds().put( + "USER", SyncopeClient.getUserSearchConditionBuilder().is("cool").equalTo("true").query()); GroupTO dynGroup = createGroup(dynGroupCR).getEntity(); assertNotNull(dynGroup); @@ -1483,7 +1454,6 @@ public void issueSYNCOPE1337() { userCR.setPassword("Password123"); userCR.setRealm("/even/two"); UserTO userTO = createUser(userCR).getEntity(); - assertNotNull(userTO); // 3. attempt to set the same password value: fails UserUR req = new UserUR(); @@ -1499,13 +1469,11 @@ public void issueSYNCOPE1337() { // 4. set another password value: works req.setPassword(new PasswordPatch.Builder().onSyncope(true).value("Password124").build()); - userTO = updateUser(req).getEntity(); - assertNotNull(userTO); + assertDoesNotThrow(() -> updateUser(req).getEntity()); // 5. set the original password value: works (history length is 1) req.setPassword(new PasswordPatch.Builder().onSyncope(true).value("Password123").build()); - userTO = updateUser(req).getEntity(); - assertNotNull(userTO); + assertDoesNotThrow(() -> updateUser(req).getEntity()); } finally { // finally revert the cipher algorithm confParamOps.set(SyncopeConstants.MASTER_DOMAIN, "password.cipher.algorithm", original); @@ -1582,8 +1550,7 @@ public void issueSYNCOPE1699() throws Exception { assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); // reading user by its username still works - userTO = USER_SERVICE.read(req.getUsername().getValue()); - assertNotNull(userTO); + assertDoesNotThrow(() -> USER_SERVICE.read(req.getUsername().getValue())); } @Test diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java index 5dc4ad9ee27..a7baed7c8b2 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java @@ -352,13 +352,7 @@ public void passwordReset() throws Exception { } ANONYMOUS_CLIENT.getService(UserSelfService.class).requestPasswordReset(user.getUsername(), "Rossi"); - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } + awaitIfExtSearchEnabled(); // 4. get token (normally sent via e-mail, now reading as admin) String email = user.getPlainAttr("email").orElseThrow().getValues().getFirst(); diff --git a/fit/enduser-reference/pom.xml b/fit/enduser-reference/pom.xml index 659f0abb17e..004ff0fc8e3 100644 --- a/fit/enduser-reference/pom.xml +++ b/fit/enduser-reference/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-fit - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope FIT Enduser Reference diff --git a/fit/enduser-reference/src/main/webapp/WEB-INF/web.xml b/fit/enduser-reference/src/main/webapp/WEB-INF/web.xml index 8933f2cfb68..9a59391540c 100644 --- a/fit/enduser-reference/src/main/webapp/WEB-INF/web.xml +++ b/fit/enduser-reference/src/main/webapp/WEB-INF/web.xml @@ -20,8 +20,8 @@ under the License. Apache Syncope ${syncope.version} Enduser diff --git a/fit/persistence-embedded/pom.xml b/fit/persistence-embedded/pom.xml index 4a2874ecf64..e549f834cc9 100644 --- a/fit/persistence-embedded/pom.xml +++ b/fit/persistence-embedded/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-fit - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope FIT Persistence Embedded diff --git a/fit/pom.xml b/fit/pom.xml index 0c68debff82..53ae1dc8148 100644 --- a/fit/pom.xml +++ b/fit/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope FIT diff --git a/fit/wa-reference/pom.xml b/fit/wa-reference/pom.xml index fa4aeefa0aa..447a917e0b0 100644 --- a/fit/wa-reference/pom.xml +++ b/fit/wa-reference/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-fit - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope FIT WA Reference diff --git a/fit/wa-reference/src/main/resources/wa-embedded.properties b/fit/wa-reference/src/main/resources/wa-embedded.properties index 3861744e87f..031a9eb0e6e 100644 --- a/fit/wa-reference/src/main/resources/wa-embedded.properties +++ b/fit/wa-reference/src/main/resources/wa-embedded.properties @@ -19,6 +19,8 @@ CasFeatureModule.AccountManagement.enabled=true spring.main.allow-circular-references=true spring.cloud.refresh.never-refreshable=com.zaxxer.hikari.HikariDataSource,org.apereo.cas.configuration.model.core.CasServerProperties +management.endpoints.web.exposure.include=info,health,env,beans,loggers,ssoSessions,registeredServices,refresh,authenticationHandlers,authenticationPolicies,resolveAttributes + keymaster.address=https://localhost:9443/syncope/rest/keymaster keymaster.username=${anonymousUser} keymaster.password=${anonymousKey} diff --git a/fit/wa-reference/src/main/webapp/WEB-INF/web.xml b/fit/wa-reference/src/main/webapp/WEB-INF/web.xml index b1fce6e8cc4..87401d7ee1b 100644 --- a/fit/wa-reference/src/main/webapp/WEB-INF/web.xml +++ b/fit/wa-reference/src/main/webapp/WEB-INF/web.xml @@ -20,8 +20,8 @@ under the License. Apache Syncope ${syncope.version} WA diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java index ad1596536b8..7be1034a579 100644 --- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java +++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java @@ -22,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.fasterxml.jackson.databind.json.JsonMapper; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import java.io.IOException; @@ -41,6 +40,7 @@ import org.apache.http.message.BasicNameValuePair; import org.apache.syncope.client.lib.SyncopeClient; import org.apache.syncope.client.lib.SyncopeClientFactoryBean; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.rest.api.service.ClientAppService; import org.apache.syncope.common.rest.api.service.ImplementationService; import org.apache.syncope.common.rest.api.service.OIDCC4UIProviderService; @@ -58,12 +58,13 @@ import org.junit.jupiter.api.BeforeAll; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.databind.json.JsonMapper; public abstract class AbstractITCase { protected static final Logger LOG = LoggerFactory.getLogger(AbstractITCase.class); - protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + protected static final JsonMapper MAPPER = new SyncopeJsonMapper(); protected static final String ADMIN_UNAME = "admin"; diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractOIDCITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractOIDCITCase.java index edab40638f6..6d1b007abba 100644 --- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractOIDCITCase.java +++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractOIDCITCase.java @@ -28,8 +28,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import jakarta.ws.rs.core.Form; @@ -70,6 +68,8 @@ import org.apereo.cas.oidc.OidcConstants; import org.jsoup.Jsoup; import org.junit.jupiter.api.Test; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ObjectNode; abstract class AbstractOIDCITCase extends AbstractSRAITCase { @@ -255,7 +255,7 @@ void web() throws IOException { response = httpclient.execute(get, context); headers = checkGetResponse(response, originalRequestURI.replace("/protected", "")); - assertTrue(headers.get(HttpHeaders.COOKIE).asText().contains("SESSION")); + assertTrue(headers.get(HttpHeaders.COOKIE).asString().contains("SESSION")); // 3. logout get = new HttpGet(SRA_ADDRESS + "/protected/logout"); @@ -311,13 +311,13 @@ void rest() throws IOException, ParseException { if (checkIdToken()) { // 1a. take and verify id_token - String idToken = json.get("id_token").asText(); + String idToken = json.get("id_token").asString(); assertNotNull(idToken); checkJWT(idToken, true); } // 1b. take and verify access_token - String accessToken = json.get("access_token").asText(); + String accessToken = json.get("access_token").asString(); checkJWT(accessToken, false); // 2. access protected route @@ -331,12 +331,12 @@ void rest() throws IOException, ParseException { json = MAPPER.readTree(response.readEntity(String.class)); ObjectNode headers = (ObjectNode) json.get("headers"); - assertEquals(MediaType.APPLICATION_JSON, headers.get(HttpHeaders.ACCEPT).asText()); - assertEquals(MediaType.APPLICATION_JSON, headers.get(HttpHeaders.CONTENT_TYPE).asText()); - assertThat(headers.get("X-Forwarded-Host").asText(), is(oneOf("localhost:8080", "127.0.0.1:8080"))); + assertEquals(MediaType.APPLICATION_JSON, headers.get(HttpHeaders.ACCEPT).asString()); + assertEquals(MediaType.APPLICATION_JSON, headers.get(HttpHeaders.CONTENT_TYPE).asString()); + assertThat(headers.get("X-Forwarded-Host").asString(), is(oneOf("localhost:8080", "127.0.0.1:8080"))); String withHost = client.getBaseURI().toASCIIString().replace("/protected", ""); String withIP = withHost.replace("localhost", "127.0.0.1"); - assertThat(json.get("url").asText(), is(oneOf(withHost, withIP))); + assertThat(json.get("url").asString(), is(oneOf(withHost, withIP))); } } diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java index b6ed937ae20..4b950fcaf0b 100644 --- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java +++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java @@ -27,9 +27,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -61,6 +58,9 @@ import org.apache.syncope.fit.AbstractITCase; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.ObjectNode; abstract class AbstractSRAITCase extends AbstractITCase { @@ -246,21 +246,21 @@ protected static ObjectNode checkGetResponse( JsonNode json = MAPPER.readTree(EntityUtils.toString(response.getEntity())); ObjectNode args = (ObjectNode) json.get("args"); - assertEquals("value1", args.get("key1").asText()); + assertEquals("value1", args.get("key1").asString()); ArrayNode key2 = (ArrayNode) args.get("key2"); - assertEquals("value2", key2.get(0).asText()); - assertEquals("value3", key2.get(1).asText()); + assertEquals("value2", key2.get(0).asString()); + assertEquals("value3", key2.get(1).asString()); - assertEquals("an url encoded value: this!", args.get("key3").asText()); + assertEquals("an url encoded value: this!", args.get("key3").asString()); ObjectNode headers = (ObjectNode) json.get("headers"); - assertEquals(MediaType.TEXT_HTML, headers.get(HttpHeaders.ACCEPT).asText()); - assertThat(headers.get("X-Forwarded-Host").asText(), is(oneOf("localhost:8080", "127.0.0.1:8080"))); + assertEquals(MediaType.TEXT_HTML, headers.get(HttpHeaders.ACCEPT).asString()); + assertThat(headers.get("X-Forwarded-Host").asString(), is(oneOf("localhost:8080", "127.0.0.1:8080"))); String withHost = StringUtils.substringBefore(originalRequestURI, "?"); String withIP = withHost.replace("localhost", "127.0.0.1"); - assertThat(StringUtils.substringBefore(json.get("url").asText(), "?"), is(oneOf(withHost, withIP))); + assertThat(StringUtils.substringBefore(json.get("url").asString(), "?"), is(oneOf(withHost, withIP))); return headers; } diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/CASSRAITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/CASSRAITCase.java index 825d7315bfb..6f985f120b0 100644 --- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/CASSRAITCase.java +++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/CASSRAITCase.java @@ -23,7 +23,6 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; -import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -52,6 +51,7 @@ import org.apache.syncope.common.rest.api.service.wa.WAConfigService; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import tools.jackson.databind.node.ObjectNode; public class CASSRAITCase extends AbstractSRAITCase { @@ -145,7 +145,7 @@ public void web() throws IOException { response = httpclient.execute(get, context); headers = checkGetResponse(response, originalRequestURI.replace("/protected", "")); - assertFalse(headers.get(HttpHeaders.COOKIE).asText().isBlank()); + assertFalse(headers.get(HttpHeaders.COOKIE).asString().isBlank()); // 3. logout get = new HttpGet(SRA_ADDRESS + "/protected/logout"); diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/SAML2SRAITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/SAML2SRAITCase.java index 808378bc9ce..b7b813b0f2b 100644 --- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/SAML2SRAITCase.java +++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/SAML2SRAITCase.java @@ -23,7 +23,6 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; -import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -53,6 +52,7 @@ import org.apache.syncope.common.rest.api.service.wa.WAConfigService; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import tools.jackson.databind.node.ObjectNode; public class SAML2SRAITCase extends AbstractSRAITCase { @@ -214,7 +214,7 @@ public void web() throws IOException { get.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); try (CloseableHttpResponse response = httpclient.execute(get, context)) { ObjectNode headers = checkGetResponse(response, originalRequestURI.replace("/protected", "")); - assertFalse(headers.get(HttpHeaders.COOKIE).asText().isBlank()); + assertFalse(headers.get(HttpHeaders.COOKIE).asString().isBlank()); } // 3. logout diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OpenFGAUIITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OpenFGAUIITCase.java index 2bae4172d1d..ffd99f8f443 100644 --- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OpenFGAUIITCase.java +++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OpenFGAUIITCase.java @@ -23,8 +23,6 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeFalse; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; import jakarta.ws.rs.core.Response; import java.io.IOException; import java.util.List; @@ -47,6 +45,7 @@ import org.apache.syncope.common.rest.api.beans.ExecSpecs; import org.apache.syncope.common.rest.api.service.wa.WAConfigService; import org.junit.jupiter.api.BeforeAll; +import tools.jackson.databind.JsonNode; public class OpenFGAUIITCase extends OIDCC4UIITCase { @@ -99,14 +98,14 @@ public static void initOpenFga() { }); } - private static AccessPolicyTO getAccessPolicy() throws JsonProcessingException { + private static AccessPolicyTO getAccessPolicy() { JsonNode health = MAPPER.readTree(WebClient.create( StringUtils.substringBefore(CORE_ADDRESS, "/rest") + "/actuator/health", ANONYMOUS_USER, ANONYMOUS_KEY, null). get().readEntity(String.class)); JsonNode openfga = health.get("components").get("openFga").get("details"); - String baseUri = openfga.get("baseUri").asText(); - String storeId = openfga.get(SyncopeConstants.MASTER_DOMAIN).get("storeId").asText(); + String baseUri = openfga.get("baseUri").asString(); + String storeId = openfga.get(SyncopeConstants.MASTER_DOMAIN).get("storeId").asString(); String description = "OpenFGA access policy"; @@ -138,7 +137,7 @@ private static AccessPolicyTO getAccessPolicy() throws JsonProcessingException { } @BeforeAll - public static void consoleOpenFGASetup() throws JsonProcessingException { + public static void consoleOpenFGASetup() { ClientAppTO clientApp = CLIENT_APP_SERVICE.list(ClientAppType.OIDCRP).stream(). filter(app -> getAppName(CONSOLE_ADDRESS).equals(app.getName())). findFirst().orElseThrow(); diff --git a/fit/wa-reference/src/test/resources/sra-cas.properties b/fit/wa-reference/src/test/resources/sra-cas.properties index 34b0b897e3f..484b150ca76 100644 --- a/fit/wa-reference/src/test/resources/sra-cas.properties +++ b/fit/wa-reference/src/test/resources/sra-cas.properties @@ -14,9 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -spring.cloud.gateway.forwarded.enabled=true -spring.cloud.gateway.x-forwarded.enabled=true -spring.cloud.gateway.trusted-proxies=.* +spring.cloud.gateway.server.webflux.trusted-proxies=.* keymaster.address=https://localhost:9443/syncope/rest/keymaster keymaster.username=${anonymousUser} diff --git a/fit/wa-reference/src/test/resources/sra-oauth2.properties b/fit/wa-reference/src/test/resources/sra-oauth2.properties index bc527e22106..318d0cadc03 100644 --- a/fit/wa-reference/src/test/resources/sra-oauth2.properties +++ b/fit/wa-reference/src/test/resources/sra-oauth2.properties @@ -14,9 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -spring.cloud.gateway.forwarded.enabled=true -spring.cloud.gateway.x-forwarded.enabled=true -spring.cloud.gateway.trusted-proxies=.* +spring.cloud.gateway.server.webflux.trusted-proxies=.* keymaster.address=https://localhost:9443/syncope/rest/keymaster keymaster.username=${anonymousUser} diff --git a/fit/wa-reference/src/test/resources/sra-oidc.properties b/fit/wa-reference/src/test/resources/sra-oidc.properties index df2d727bb9b..5dfa69c5bc3 100644 --- a/fit/wa-reference/src/test/resources/sra-oidc.properties +++ b/fit/wa-reference/src/test/resources/sra-oidc.properties @@ -14,9 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -spring.cloud.gateway.forwarded.enabled=true -spring.cloud.gateway.x-forwarded.enabled=true -spring.cloud.gateway.trusted-proxies=.* +spring.cloud.gateway.server.webflux.trusted-proxies=.* keymaster.address=https://localhost:9443/syncope/rest/keymaster keymaster.username=${anonymousUser} diff --git a/fit/wa-reference/src/test/resources/sra-saml2.properties b/fit/wa-reference/src/test/resources/sra-saml2.properties index 33cd5faa295..fd0b7bd0bc6 100644 --- a/fit/wa-reference/src/test/resources/sra-saml2.properties +++ b/fit/wa-reference/src/test/resources/sra-saml2.properties @@ -14,9 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -spring.cloud.gateway.forwarded.enabled=true -spring.cloud.gateway.x-forwarded.enabled=true -spring.cloud.gateway.trusted-proxies=.* +spring.cloud.gateway.server.webflux.trusted-proxies=.* keymaster.address=https://localhost:9443/syncope/rest/keymaster keymaster.username=${anonymousUser} diff --git a/pom.xml b/pom.xml index 034d08fba0d..10efb784704 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ under the License. org.apache.syncope syncope - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT pom @@ -412,7 +412,7 @@ under the License. - 2025-04-05T12:25:38Z + 2025-11-25T06:53:52Z ${project.version} 1.6.1.0 @@ -431,14 +431,14 @@ under the License. 1.0.0 0.1.0 - 4.1.4 + 4.2.0-SNAPSHOT 1.83 10.7 - 3.5.10 - 4.3.3 + 4.0.2 + 5.0.1 - 4.1.1 + 7.2.3.Final 1.9.3 @@ -461,9 +461,9 @@ under the License. 3.6.1 1.15.0 - 6.2.2 + 6.3.1 - 7.3.3 + 8.0.0-RC1 4.0.4 2.2.42 @@ -523,9 +523,9 @@ under the License. 9805 60000 - 10.1.52 + 11.0.18 39.0.0.Final - 6.2025.11 + 7.2026.1 4.1.6 18-alpine @@ -565,7 +565,7 @@ under the License. ${project.build.directory}/surefire-reports target/site/jacoco/jacoco.xml - 21 + 25 ${basedir} ${rootpom.basedir} UTF-8 @@ -577,7 +577,7 @@ under the License. jakarta.servlet jakarta.servlet-api - 6.0.0 + 6.1.0 provided @@ -629,12 +629,6 @@ under the License. io.swagger.core.v3 swagger-annotations-jakarta ${swagger-core.version} - - - com.fasterxml.jackson.datatype - jackson-dataformat-yaml - - io.swagger.core.v3 @@ -666,10 +660,16 @@ under the License. - org.apache.openjpa - openjpa - ${openjpa.version} + org.hibernate.orm + hibernate-core + ${hibernate.version} + + org.hibernate.orm + hibernate-jcache + ${hibernate.version} + + com.fasterxml.uuid java-uuid-generator @@ -702,13 +702,21 @@ under the License. org.springframework.boot - spring-boot-starter-data-jpa + spring-boot-starter-security ${spring-boot.version} - org.hibernate.orm - hibernate-core + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-data-jpa + ${spring-boot.version} + org.springframework.boot spring-boot-starter-logging @@ -750,8 +758,8 @@ under the License. spring-boot-starter-logging - com.fasterxml.jackson.module - jackson-module-parameter-names + org.springframework.boot + spring-boot-jackson @@ -759,12 +767,6 @@ under the License. org.springframework.boot spring-boot-starter-webflux ${spring-boot.version} - - - com.fasterxml.jackson.module - jackson-module-parameter-names - - @@ -826,11 +828,6 @@ under the License. ${spring-boot.version} - - org.springframework.boot - spring-boot-actuator - ${spring-boot.version} - org.springframework.boot spring-boot-properties-migrator @@ -897,25 +894,23 @@ under the License. org.springframework.cloud - spring-cloud-starter-gateway - ${spring-cloud-gateway.version} - - - org.bouncycastle - bcpkix-jdk15on - - + spring-cloud-starter-gateway-server-webflux + ${spring-cloud-version} org.springframework.cloud spring-cloud-starter-bootstrap - 4.3.1 - - - org.bouncycastle - bcpkix-jdk15on - - + ${spring-cloud-version} + + + org.springframework.cloud + spring-cloud-commons + ${spring-cloud-version} + + + org.springframework.cloud + spring-cloud-context + ${spring-cloud-version} @@ -927,18 +922,17 @@ under the License. org.apache.commons commons-jexl3 ${commons-jexl.version} - - - commons-logging - commons-logging - - org.apache.commons commons-text ${commons-text.version} + + commons-logging + commons-logging + provided + org.apache.tika @@ -1209,12 +1203,6 @@ under the License. org.apache.pdfbox pdfbox 3.0.6 - - - commons-logging - commons-logging - - @@ -1475,9 +1463,9 @@ under the License. test - org.springframework.cloud - spring-cloud-contract-wiremock - 4.3.1 + org.wiremock.integrations + wiremock-spring-boot + 4.0.8 test @@ -1643,9 +1631,9 @@ under the License. - org.apache.openjpa - openjpa-maven-plugin - ${openjpa.version} + org.hibernate.orm + hibernate-maven-plugin + ${hibernate.version} @@ -2181,7 +2169,7 @@ under the License. 3.12.0 ${targetJdk} - apidocs/4.1 + apidocs/5.0 false true true @@ -2191,21 +2179,22 @@ under the License. https://docs.oracle.com/en/java/javase/21/docs/api/ - https://jakarta.ee/specifications/platform/10/apidocs/ + https://jakarta.ee/specifications/platform/11/apidocs/ + https://jakarta.ee/specifications/persistence/3.2/apidocs/ https://www.slf4j.org/api/ https://connid.tirasa.net/apidocs/1.6/ https://cxf.apache.org/javadoc/latest/ - https://javadoc.io/static/com.fasterxml.jackson.core/jackson-core/2.19.4/ - https://javadoc.io/static/com.fasterxml.jackson.core/jackson-databind/2.19.4/ - https://javadoc.io/static/com.fasterxml.jackson.core/jackson-annotations/2.19.4/ - https://javadoc.io/static/com.fasterxml.jackson.dataformat/jackson-dataformat-csv/2.19.4/ + https://javadoc.io/static/tools.jackson.core/jackson-core/3.0.3/ + https://javadoc.io/static/tools.jackson.core/jackson-databind/3.0.3/ + https://javadoc.io/static/tools.jackson.dataformat/jackson-dataformat-csv/3.0.3/ https://nightlies.apache.org/wicket/apidocs/10.x/ https://commons.apache.org/proper/commons-lang/javadocs/api-release/ https://commons.apache.org/proper/commons-jexl/apidocs/ https://tika.apache.org/3.2.3/api/ - https://docs.spring.io/spring-boot/3.5/api/java/ - https://docs.spring.io/spring-framework/docs/6.2.x/javadoc-api/ - https://docs.spring.io/spring-security/site/docs/6.5.x/api/ + https://docs.hibernate.org/orm/7.2/javadocs/ + https://docs.spring.io/spring-boot/4.0/api/java/ + https://docs.spring.io/spring-framework/docs/7.0.x/javadoc-api/ + https://docs.spring.io/spring-security/site/docs/7.0.x/api/ https://www.flowable.com/open-source/docs/javadocs/ https://docs.swagger.io/swagger-core/v2.2.41/apidocs/ diff --git a/sra/LICENSE b/sra/LICENSE index 6f6ee6d4e4f..96d39d0152b 100644 --- a/sra/LICENSE +++ b/sra/LICENSE @@ -263,11 +263,6 @@ This is licensed under the AL 2.0, see above. == -For org.json compatible Android implementation extracted from the Android SDK: -This is licensed under the AL 2.0, see above. - -== - For Jakarta Activation (https://eclipse-ee4j.github.io/jaf/): This is licensed under the EDL 1.0: diff --git a/sra/NOTICE b/sra/NOTICE index 2de9f9fba23..e287cc63f92 100644 --- a/sra/NOTICE +++ b/sra/NOTICE @@ -131,7 +131,7 @@ All content is the property of the respective authors or their employers. == This product includes software developed by Hibernate Validator -Copyright: Red Hat Inc. and Hibernate Authors +Copyright 2022, The Hibernate Validator project == diff --git a/sra/pom.xml b/sra/pom.xml index 80310892b90..87c55049e2b 100644 --- a/sra/pom.xml +++ b/sra/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope SRA @@ -40,18 +40,12 @@ under the License. org.springframework.cloud - spring-cloud-starter-gateway + spring-cloud-starter-gateway-server-webflux org.springframework.boot spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-starter-logging - - @@ -87,6 +81,11 @@ under the License. disruptor + + org.springframework.boot + spring-boot-health + + org.pac4j pac4j-saml @@ -136,10 +135,15 @@ under the License. test - org.springframework.cloud - spring-cloud-contract-wiremock + org.wiremock.integrations + wiremock-spring-boot test + + org.springframework.boot + spring-boot-webtestclient + test + org.springframework.boot spring-boot-starter-test @@ -189,7 +193,6 @@ under the License. ZIP exec false - CLASSIC repackage diff --git a/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java b/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java index 553ffaf94d4..9efe9b8347a 100644 --- a/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java +++ b/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java @@ -36,6 +36,7 @@ import org.apache.syncope.common.lib.to.SRARouteTO; import org.apache.syncope.common.lib.types.SRARouteFilter; import org.apache.syncope.common.lib.types.SRARoutePredicate; +import org.apache.syncope.common.lib.types.SRARoutePredicateCond; import org.apache.syncope.common.rest.api.service.SRARouteService; import org.apache.syncope.sra.filters.ClientCertsToRequestHeaderFilterFactory; import org.apache.syncope.sra.filters.CustomGatewayFilterFactory; @@ -97,6 +98,10 @@ public class RouteProvider { + protected record TranslatedPredicate(SRARoutePredicateCond cond, AsyncPredicate predicate) { + + } + protected static final Logger LOG = LoggerFactory.getLogger(RouteProvider.class); protected final ServiceOps serviceOps; @@ -128,7 +133,7 @@ public RouteProvider( } @SuppressWarnings("unchecked") - protected GatewayFilter toFilter(final SRARouteTO route, final SRARouteFilter gwfilter) + protected GatewayFilter translate(final SRARouteTO route, final SRARouteFilter gwfilter) throws ClassNotFoundException, NumberFormatException { GatewayFilter filter; @@ -404,7 +409,7 @@ protected GatewayFilter toFilter(final SRARouteTO route, final SRARouteFilter gw return filter instanceof Ordered ? filter : new OrderedGatewayFilter(filter, 0); } - protected AsyncPredicate toPredicate(final SRARoutePredicate gwpredicate, final boolean negate) + protected AsyncPredicate translate(final SRARoutePredicate gwpredicate) throws ClassNotFoundException, NumberFormatException { AsyncPredicate predicate; @@ -477,7 +482,7 @@ protected AsyncPredicate toPredicate(final SRARoutePredicate case WEIGHT: String[] weigthArgs = gwpredicate.getArgs().split(","); - Mutable weight = new MutableObject(); + Mutable weight = new MutableObject<>(); try { weight.setValue(Integer.valueOf(weigthArgs[1].trim())); } catch (NumberFormatException e) { @@ -506,7 +511,7 @@ protected AsyncPredicate toPredicate(final SRARoutePredicate throw new IllegalArgumentException("Could not translate predicate " + gwpredicate); } - return negate ? predicate.negate() : predicate; + return gwpredicate.isNegate() ? predicate.negate() : predicate; } protected Route.AsyncBuilder toRoute(final SRARouteTO gwroute) { @@ -516,36 +521,40 @@ protected Route.AsyncBuilder toRoute(final SRARouteTO gwroute) { if (gwroute.getPredicates().isEmpty()) { builder.predicate(exchange -> true); } else { + List asyncPredicates = new ArrayList<>(); + gwroute.getPredicates().forEach(gwpredicate -> { - if (builder.getPredicate() == null) { + if (asyncPredicates.isEmpty()) { try { - builder.asyncPredicate(toPredicate(gwpredicate, gwpredicate.isNegate())); + asyncPredicates.add(new TranslatedPredicate(null, translate(gwpredicate))); } catch (Exception e) { LOG.error("Could not translate {}, skipping", gwpredicate, e); } } else { try { - switch (gwpredicate.getCond()) { - case OR: - builder.or(toPredicate(gwpredicate, gwpredicate.isNegate())); - break; - - case AND: - default: - builder.and(toPredicate(gwpredicate, gwpredicate.isNegate())); - } + asyncPredicates.add(new TranslatedPredicate(gwpredicate.getCond(), translate(gwpredicate))); } catch (Exception e) { LOG.error("Could not translate {}, skipping", gwpredicate, e); } } }); + + asyncPredicates.forEach(translated -> { + if (translated.cond() == null) { + builder.asyncPredicate(translated.predicate()); + } else if (translated.cond() == SRARoutePredicateCond.AND) { + builder.and(translated.predicate()); + } else if (translated.cond() == SRARoutePredicateCond.OR) { + builder.or(translated.predicate()); + } + }); } if (!gwroute.getFilters().isEmpty()) { builder.filters(gwroute.getFilters().stream(). map(gwfilter -> { try { - return toFilter(gwroute, gwfilter); + return translate(gwroute, gwfilter); } catch (Exception e) { LOG.error("Could not translate {}, skipping", gwfilter, e); return null; diff --git a/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java b/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java index b8ade85df3e..10b098d62c6 100644 --- a/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java +++ b/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java @@ -43,9 +43,9 @@ import org.pac4j.saml.metadata.keystore.BaseSAML2KeystoreGenerator; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.actuate.autoconfigure.security.reactive.EndpointRequest; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.security.autoconfigure.actuate.web.reactive.EndpointRequest; import org.springframework.cache.CacheManager; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; diff --git a/sra/src/main/java/org/apache/syncope/sra/actuate/SRASessions.java b/sra/src/main/java/org/apache/syncope/sra/actuate/SRASessions.java index 24387b3a79d..e9de1323c5b 100644 --- a/sra/src/main/java/org/apache/syncope/sra/actuate/SRASessions.java +++ b/sra/src/main/java/org/apache/syncope/sra/actuate/SRASessions.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.sra.actuate; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.time.OffsetDateTime; import java.util.List; import java.util.Objects; @@ -40,6 +38,7 @@ import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository; import org.springframework.session.MapSession; +import tools.jackson.databind.json.JsonMapper; @Endpoint(id = "sraSessions") public class SRASessions { @@ -65,21 +64,21 @@ protected static AMSession map(final MapSession mapSession) { session.setKey(mapSession.getId()); session.setAuthenticationDate(mapSession.getCreationTime().atOffset(OffsetDateTime.now().getOffset())); - String principal; - if (ctx.getAuthentication() instanceof SAML2AuthenticationToken saml2AuthenticationToken) { - principal = saml2AuthenticationToken.getPrincipal().getUserProfile().getUsername(); - } else if (ctx.getAuthentication() instanceof CASAuthenticationToken casAuthenticationToken) { - principal = casAuthenticationToken.getPrincipal().getPrincipal().getName(); - } else if (ctx.getAuthentication() instanceof OAuth2AuthenticationToken oauth2AuthenticationToken) { - principal = oauth2AuthenticationToken.getPrincipal().getName(); - } else { - principal = ctx.getAuthentication().getPrincipal().toString(); - } + String principal = switch (ctx.getAuthentication()) { + case SAML2AuthenticationToken saml2AuthenticationToken -> + saml2AuthenticationToken.getPrincipal().getUserProfile().getUsername(); + case CASAuthenticationToken casAuthenticationToken -> + casAuthenticationToken.getPrincipal().getPrincipal().getName(); + case OAuth2AuthenticationToken oauth2AuthenticationToken -> + oauth2AuthenticationToken.getPrincipal().getName(); + default -> + ctx.getAuthentication().getPrincipal().toString(); + }; session.setPrincipal(principal); try { session.setJson(MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(ctx.getAuthentication())); - } catch (JsonProcessingException e) { + } catch (Exception e) { LOG.error("While serializing session {}", mapSession.getId(), e); } diff --git a/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeCoreHealthIndicator.java b/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeCoreHealthIndicator.java index 94f9c0095d9..c0c0f1fc96e 100644 --- a/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeCoreHealthIndicator.java +++ b/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeCoreHealthIndicator.java @@ -25,9 +25,9 @@ import org.apache.syncope.common.rest.api.service.SRARouteService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.actuate.health.Health; -import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.boot.actuate.health.Status; +import org.springframework.boot.health.contributor.Health; +import org.springframework.boot.health.contributor.HealthIndicator; +import org.springframework.boot.health.contributor.Status; public class SyncopeCoreHealthIndicator implements HealthIndicator { diff --git a/sra/src/main/java/org/apache/syncope/sra/security/cas/CASAuthenticationToken.java b/sra/src/main/java/org/apache/syncope/sra/security/cas/CASAuthenticationToken.java index 2c07384d99e..fd07935a64d 100644 --- a/sra/src/main/java/org/apache/syncope/sra/security/cas/CASAuthenticationToken.java +++ b/sra/src/main/java/org/apache/syncope/sra/security/cas/CASAuthenticationToken.java @@ -18,9 +18,11 @@ */ package org.apache.syncope.sra.security.cas; +import java.util.Collection; import org.apache.commons.lang3.StringUtils; import org.apereo.cas.client.validation.Assertion; import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; public class CASAuthenticationToken extends AbstractAuthenticationToken { @@ -29,7 +31,7 @@ public class CASAuthenticationToken extends AbstractAuthenticationToken { private final Assertion assertion; public CASAuthenticationToken(final Assertion assertion) { - super(null); + super((Collection) null); this.assertion = assertion; this.setAuthenticated(true); } diff --git a/sra/src/test/java/org/apache/syncope/sra/AbstractTest.java b/sra/src/test/java/org/apache/syncope/sra/AbstractTest.java index cd4d5e1e7dc..540581bb765 100644 --- a/sra/src/test/java/org/apache/syncope/sra/AbstractTest.java +++ b/sra/src/test/java/org/apache/syncope/sra/AbstractTest.java @@ -18,26 +18,27 @@ */ package org.apache.syncope.sra; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import java.io.IOException; import java.net.Socket; import java.util.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; +import org.springframework.boot.webtestclient.autoconfigure.AutoConfigureWebTestClient; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; +import org.wiremock.spring.EnableWireMock; +import tools.jackson.databind.json.JsonMapper; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @TestPropertySource(locations = { "classpath:sra.properties", "classpath:test.properties" }) @ContextConfiguration(initializers = ZookeeperTestingServer.class) -@AutoConfigureWireMock(port = 0) +@AutoConfigureWebTestClient +@EnableWireMock public abstract class AbstractTest { - protected static final JsonMapper MAPPER = JsonMapper.builder().addModule(new JavaTimeModule()).build(); + protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); public static boolean available(final int port) { try (Socket ignored = new Socket("localhost", port)) { diff --git a/sra/src/test/java/org/apache/syncope/sra/RouteProviderTest.java b/sra/src/test/java/org/apache/syncope/sra/RouteProviderTest.java index aea595d73a6..048d41e9494 100644 --- a/sra/src/test/java/org/apache/syncope/sra/RouteProviderTest.java +++ b/sra/src/test/java/org/apache/syncope/sra/RouteProviderTest.java @@ -33,8 +33,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.fasterxml.jackson.databind.JsonNode; -import java.io.IOException; import java.net.URI; import java.time.ZonedDateTime; import org.apache.syncope.common.lib.to.SRARouteTO; @@ -64,6 +62,7 @@ import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.BodyInserters; +import tools.jackson.databind.JsonNode; public class RouteProviderTest extends AbstractTest { @@ -676,7 +675,7 @@ public void custom() { JsonNode body = MAPPER.readTree(response.getResponseBody()); assertTrue(body.has("customized")); assertTrue(body.get("customized").asBoolean()); - } catch (IOException e) { + } catch (Exception e) { fail(e.getMessage(), e); } }); diff --git a/sra/src/test/java/org/apache/syncope/sra/SyncopeCoreTestingServer.java b/sra/src/test/java/org/apache/syncope/sra/SyncopeCoreTestingServer.java index d7e9ca9d5d9..f0da28b032b 100644 --- a/sra/src/test/java/org/apache/syncope/sra/SyncopeCoreTestingServer.java +++ b/sra/src/test/java/org/apache/syncope/sra/SyncopeCoreTestingServer.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.sra; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.core.Response; import java.util.Comparator; @@ -30,12 +28,14 @@ import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider; import org.apache.syncope.common.keymaster.client.api.ServiceOps; import org.apache.syncope.common.keymaster.client.api.model.NetworkService; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.to.SRARouteTO; import org.apache.syncope.common.rest.api.service.SRARouteService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; +import tools.jackson.jakarta.rs.json.JacksonJsonProvider; @Component public class SyncopeCoreTestingServer implements ApplicationListener { @@ -61,7 +61,7 @@ public void onApplicationEvent(final ContextRefreshedEvent event) { sf.setResourceClasses(SRARouteService.class); sf.setResourceProvider(SRARouteService.class, new SingletonResourceProvider(new StubSRARouteService(), true)); - sf.setProviders(List.of(new JacksonJsonProvider(JsonMapper.builder().findAndAddModules().build()))); + sf.setProviders(List.of(new JacksonJsonProvider(new SyncopeJsonMapper()))); sf.create(); // 2. register Core in Keymaster diff --git a/sra/src/test/java/org/apache/syncope/sra/filters/BodyPropertyAddingGatewayFilterFactory.java b/sra/src/test/java/org/apache/syncope/sra/filters/BodyPropertyAddingGatewayFilterFactory.java index 9feca897cdb..bd60da569a1 100644 --- a/sra/src/test/java/org/apache/syncope/sra/filters/BodyPropertyAddingGatewayFilterFactory.java +++ b/sra/src/test/java/org/apache/syncope/sra/filters/BodyPropertyAddingGatewayFilterFactory.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.sra.filters; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -43,6 +41,8 @@ import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.node.ObjectNode; /** * Inspired by {@link org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory}. @@ -118,7 +118,7 @@ public Mono writeWith(final Publisher body) { content.put(kv[0], kv[1]); output = MAPPER.writeValueAsBytes(content); - } catch (IOException e) { + } catch (Exception e) { LOG.error("While (de)serializing as JSON", e); output = ArrayUtils.clone(input); } finally { diff --git a/sra/src/test/java/org/apache/syncope/sra/predicates/BodyPropertyMatchingRoutePredicateFactory.java b/sra/src/test/java/org/apache/syncope/sra/predicates/BodyPropertyMatchingRoutePredicateFactory.java index cdbd3fde8e0..521f3b9d2b7 100644 --- a/sra/src/test/java/org/apache/syncope/sra/predicates/BodyPropertyMatchingRoutePredicateFactory.java +++ b/sra/src/test/java/org/apache/syncope/sra/predicates/BodyPropertyMatchingRoutePredicateFactory.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.sra.predicates; -import com.fasterxml.jackson.databind.JsonNode; import java.util.List; import org.springframework.cloud.gateway.handler.AsyncPredicate; import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; @@ -27,9 +26,10 @@ import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; +import tools.jackson.databind.JsonNode; /** - * Inspired by {@link org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory}. + * Inspired by {@link org.springframework.cloud.gateway.handler.predicate.ReadBodyRoutePredicateFactory}. */ public class BodyPropertyMatchingRoutePredicateFactory extends CustomRoutePredicateFactory { diff --git a/sra/src/test/resources/log4j2.xml b/sra/src/test/resources/log4j2.xml new file mode 100644 index 00000000000..6fdccd14e40 --- /dev/null +++ b/sra/src/test/resources/log4j2.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/asciidoc/getting-started/introduction.adoc b/src/main/asciidoc/getting-started/introduction.adoc index 74d03049e5a..5e11e15f6ce 100644 --- a/src/main/asciidoc/getting-started/introduction.adoc +++ b/src/main/asciidoc/getting-started/introduction.adoc @@ -129,7 +129,7 @@ for delegated administration. *_Secure Remote Access_* or *_SRA_* is a security-enabled API gateway with HTTP reverse proxying capabilities. *_Core_* is the component providing IdM services and acting as central repository for other components' configuration. + -It exposes a fully-compliant https://en.wikipedia.org/wiki/Jakarta_RESTful_Web_Services[Jakarta RESTful Web Services 3.1^] +It exposes a fully-compliant https://jakarta.ee/specifications/restful-ws/4.0/[Jakarta RESTful Web Services 4.0^] https://en.wikipedia.org/wiki/Representational_state_transfer[RESTful^] interface which enables third-party applications, written in any programming language, to consume IdM services. @@ -144,7 +144,7 @@ all-Java implementation can be extended for this purpose. from a provided list - including one based on https://www.flowable.org/[Flowable^], the reference open source http://www.bpmn.org/[BPMN 2.0^] implementations - or define new, custom ones. * *_Persistence_* manages all data (users, groups, attributes, resources, ...) at a high level -using a standard https://en.wikipedia.org/wiki/Jakarta_Persistence[Jakarta Persistence 3.2] approach. The data is persisted to an underlying +using a standard https://jakarta.ee/specifications/persistence/3.2/[Jakarta Persistence 3.2] approach. The data is persisted to an underlying database, referred to as *_Internal Storage_*. Consistency is ensured via the comprehensive https://docs.spring.io/spring-framework/reference/7.0/data-access/transaction.html[transaction management^] provided by the Spring Framework. + diff --git a/src/main/asciidoc/getting-started/obtain.adoc b/src/main/asciidoc/getting-started/obtain.adoc index 4f742f0c8a9..6c812451ca5 100644 --- a/src/main/asciidoc/getting-started/obtain.adoc +++ b/src/main/asciidoc/getting-started/obtain.adoc @@ -53,7 +53,7 @@ $ ./bin/startup.sh ---- [TIP] -Please refer to the https://tomcat.apache.org/tomcat-10.0-doc/[Apache Tomcat documentation^] for more advanced setup and +Please refer to the https://tomcat.apache.org/tomcat-11.0-doc/[Apache Tomcat documentation^] for more advanced setup and instructions. [[standalone-components]] @@ -102,10 +102,10 @@ Environment variables: * `DB_PASSWORD`: password for internal storage authentication * `DB_POOL_MAX`: internal storage connection pool: ceiling * `DB_POOL_MIN`: internal storage connection pool: floor -* `OPENJPA_REMOTE_COMMIT`: configure multiple instances, with high availability; valid values are the ones accepted by -OpenJPA for -https://openjpa.apache.org/builds/4.0.1/apache-openjpa/docs/ref_guide_event.html[remote event notification^] including -`sjvm` (single instance) +* `HIBERNATE_JCACHE_PROVIDER`: configure multiple instances, with high availability; valid values are the ones accepted by +Hibernate for +https://docs.hibernate.org/orm/7.2/userguide/html_single/#caching-provider-jcache-cache-manager[JCache CacheManager^] +including the default value `com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider` (single instance) ===== Console @@ -180,7 +180,7 @@ services: DB_PASSWORD: syncope DB_POOL_MAX: 20 DB_POOL_MIN: 5 - OPENJPA_REMOTE_COMMIT: sjvm + HIBERNATE_JCACHE_PROVIDER: com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster KEYMASTER_USERNAME: ${ANONYMOUS_USER} KEYMASTER_PASSWORD: ${ANONYMOUS_KEY} @@ -265,7 +265,7 @@ services: DB_PASSWORD: syncope DB_POOL_MAX: 20 DB_POOL_MIN: 5 - OPENJPA_REMOTE_COMMIT: sjvm + HIBERNATE_JCACHE_PROVIDER: com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider KEYMASTER_ADDRESS: keymaster:2181 KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-} KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-} @@ -666,7 +666,7 @@ You can configure any LDAP client (such as http://jxplorer.org/[JXplorer^], for Click 'Connect' button | External resource: Apache Kafka -| Broker listening at localhost:19092 +| Broker connection information available at https://localhost:9443/syncope-fit-build-tools/cxf/rest/kafka/brokers |=== diff --git a/src/main/asciidoc/getting-started/systemRequirements.adoc b/src/main/asciidoc/getting-started/systemRequirements.adoc index 1e751db19d5..fe64fcac675 100644 --- a/src/main/asciidoc/getting-started/systemRequirements.adoc +++ b/src/main/asciidoc/getting-started/systemRequirements.adoc @@ -30,15 +30,15 @@ managed entities (Users, Groups and Any Objects), their attributes and resources === Java -Apache Syncope {docVersion} requires the latest JDK 21 that is available. Works with later versions. +Apache Syncope {docVersion} requires the latest JDK 25 that is available. Works with later versions. === Jakarta EE Container Apache Syncope {docVersion} is verified with the following Jakarta EE containers: - . https://tomcat.apache.org/download-10.cgi[Apache Tomcat 10^] - . https://www.payara.fish/[Payara Server 6^] - . https://www.wildfly.org/[Wildfly 38^] + . https://tomcat.apache.org/download-11.cgi[Apache Tomcat 11^] + . https://www.payara.fish/[Payara Server 7^] + . https://www.wildfly.org/[Wildfly Preview 39^] === Internal Storage diff --git a/src/main/asciidoc/reference-guide/architecture/core.adoc b/src/main/asciidoc/reference-guide/architecture/core.adoc index e3b794bdfff..861f26d433f 100644 --- a/src/main/asciidoc/reference-guide/architecture/core.adoc +++ b/src/main/asciidoc/reference-guide/architecture/core.adoc @@ -33,7 +33,7 @@ The rich pre-defined set of endpoints can be <> by adding n given Apache Syncope deployment to complement the native features with domain-specific operations. At a technical level, the RESTful interface is a fully-compliant -https://en.wikipedia.org/wiki/Jakarta_RESTful_Web_Services[Jakarta RESTful Web Services 4.0^] implementation based on +https://jakarta.ee/specifications/restful-ws/4.0/[Jakarta RESTful Web Services 4.0^] implementation based on http://cxf.apache.org[Apache CXF^], natively dealing with JSON payloads. More details are available in the dedicated <> section. @@ -75,12 +75,13 @@ management and new statuses definitions; a web-based GUI editor to model workflo image::userWorkflow.png[title="Default Flowable user workflow",alt="Default Flowable user workflow"] Besides Flowable, new workflow engines - possibly integrating with third-party tools as -https://camunda.org/[Camunda^] or http://jbpm.jboss.org/[jBPM^], can be written and plugged into specific deployments. +https://camunda.org/[Camunda^] or https://kie.apache.org/[Apache KIE^], can be written and plugged into specific +deployments. ==== Persistence All data (users, groups, attributes, resources, ...) is internally managed at a high level using a standard -https://en.wikipedia.org/wiki/Jakarta_Persistence[Jakarta Persistence 3.1] approach based on +using a standard https://jakarta.ee/specifications/persistence/3.2/[Jakarta Persistence 3.2] approach based on https://hibernate.org/orm/[Hibernate ORM^]. The data is persisted into an underlying database, referred to as *_Internal Storage_*. Consistency is ensured via the comprehensive diff --git a/src/main/asciidoc/reference-guide/concepts/entitlements.adoc b/src/main/asciidoc/reference-guide/concepts/entitlements.adoc index 88f835e63e8..d65caa78062 100644 --- a/src/main/asciidoc/reference-guide/concepts/entitlements.adoc +++ b/src/main/asciidoc/reference-guide/concepts/entitlements.adoc @@ -30,7 +30,7 @@ ifeval::["{snapshotOrRelease}" == "snapshot"] https://github.com/apache/syncope/blob/master/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java[RealmLogic^] endif::[] , the -https://docs.spring.io/spring-security/reference/6.5/servlet/authorization/method-security.html#authorization-expressions[`hasRole` expression^] +https://docs.spring.io/spring-security/reference/7.0/servlet/authorization/method-security.html#authorization-expressions[`hasRole` expression^] is used together with one of the standard entitlements to restrict access only to Users owning the `REALM_SEARCH` entitlement. diff --git a/src/main/asciidoc/reference-guide/concepts/routes.adoc b/src/main/asciidoc/reference-guide/concepts/routes.adoc index 22859312ce6..c9da3de1264 100644 --- a/src/main/asciidoc/reference-guide/concepts/routes.adoc +++ b/src/main/asciidoc/reference-guide/concepts/routes.adoc @@ -50,7 +50,7 @@ The received response, after being post-processed by matching route's _filters_, ==== Predicates Inside Route definition, each predicate will be referring to some Spring Cloud Gateway's -https://docs.spring.io/spring-cloud-gateway/reference/4.3/spring-cloud-gateway-server-webflux/request-predicates-factories.html[Predicate factory^]: +https://docs.spring.io/spring-cloud-gateway/reference/5.0/spring-cloud-gateway-server-webflux/request-predicates-factories.html[Predicate factory^]: * `AFTER` matches requests that happen after the specified datetime; * `BEFORE` matches requests that happen before the specified datetime; @@ -74,7 +74,7 @@ endif::[] ==== Filters Inside Route definition, each filter will be referring to some Spring Cloud Gateway's -https://docs.spring.io/spring-cloud-gateway/reference/4.3/spring-cloud-gateway-server-webflux/gatewayfilter-factories.html[Filter factory^]: +https://docs.spring.io/spring-cloud-gateway/reference/5.0/spring-cloud-gateway-server-webflux/gatewayfilter-factories.html[Filter factory^]: * `ADD_REQUEST_HEADER` adds a header to the downstream request's headers; * `ADD_REQUEST_PARAMETER` adds a parameter too the downstream request's query string; diff --git a/src/main/asciidoc/reference-guide/configuration/dbms.adoc b/src/main/asciidoc/reference-guide/configuration/dbms.adoc index f9a1ad7c002..123c87c0f4a 100644 --- a/src/main/asciidoc/reference-guide/configuration/dbms.adoc +++ b/src/main/asciidoc/reference-guide/configuration/dbms.adoc @@ -32,7 +32,7 @@ persistence.domain[0].jdbcDriver=org.postgresql.Driver persistence.domain[0].jdbcURL=jdbc:postgresql://localhost:5432/syncope?stringtype=unspecified persistence.domain[0].dbUsername=syncope persistence.domain[0].dbPassword=syncope -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary +persistence.domain[0].databasePlatform=org.apache.syncope.core.persistence.jpa.hibernate.SyncopePostgreSQLDialect persistence.domain[0].poolMaxActive=20 persistence.domain[0].poolMinIdle=5 .... @@ -40,7 +40,7 @@ persistence.domain[0].poolMinIdle=5 as `core/src/main/resources/core-postgresql.properties`. Do not forget to include `postgresql` as -https://docs.spring.io/spring-boot/3.5/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] +https://docs.spring.io/spring-boot/4.0/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] for the Core application. [WARNING] @@ -64,7 +64,7 @@ persistence.domain[0].jdbcDriver=com.mysql.cj.jdbc.Driver persistence.domain[0].jdbcURL=jdbc:mysql://localhost:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8 persistence.domain[0].dbUsername=syncope persistence.domain[0].dbPassword=syncope -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3,useSetStringForClobs=true) +persistence.domain[0].databasePlatform=org.hibernate.dialect.MySQLDialect persistence.domain[0].orm=META-INF/mysql/spring-orm.xml persistence.domain[0].poolMaxActive=20 persistence.domain[0].poolMinIdle=5 @@ -73,7 +73,7 @@ persistence.domain[0].poolMinIdle=5 as `core/src/main/resources/core-mysql.properties`. Do not forget to include `mysql` as -https://docs.spring.io/spring-boot/3.5/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] +https://docs.spring.io/spring-boot/4.0/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] for the Core application. [CAUTION] @@ -100,7 +100,7 @@ persistence.domain[0].jdbcDriver=org.mariadb.jdbc.Driver persistence.domain[0].jdbcURL=jdbc:mariadb://localhost:3306/syncope?characterEncoding=UTF-8 persistence.domain[0].dbUsername=syncope persistence.domain[0].dbPassword=syncope -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3,useSetStringForClobs=true) +persistence.domain[0].databasePlatform=org.hibernate.dialect.MariaDBDialect persistence.domain[0].orm=META-INF/mariadb/spring-orm.xml persistence.domain[0].poolMaxActive=20 persistence.domain[0].poolMinIdle=5 @@ -109,7 +109,7 @@ persistence.domain[0].poolMinIdle=5 as `core/src/main/resources/core-mariadb.properties`. Do not forget to include `mariadb` as -https://docs.spring.io/spring-boot/3.5/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] +https://docs.spring.io/spring-boot/4.0/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] for the Core application. [CAUTION] @@ -148,7 +148,7 @@ persistence.domain[0].jdbcURL=jdbc:oracle:thin:@localhost}:1521/FREEPDB1 persistence.domain[0].dbSchema=SYNCOPE persistence.domain[0].dbUsername=syncope persistence.domain[0].dbPassword=syncope -persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.OracleDictionary +persistence.domain[0].databasePlatform=org.hibernate.dialect.OracleDialect persistence.domain[0].orm=META-INF/oracle/spring-orm.xml persistence.domain[0].poolMaxActive=20 persistence.domain[0].poolMinIdle=5 @@ -157,7 +157,7 @@ persistence.domain[0].poolMinIdle=5 as `core/src/main/resources/core-oracle.properties`. Do not forget to include `oracle` as -https://docs.spring.io/spring-boot/3.5/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] +https://docs.spring.io/spring-boot/4.0/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] for the Core application. [WARNING] diff --git a/src/main/asciidoc/reference-guide/configuration/deployment.adoc b/src/main/asciidoc/reference-guide/configuration/deployment.adoc index e0fbec3248c..520575ae87e 100644 --- a/src/main/asciidoc/reference-guide/configuration/deployment.adoc +++ b/src/main/asciidoc/reference-guide/configuration/deployment.adoc @@ -51,10 +51,10 @@ applications as standalone fat JAR or WAR files. [TIP] Spring Boot applications can also be -https://docs.spring.io/spring-boot/3.5/how-to/deployment/installing.html[installed as system services^]. +https://docs.spring.io/spring-boot/4.0/how-to/deployment/installing.html[installed as system services^]. [TIP] -https://docs.spring.io/spring-boot/3.5/reference/features/spring-application.html#features.spring-application.virtual-threads[Virtual Threads^] can be enabled but JDK >= 24 is required in order to run properly. +https://docs.spring.io/spring-boot/4.0/reference/features/spring-application.html#features.spring-application.virtual-threads[Virtual Threads^] can be enabled but JDK >= 24 is required in order to run properly. .Run Core application as standalone under GNU / Linux ==== @@ -74,7 +74,7 @@ $ java -Dsyncope.conf.dir=/opt/syncope/conf \ -jar /opt/syncope/lib/syncope.war ---- Further options can be passed to last command, according to Spring Boot -https://docs.spring.io/spring-boot/3.5/appendix/application-properties/index.html[documentation^]; +https://docs.spring.io/spring-boot/4.0/appendix/application-properties/index.html[documentation^]; for example: * `--spring.config.additional-location=/path` + @@ -104,12 +104,12 @@ for the Master domain. . <>-managed, via the JNDI resource matching the name specified for a given <>, e.g. `java:comp/env/jdbc/syncopeMasterDataSource` for the `Master` domain. + Each JavaEE Container provides its own way to accomplish this task: - * https://tomcat.apache.org/tomcat-10.0-doc/jdbc-pool.html[Apache Tomcat 10^] - * https://docs.payara.fish/community/docs/Technical%20Documentation/Payara%20Server%20Documentation/Server%20Configuration%20And%20Management/JDBC%20Resource%20Management/JDBC.html[Payara Server 6^] - * https://docs.wildfly.org/38/Admin_Guide.html#DataSource[Wildfly 38^] + * https://tomcat.apache.org/tomcat-11.0-doc/jdbc-pool.html[Apache Tomcat 11^] + * https://docs.payara.fish/community/docs/Technical%20Documentation/Payara%20Server%20Documentation/General%20Administration/Administering%20Database%20Connectivity.html[Payara Server 7^] + * https://docs.wildfly.org/39/Admin_Guide.html#DataSource[Wildfly 39^] **** -==== Apache Tomcat 10 +==== Apache Tomcat 11 On GNU / Linux - Mac OS X, create `$CATALINA_HOME/bin/setenv.sh` with similar content (keep everything on a single line): @@ -133,7 +133,7 @@ set JAVA_OPTS=-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server \ -Xms1536m -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:+DisableExplicitGC .... -==== Payara Server 6 +==== Payara Server 7 Add @@ -169,11 +169,15 @@ with org.apache.syncope.core syncope-core-starter - - - org.apache.tomcat.embed - tomcat-embed-el - + + + org.springframework.boot + spring-boot-starter-tomcat + + + org.apache.tomcat.embed + tomcat-embed-el + com.github.ben-manes.caffeine jcache @@ -217,7 +221,7 @@ For better performance under GNU / Linux, do not forget to include the system pr .... ==== -==== Wildfly 38 +==== Wildfly 39 Add @@ -264,14 +268,14 @@ with org.apache.syncope.core syncope-core-starter - - org.apache.tomcat.embed - tomcat-embed-el - org.springframework.boot spring-boot-starter-tomcat + + org.apache.tomcat.embed + tomcat-embed-el + .... @@ -280,11 +284,6 @@ in `core/pom.xml` and create [source] .... -persistence.metaDataFactory=jpa(URLs=\ -vfs:/content/${project.build.finalName}.war/WEB-INF/lib/syncope-core-persistence-jpa-${syncope.version}.jar; \ -vfs:/content/${project.build.finalName}.war/WEB-INF/lib/syncope-core-self-keymaster-starter-${syncope.version}.jar, \ -Resources=##orm##) - javadocPaths=/WEB-INF/lib/syncope-common-idrepo-rest-api-${syncope.version}-javadoc.jar,\ /WEB-INF/lib/syncope-common-idm-rest-api-${syncope.version}-javadoc.jar,\ /WEB-INF/lib/syncope-common-am-rest-api-${syncope.version}-javadoc.jar @@ -373,7 +372,7 @@ with in `enduser/pom.xml`. Do not forget to include `widlfly` as -https://docs.spring.io/spring-boot/3.5/reference/features/profiles.html[Spring Boot profile^] +https://docs.spring.io/spring-boot/4.0/reference/features/profiles.html[Spring Boot profile^] for the Core application. [WARNING] diff --git a/src/main/asciidoc/reference-guide/configuration/highavailability.adoc b/src/main/asciidoc/reference-guide/configuration/highavailability.adoc index ac4d7b89a42..6c3a30e3336 100644 --- a/src/main/asciidoc/reference-guide/configuration/highavailability.adoc +++ b/src/main/asciidoc/reference-guide/configuration/highavailability.adoc @@ -18,39 +18,34 @@ // === High-Availability -[discrete] -===== OpenJPA +==== Core HA When deploying multiple Syncope <> instances with a single database or database cluster, it is of -fundamental importance that the contained OpenJPA instances are correctly configured for -https://openjpa.apache.org/builds/4.0.1/apache-openjpa/docs/ref_guide_event.html[remote event notification^]. + -Such configuration, in fact, allows the OpenJPA data cache to remain synchronized when deployed in multiple JVMs, thus +fundamental importance that the https://docs.hibernate.org/orm/7.2/userguide/html_single/#caching[second-level cache^] +is correctly configured for Hibernate ORM. +Such configuration, in fact, allows the Hibernate ORM cache to remain synchronized across multiple JVMs, thus enforcing data consistency across all Syncope Core instances. The default configuration in `core.properties` is [source] .... -persistence.remoteCommitProvider=sjvm +persistence.cacheProvider=com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider .... -which is suited for single JVM installations; with multiple instances, more options like as TCP, JMS or Kubernetes -are available; see the OpenJPA documentation for reference. +which is suited for single JVM installations; with multiple instances, more options are available depending on the +configured https://github.com/jsr107/jsr107spec[JCache^] implementation, such as, for example, +https://docs.hazelcast.com/hazelcast/5.6/jcache/overview[Hazelcast^], +https://www.ehcache.org/documentation/3.0/107.html[Ehcache], +https://infinispan.org/docs/stable/titles/hibernate/hibernate.html[Infinispan^] or +https://ignite.apache.org/[Apache Ignite^]. -[WARNING] -==== -The https://openjpa.apache.org/builds/4.0.1/apache-openjpa/docs/ref_guide_event.html[OpenJPA documentation^]'s XML -snippets refer to a different configuration style; for example, when used in `core.properties`, this: +Additional, implementation-specific configuration files can be set up via `persistence.cacheURI`. -[source,xml] -.... - -.... - -becomes: +==== WA HA -[source] -.... -persistence.remoteCommitProvider=tcp(Addresses=10.0.1.10;10.0.1.11,TransmitPersistedObjectIds=true) -.... -==== +When multiple Syncope <> instances are needed to support authentication and authorization requirements, +a distributed https://apereo.github.io/cas/7.3.x/ticketing/Configuring-Ticketing-Components.html#ticket-registry[Ticket Registry^] +is required, such has the ones based on +https://apereo.github.io/cas/7.3.x/ticketing/Hazelcast-Ticket-Registry.html[Hazelcast^] +or https://apereo.github.io/cas/7.3.x/ticketing/Ignite-Ticket-Registry.html[Apache Ignite^]. diff --git a/src/main/asciidoc/reference-guide/usage/actuator.adoc b/src/main/asciidoc/reference-guide/usage/actuator.adoc index de2414042c1..fb55087fe13 100644 --- a/src/main/asciidoc/reference-guide/usage/actuator.adoc +++ b/src/main/asciidoc/reference-guide/usage/actuator.adoc @@ -23,7 +23,7 @@ Spring Boot's actuator endpoints let you monitor and interact with Syncope compo Each individual endpoint can be enabled / disabled and exposed over HTTP (pre-defined, under the `/actuator` subcontext) or JMX. -Besides a number of https://docs.spring.io/spring-boot/3.5/reference/actuator/endpoints.html[built-in endpoints^], +Besides a number of https://docs.spring.io/spring-boot/4.0/reference/actuator/endpoints.html[built-in endpoints^], more are made available for each component, as reported below. [NOTE] @@ -40,7 +40,7 @@ The pre-defined `health` endpoint is typically used for liveness and readiness p |=== | `entityCache` -a| Allows to work with https://openjpa.apache.org/builds/4.0.1/apache-openjpa/docs/ref_guide_caching.html#ref_guide_cache_statistics[JPA cache statistics^] +a| Allows to work with https://docs.hibernate.org/orm/7.2/userguide/html_single/#caching-statistics[JPA cache statistics^] * `GET` - shows JPA cache statistics * `POST {ENABLE,DISABLE,RESET}` - performs the requested operation onto JPA cache @@ -85,6 +85,6 @@ a| * `DELETE {id}` - removes the session with given `id` | `gateway` -| https://docs.spring.io/spring-cloud-gateway/reference/4.3/spring-cloud-gateway-server-webflux/actuator-api.html[More details^] +| https://docs.spring.io/spring-cloud-gateway/reference/5.0/spring-cloud-gateway-server-webflux/actuator-api.html[More details^] |=== diff --git a/src/main/asciidoc/reference-guide/usage/core.adoc b/src/main/asciidoc/reference-guide/usage/core.adoc index fb3b3a944a2..cc32aa7f6cc 100644 --- a/src/main/asciidoc/reference-guide/usage/core.adoc +++ b/src/main/asciidoc/reference-guide/usage/core.adoc @@ -98,7 +98,7 @@ The set of RESTful services provided by Apache Syncope can be divided as: . endpoints disclosing information about the given Syncope deployment (available <>, configured <>, Groups, ...), requiring some sort of shared authentication defined by the `anonymousKey` value in the `security.properties` file - for more information, read about Spring Security's -https://docs.spring.io/spring-security/reference/6.5/servlet/authentication/anonymous.html#page-title[Anonymous Authentication^]; +https://docs.spring.io/spring-security/reference/7.0/servlet/authentication/anonymous.html#page-title[Anonymous Authentication^]; . endpoints for self-service (self-update, password change, ...), requiring user authentication and no entitlements; . endpoints for administrative operations, requiring user authentication with authorization granted by the related <>, handed over to users via <>. diff --git a/src/main/asciidoc/reference-guide/usage/customization.adoc b/src/main/asciidoc/reference-guide/usage/customization.adoc index 43dcb17b3cd..fd4361f62e2 100644 --- a/src/main/asciidoc/reference-guide/usage/customization.adoc +++ b/src/main/asciidoc/reference-guide/usage/customization.adoc @@ -155,7 +155,7 @@ endif::[] ===== Extending configuration Apache Syncope <> are built on https://spring.io/projects/spring-boot[Spring Boot^], hence designing and extending Syncope configuration very much comes down to -https://docs.spring.io/spring-boot/3.5/index.html[their guide^], some aspects of which are briefly +https://docs.spring.io/spring-boot/4.0/index.html[their guide^], some aspects of which are briefly highlighted here. To design your own configuration class, take inspiration from the following sample: @@ -236,7 +236,7 @@ $ mkdir /opt/syncope/conf [TIP] ==== The `conf` directory must be configured for deployment, following Spring Boot's -https://docs.spring.io/spring-boot/3.5/reference/features/external-config.html[Externalized Configuration^] +https://docs.spring.io/spring-boot/4.0/reference/features/external-config.html[Externalized Configuration^] settings; with above reference: * <>: `--spring.config.additional-location=/opt/syncope/conf/` @@ -273,10 +273,10 @@ components: * <> [discrete] -===== Customize OpenJPA settings +===== Customize Hibernate ORM settings -Apache OpenJPA is at the core of the <> layer; its configuration can be tweaked under several -aspects - including https://openjpa.apache.org/builds/4.0.1/apache-openjpa/docs/ref_guide_caching.html[caching^] for +Hibernate ORM is at the core of the <> layer; its configuration can be tweaked under several +aspects - including https://docs.hibernate.org/orm/7.2/userguide/html_single/#caching[caching^] for example, to best suit the various environments. The main configuration classes are: @@ -383,7 +383,7 @@ elasticsearch.numberOfReplicas=1 as `core/src/main/resources/core-elasticsearch.properties`. Do not forget to include `elasticsearch` as -https://docs.spring.io/spring-boot/3.5/reference/features/profiles.html[Spring Boot profile^] +https://docs.spring.io/spring-boot/4.0/reference/features/profiles.html[Spring Boot profile^] for the Core application. If needed, customize the `@Bean` declarations from @@ -436,7 +436,7 @@ opensearch.numberOfReplicas=1 as `core/src/main/resources/core-opensearch.properties`. Do not forget to include `opensearch` as -https://docs.spring.io/spring-boot/3.5/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] +https://docs.spring.io/spring-boot/4.0/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] for the Core application. If needed, customize the `@Bean` declarations from @@ -508,7 +508,7 @@ endif::[] . Do not forget to include `openfga` as -https://docs.spring.io/spring-boot/3.5/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] +https://docs.spring.io/spring-boot/4.0/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] for the Core application. If needed, customize the `@Bean` declarations from diff --git a/src/main/asciidoc/reference-guide/usage/loggers.adoc b/src/main/asciidoc/reference-guide/usage/loggers.adoc index 60ca82e74c2..6ccbfe300ba 100644 --- a/src/main/asciidoc/reference-guide/usage/loggers.adoc +++ b/src/main/asciidoc/reference-guide/usage/loggers.adoc @@ -19,7 +19,7 @@ === Loggers Spring Boot actuator includes the ability to -https://docs.spring.io/spring-boot/3.5/reference/actuator/loggers.html[view and configure the log levels^] of all +https://docs.spring.io/spring-boot/4.0/reference/actuator/loggers.html[view and configure the log levels^] of all Syncope modules at runtime. In addition, Console provides a UI to view the list of logger’s configuration and set their level, for all diff --git a/src/main/asciidoc/reference-guide/usage/metrics.adoc b/src/main/asciidoc/reference-guide/usage/metrics.adoc index 984a410c834..01baccff3da 100644 --- a/src/main/asciidoc/reference-guide/usage/metrics.adoc +++ b/src/main/asciidoc/reference-guide/usage/metrics.adoc @@ -19,7 +19,7 @@ === Metrics Monitoring performance to ensure reliability and efficiency can be achieved by leveraging -https://docs.spring.io/spring-boot/3.5/reference/actuator/metrics.html[Spring Boot's metrics^]. +https://docs.spring.io/spring-boot/4.0/reference/actuator/metrics.html[Spring Boot's metrics^]. [[metrics-core]] ==== Core @@ -36,7 +36,7 @@ This can be enabled by adding the following dependency to `core/pom.xml`: ---- Additional dependencies might be required, depending on the -https://docs.spring.io/spring-boot/3.5/reference/actuator/metrics.html#actuator.metrics.export[actual monitoring system in use^]. +https://docs.spring.io/spring-boot/4.0/reference/actuator/metrics.html#actuator.metrics.export[actual monitoring system in use^]. [[metrics-wa]] ==== WA diff --git a/standalone/LICENSE b/standalone/LICENSE index 32c6fe65f9b..218256ac0ec 100644 --- a/standalone/LICENSE +++ b/standalone/LICENSE @@ -252,12 +252,216 @@ This is licensed under the MIT license, see above. == -For Chart.js (http://www.chartjs.org/): -This is licensed under the AL 2.0, see above. +For Logback (https://logback.qos.ch/): +This is licensed under the EPL 1.0: + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM +CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation + distributed under this Agreement, and +b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + + where such changes and/or additions to the Program originate from and are + distributed by that particular Contributor. A Contribution 'originates' + from a Contributor if it was added to the Program by such Contributor + itself or anyone acting on such Contributor's behalf. Contributions do not + include additions to the Program which: (i) are separate modules of + software distributed in conjunction with the Program under their own + license agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are +necessarily infringed by the use or sale of its Contribution alone or when +combined with the Program. + +"Program" means the Contributions distributed in accordance with this +Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + +2. GRANT OF RIGHTS + a) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free copyright license to + reproduce, prepare derivative works of, publicly display, publicly + perform, distribute and sublicense the Contribution of such Contributor, + if any, and such derivative works, in source code and object code form. + b) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free patent license under + Licensed Patents to make, use, sell, offer to sell, import and otherwise + transfer the Contribution of such Contributor, if any, in source code and + object code form. This patent license shall apply to the combination of + the Contribution and the Program if, at the time the Contribution is + added by the Contributor, such addition of the Contribution causes such + combination to be covered by the Licensed Patents. The patent license + shall not apply to any other combinations which include the Contribution. + No hardware per se is licensed hereunder. + c) Recipient understands that although each Contributor grants the licenses + to its Contributions set forth herein, no assurances are provided by any + Contributor that the Program does not infringe the patent or other + intellectual property rights of any other entity. Each Contributor + disclaims any liability to Recipient for claims brought by any other + entity based on infringement of intellectual property rights or + otherwise. As a condition to exercising the rights and licenses granted + hereunder, each Recipient hereby assumes sole responsibility to secure + any other intellectual property rights needed, if any. For example, if a + third party patent license is required to allow Recipient to distribute + the Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + d) Each Contributor represents that to its knowledge it has sufficient + copyright rights in its Contribution, if any, to grant the copyright + license set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under +its own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + b) its license agreement: + i) effectively disclaims on behalf of all Contributors all warranties + and conditions, express and implied, including warranties or + conditions of title and non-infringement, and implied warranties or + conditions of merchantability and fitness for a particular purpose; + ii) effectively excludes on behalf of all Contributors all liability for + damages, including direct, indirect, special, incidental and + consequential damages, such as lost profits; + iii) states that any provisions which differ from this Agreement are + offered by that Contributor alone and not by any other party; and + iv) states that source code for the Program is available from such + Contributor, and informs licensees how to obtain it in a reasonable + manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + b) a copy of this Agreement must be included with each copy of the Program. + Contributors may not remove or alter any copyright notices contained + within the Program. + +Each Contributor must identify itself as the originator of its Contribution, +if +any, in a manner that reasonably allows subsequent Recipients to identify the +originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with +respect to end users, business partners and the like. While this license is +intended to facilitate the commercial use of the Program, the Contributor who +includes the Program in a commercial product offering should do so in a manner +which does not create potential liability for other Contributors. Therefore, +if a Contributor includes the Program in a commercial product offering, such +Contributor ("Commercial Contributor") hereby agrees to defend and indemnify +every other Contributor ("Indemnified Contributor") against any losses, +damages and costs (collectively "Losses") arising from claims, lawsuits and +other legal actions brought by a third party against the Indemnified +Contributor to the extent caused by the acts or omissions of such Commercial +Contributor in connection with its distribution of the Program in a commercial +product offering. The obligations in this section do not apply to any claims +or Losses relating to any actual or alleged intellectual property +infringement. In order to qualify, an Indemnified Contributor must: +a) promptly notify the Commercial Contributor in writing of such claim, and +b) allow the Commercial Contributor to control, and cooperate with the +Commercial Contributor in, the defense and any related settlement +negotiations. The Indemnified Contributor may participate in any such claim at +its own expense. + +For example, a Contributor might include the Program in a commercial product +offering, Product X. That Contributor is then a Commercial Contributor. If +that Commercial Contributor then makes performance claims, or offers +warranties related to Product X, those performance claims and warranties are +such Commercial Contributor's responsibility alone. Under this section, the +Commercial Contributor would have to defend claims against the other +Contributors related to those performance claims and warranties, and if a +court requires any other Contributor to pay any damages as a result, the +Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each +Recipient is solely responsible for determining the appropriateness of using +and distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement , including but not limited to the +risks and costs of program errors, compliance with applicable laws, damage to +or loss of data, programs or equipment, and unavailability or interruption of +operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY +CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION +LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE +EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of the +remainder of the terms of this Agreement, and without further action by the +parties hereto, such provision shall be reformed to the minimum extent +necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Program itself +(excluding combinations of the Program with other software or hardware) +infringes such Recipient's patent(s), then such Recipient's rights granted +under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to +comply with any of the material terms or conditions of this Agreement and does +not cure such failure in a reasonable period of time after becoming aware of +such noncompliance. If all Recipient's rights under this Agreement terminate, +Recipient agrees to cease use and distribution of the Program as soon as +reasonably practicable. However, Recipient's obligations under this Agreement +and any licenses granted by Recipient relating to the Program shall continue +and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in +order to avoid inconsistency the Agreement is copyrighted and may only be +modified in the following manner. The Agreement Steward reserves the right to +publish new versions (including revisions) of this Agreement from time to +time. No one other than the Agreement Steward has the right to modify this +Agreement. The Eclipse Foundation is the initial Agreement Steward. The +Eclipse Foundation may assign the responsibility to serve as the Agreement +Steward to a suitable separate entity. Each new version of the Agreement will +be given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new version of the +Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly +stated in Sections 2(a) and 2(b) above, Recipient receives no rights or +licenses to the intellectual property of any Contributor under this Agreement, +whether expressly, by implication, estoppel or otherwise. All rights in the +Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to this +Agreement will bring a legal action under this Agreement more than one year +after the cause of action arose. Each party waives its rights to a jury trial in +any resulting litigation. == -For ClientJS (https://github.com/jackspirou/clientjs): +For Chart.js (http://www.chartjs.org/): This is licensed under the AL 2.0, see above. == @@ -312,6 +516,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. == +For hash4j (https://github.com/dynatrace-oss/hash4j): +This is licensed under the AL 2.0, see above. + +== + For Kryo (https://github.com/EsotericSoftware/kryo): This is licensed under the BSD license, see above. @@ -557,6 +766,11 @@ This is licensed under the BSD license, see above. == +For RE2/J (https://github.com/google/re2j): +This is licensed under the BSD license, see above. + +== + For ZXing (https://github.com/zxing/zxing): This is licensed under the AL 2.0, see above. @@ -578,211 +792,7 @@ This is licensed under the AL 2.0, see above. == For H2 (http://www.h2database.com/): -This is licensed under the EPL 1.0: - -Eclipse Public License - v 1.0 - -THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC -LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM -CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. - -1. DEFINITIONS - -"Contribution" means: - -a) in the case of the initial Contributor, the initial code and documentation - distributed under this Agreement, and -b) in the case of each subsequent Contributor: - i) changes to the Program, and - ii) additions to the Program; - - where such changes and/or additions to the Program originate from and are - distributed by that particular Contributor. A Contribution 'originates' - from a Contributor if it was added to the Program by such Contributor - itself or anyone acting on such Contributor's behalf. Contributions do not - include additions to the Program which: (i) are separate modules of - software distributed in conjunction with the Program under their own - license agreement, and (ii) are not derivative works of the Program. - -"Contributor" means any person or entity that distributes the Program. - -"Licensed Patents" mean patent claims licensable by a Contributor which are -necessarily infringed by the use or sale of its Contribution alone or when -combined with the Program. - -"Program" means the Contributions distributed in accordance with this -Agreement. - -"Recipient" means anyone who receives the Program under this Agreement, -including all Contributors. - -2. GRANT OF RIGHTS - a) Subject to the terms of this Agreement, each Contributor hereby grants - Recipient a non-exclusive, worldwide, royalty-free copyright license to - reproduce, prepare derivative works of, publicly display, publicly - perform, distribute and sublicense the Contribution of such Contributor, - if any, and such derivative works, in source code and object code form. - b) Subject to the terms of this Agreement, each Contributor hereby grants - Recipient a non-exclusive, worldwide, royalty-free patent license under - Licensed Patents to make, use, sell, offer to sell, import and otherwise - transfer the Contribution of such Contributor, if any, in source code and - object code form. This patent license shall apply to the combination of - the Contribution and the Program if, at the time the Contribution is - added by the Contributor, such addition of the Contribution causes such - combination to be covered by the Licensed Patents. The patent license - shall not apply to any other combinations which include the Contribution. - No hardware per se is licensed hereunder. - c) Recipient understands that although each Contributor grants the licenses - to its Contributions set forth herein, no assurances are provided by any - Contributor that the Program does not infringe the patent or other - intellectual property rights of any other entity. Each Contributor - disclaims any liability to Recipient for claims brought by any other - entity based on infringement of intellectual property rights or - otherwise. As a condition to exercising the rights and licenses granted - hereunder, each Recipient hereby assumes sole responsibility to secure - any other intellectual property rights needed, if any. For example, if a - third party patent license is required to allow Recipient to distribute - the Program, it is Recipient's responsibility to acquire that license - before distributing the Program. - d) Each Contributor represents that to its knowledge it has sufficient - copyright rights in its Contribution, if any, to grant the copyright - license set forth in this Agreement. - -3. REQUIREMENTS - -A Contributor may choose to distribute the Program in object code form under -its own license agreement, provided that: - - a) it complies with the terms and conditions of this Agreement; and - b) its license agreement: - i) effectively disclaims on behalf of all Contributors all warranties - and conditions, express and implied, including warranties or - conditions of title and non-infringement, and implied warranties or - conditions of merchantability and fitness for a particular purpose; - ii) effectively excludes on behalf of all Contributors all liability for - damages, including direct, indirect, special, incidental and - consequential damages, such as lost profits; - iii) states that any provisions which differ from this Agreement are - offered by that Contributor alone and not by any other party; and - iv) states that source code for the Program is available from such - Contributor, and informs licensees how to obtain it in a reasonable - manner on or through a medium customarily used for software exchange. - -When the Program is made available in source code form: - - a) it must be made available under this Agreement; and - b) a copy of this Agreement must be included with each copy of the Program. - Contributors may not remove or alter any copyright notices contained - within the Program. - -Each Contributor must identify itself as the originator of its Contribution, -if -any, in a manner that reasonably allows subsequent Recipients to identify the -originator of the Contribution. - -4. COMMERCIAL DISTRIBUTION - -Commercial distributors of software may accept certain responsibilities with -respect to end users, business partners and the like. While this license is -intended to facilitate the commercial use of the Program, the Contributor who -includes the Program in a commercial product offering should do so in a manner -which does not create potential liability for other Contributors. Therefore, -if a Contributor includes the Program in a commercial product offering, such -Contributor ("Commercial Contributor") hereby agrees to defend and indemnify -every other Contributor ("Indemnified Contributor") against any losses, -damages and costs (collectively "Losses") arising from claims, lawsuits and -other legal actions brought by a third party against the Indemnified -Contributor to the extent caused by the acts or omissions of such Commercial -Contributor in connection with its distribution of the Program in a commercial -product offering. The obligations in this section do not apply to any claims -or Losses relating to any actual or alleged intellectual property -infringement. In order to qualify, an Indemnified Contributor must: -a) promptly notify the Commercial Contributor in writing of such claim, and -b) allow the Commercial Contributor to control, and cooperate with the -Commercial Contributor in, the defense and any related settlement -negotiations. The Indemnified Contributor may participate in any such claim at -its own expense. - -For example, a Contributor might include the Program in a commercial product -offering, Product X. That Contributor is then a Commercial Contributor. If -that Commercial Contributor then makes performance claims, or offers -warranties related to Product X, those performance claims and warranties are -such Commercial Contributor's responsibility alone. Under this section, the -Commercial Contributor would have to defend claims against the other -Contributors related to those performance claims and warranties, and if a -court requires any other Contributor to pay any damages as a result, the -Commercial Contributor must pay those damages. - -5. NO WARRANTY - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR -IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, -NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each -Recipient is solely responsible for determining the appropriateness of using -and distributing the Program and assumes all risks associated with its -exercise of rights under this Agreement , including but not limited to the -risks and costs of program errors, compliance with applicable laws, damage to -or loss of data, programs or equipment, and unavailability or interruption of -operations. - -6. DISCLAIMER OF LIABILITY - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY -CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION -LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE -EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY -OF SUCH DAMAGES. - -7. GENERAL - -If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of the -remainder of the terms of this Agreement, and without further action by the -parties hereto, such provision shall be reformed to the minimum extent -necessary to make such provision valid and enforceable. - -If Recipient institutes patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Program itself -(excluding combinations of the Program with other software or hardware) -infringes such Recipient's patent(s), then such Recipient's rights granted -under Section 2(b) shall terminate as of the date such litigation is filed. - -All Recipient's rights under this Agreement shall terminate if it fails to -comply with any of the material terms or conditions of this Agreement and does -not cure such failure in a reasonable period of time after becoming aware of -such noncompliance. If all Recipient's rights under this Agreement terminate, -Recipient agrees to cease use and distribution of the Program as soon as -reasonably practicable. However, Recipient's obligations under this Agreement -and any licenses granted by Recipient relating to the Program shall continue -and survive. - -Everyone is permitted to copy and distribute copies of this Agreement, but in -order to avoid inconsistency the Agreement is copyrighted and may only be -modified in the following manner. The Agreement Steward reserves the right to -publish new versions (including revisions) of this Agreement from time to -time. No one other than the Agreement Steward has the right to modify this -Agreement. The Eclipse Foundation is the initial Agreement Steward. The -Eclipse Foundation may assign the responsibility to serve as the Agreement -Steward to a suitable separate entity. Each new version of the Agreement will -be given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new version of the -Agreement is published, Contributor may elect to distribute the Program -(including its Contributions) under the new version. Except as expressly -stated in Sections 2(a) and 2(b) above, Recipient receives no rights or -licenses to the intellectual property of any Contributor under this Agreement, -whether expressly, by implication, estoppel or otherwise. All rights in the -Program not expressly granted under this Agreement are reserved. - -This Agreement is governed by the laws of the State of New York and the -intellectual property laws of the United States of America. No party to this -Agreement will bring a legal action under this Agreement more than one year -after the cause of action arose. Each party waives its rights to a jury trial in -any resulting litigation. +This is licensed under the EPL 1.0, see above. == @@ -1282,16 +1292,6 @@ This is licensed under the BSD license, see above. == -For t-digest (https://github.com/tdunning/t-digest): -This is licensed under the AL 2.0, see above. - -== - -For Method Parameter Name Access for Java (https://github.com/paul-hammant/paranamer): -This is licensed under the BSD license, see above. - -== - For Scala Logging (https://github.com/lightbend-labs/scala-logging): This is licensed under the AL 2.0, see above. @@ -1322,11 +1322,6 @@ This is licensed under the BSD license, see above. == -For Wavefront's Java SDK (https://github.com/wavefrontHQ/wavefront-sdk-java): -This is licensed under the AL 2.0, see above. - -== - For Yammer Metrics (https://github.com/StoyanTc/yammer-metrics): This is licensed under the AL 2.0, see above. @@ -1352,16 +1347,6 @@ This is licensed under the AL 2.0, see above. == -For define-data-property (https://github.com/ljharb/define-data-property): -This is licensed under the MIT license, see above. - -== - -For define-properties (https://github.com/ljharb/define-properties): -This is licensed under the MIT license, see above. - -== - For Jakarta Activation (https://eclipse-ee4j.github.io/jaf/): This is licensed under the EDL 1.0, see above. @@ -1382,61 +1367,31 @@ This is licensed under the MIT license, see above. == -For es-define-property (https://github.com/ljharb/es-define-property/): +For FingerprintJS (https://github.com/fingerprintjs/fingerprintjs): This is licensed under the MIT license, see above. == -For MIT: -This is licensed under the AL 2.0, see above. - -== - For Font Awesome (http://fontawesome.io/): This is licensed under the MIT license, see above. == -For globalThis (https://github.com/es-shims/globalThis): -This is licensed under the MIT license, see above. - -== - For google-diff-match-patch (https://code.google.com/archive/p/google-diff-match-patch/): This is licensed under the AL 2.0, see above. == -For gopd (https://github.com/ljharb/gopd): -This is licensed under the AL 2.0, see above. - -== - -For has-property-descriptors (https://github.com/inspect-js/has-property-descriptors): -This is licensed under the MIT license, see above. - -== - For highlight.js (http://highlightjs.org/): This is licensed under the BSD license, see above. == -For Hypersistence Utils (https://github.com/vladmihalcea/hypersistence-utils): -This is licensed under the AL 2.0, see above. - -== - For gmetric4j (https://github.com/ganglia/gmetric4j): This is licensed under the BSD license, see above. == -For inherits (https://github.com/isaacs/inherits): -This is licensed under the MIT license, see above. - -== - For Inputmask (https://github.com/RobinHerbots/Inputmask): This is licensed under the MIT license, see above. @@ -1457,6 +1412,11 @@ This is licensed under the MIT license, see above. == +For Hypersistence Utils (https://github.com/vladmihalcea/hypersistence-utils): +This is licensed under the AL 2.0, see above. + +== + For jsonwebtoken.io (https://jsonwebtoken.io): This is licensed under the AL 2.0, see above. @@ -1487,11 +1447,6 @@ This is licensed under the AL 2.0, see above. == -For Jandex (https://github.com/smallrye/jandex/): -This is licensed under the AL 2.0, see above. - -== - For Swagger (http://swagger.io/): This is licensed under the AL 2.0, see above. @@ -1622,11 +1577,6 @@ This is licensed under the MIT license, see above. == -For murmurhash-js (https://github.com/mikolalysenko/murmurhash-js): -This is licensed under the MIT license, see above. - -== - For Byte Buddy (https://bytebuddy.net/): This is licensed under the AL 2.0, see above. @@ -1682,11 +1632,6 @@ This is licensed under the MIT license, see above. == -For object-keys (https://github.com/ljharb/object-keys): -This is licensed under the MIT license, see above. - -== - For OGNL (https://github.com/jkuhnert/ognl/): This is licensed under the AL 2.0, see above. @@ -1797,7 +1742,7 @@ This is licensed under the CC0 1.0, see above. == -For Hibernate Commons Annotations (https://github.com/hibernate/hibernate-commons-annotations): +For Hibernate models (https://github.com/hibernate/hibernate-models): This is licensed under the AL 2.0, see above. == @@ -1952,6 +1897,11 @@ This is licensed under the MIT license, see above. == +For snakeyaml-engine (https://bitbucket.org/snakeyaml/snakeyaml-engine/): +This is licensed under the AL 2.0, see above. + +== + For Spring openapi documentation (https://springdoc.org/): This is licensed under the AL 2.0, see above. @@ -2047,11 +1997,6 @@ This is licensed under the MIT license, see above. == -For UAParser.js (https://github.com/faisalman/ua-parser-js): -This is licensed under the MIT license, see above. - -== - For webjars-locator-lite (https://github.com/webjars/webjars-locator-lite/): This is licensed under the MIT license, see above. diff --git a/standalone/NOTICE b/standalone/NOTICE index 75120044793..221362e4c98 100644 --- a/standalone/NOTICE +++ b/standalone/NOTICE @@ -41,6 +41,11 @@ Copyright (c) 2022 Pablo Alcaraz Martínez == +This product includes software developed by Logback +Copyright (C) 1999-2024, QOS.ch. All rights reserved. + +== + This product includes software developed by the Chart.js project. Copyright (c) 2018 Chart.js Contributors @@ -66,6 +71,11 @@ Copyright (c) 2012, Duo Security, Inc. == +This product includes software developed by hash4j +Copyright 2025-2026 Dynatrace LLC + +== + This product includes software developed by Esoteric Software. Copyright (c) 2008-2023, Nathan Sweet All rights reserved. @@ -136,6 +146,11 @@ Copyright 2008 Google Inc. All rights reserved. == +This product includes software developed by RE2/J +Copyright (c) 2009 The Go Authors. All rights reserved. + +== + This product includes software developed by ZXing Copyright: 2007-2020, ZXing authors @@ -227,18 +242,6 @@ Copyright (c) 2018, 2024 Oracle and/or its affiliates. All rights reserved. == -This product includes software developed by t-digest -The code for the t-digest was originally authored by Ted Dunning - -== - -This product includes software developed by Method Parameter Name Access for Java. -Portions copyright (c) 2006-2018 Paul Hammant & ThoughtWorks Inc -Portions copyright (c) 2000-2007 INRIA, France Telecom -All rights reserved. - -== - This product includes software developed by Scala Logging. Copyright 2014-2021 Lightbend, Inc. @@ -260,11 +263,6 @@ Copyright (c) 2014-2017 Enrico M. Crisostomo == -This product includes software developed by Wavefront's Java SDK -Copyright (c) 2017-Present VMware, Inc. All Rights Reserved. - -== - This product includes software developed by Yammer Metrics. Copyright (c) 2010-2013 Coda Hale, Yammer.com @@ -284,16 +282,6 @@ Copyright (c) 2008-present, SpryMedia Limited == -This product includes software developed by define-data-property -Copyright (c) 2023 Jordan Harband - -== - -This product includes software developed by define-properties -Copyright (C) 2015 Jordan Harband - -== - This product includes software produced and maintained by Jakarta Activation project All content is the property of the respective authors or their employers. @@ -314,18 +302,8 @@ Copyright (c) 2021 Jonathan Peterson (@Eonasdan) == -This product includes software developed by es-define-property -Copyright (c) 2024 Jordan Harband - -== - -This product includes software developed by es-errors -Copyright (c) 2024 Jordan Harband - -== - -This product includes software developed by globalThis -Copyright (c) 2016 Jordan Harband +This product includes software developed by FingerprintJS +Copyright (c) 2025 FingerprintJS, Inc == @@ -334,16 +312,6 @@ Copyright (c) 2006 Google Inc. == -This product includes software developed by gopd -Copyright (c) 2022 Jordan Harband - -== - -This product includes software developed by has-property-descriptors -Copyright (c) 2022 Inspect JS - -== - This product includes the Javascript syntax highlighter. Copyright (c) 2006, Ivan Sagalaev @@ -355,11 +323,6 @@ Copyright (c) 2008-2011 Jasper Humphrey == -This product includes software developed by inherits -Copyright (c) 2011-2023 Isaac Z. Schlueter - -== - This product includes software developed by Inputmask Copyright (c) 2010 - 2018 Robin Herbots @@ -528,11 +491,6 @@ Copyright (c) 2011-2016 Tim Wood, Iskren Chernev, Moment.js contributors == -This product includes software developed by murmurhash-js -Copyright (c) 2011 Gary Court - -== - This product includes software developed by Byte Buddy Copyright 2014 - Present Rafael Winterhalter @@ -576,11 +534,6 @@ Copyright © Nicolas Gallagher and Jonathan Neal == -This product includes software developed by object-keys -Copyright (C) 2013 Jordan Harband - -== - This product includes software developed by OGNL Copyright (c) 2004, Drew Davidson and Luke Blanshard @@ -678,6 +631,11 @@ All content is the property of the respective authors or their employers. == +This product includes software developed by Hibernate models +Copyright: Red Hat Inc. and Hibernate Authors + +== + This product includes software developed by Hibernate ORM Copyright Red Hat Inc. and Hibernate Authors @@ -883,11 +841,6 @@ Copyright (c) Microsoft Corporation. == -This product includes software developed by UAParser.js -Copyright (c) 2012-2025 Faisal Salman - -== - This product includes software developed by webjars-locator-lite Copyright (c) 2013 James Ward diff --git a/standalone/pom.xml b/standalone/pom.xml index 1a5953c0fd9..a704c174c96 100644 --- a/standalone/pom.xml +++ b/standalone/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope Standalone Distribution diff --git a/wa/bootstrap/pom.xml b/wa/bootstrap/pom.xml index 36f4b4e52d1..6d85d65220b 100644 --- a/wa/bootstrap/pom.xml +++ b/wa/bootstrap/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-wa - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope WA Bootstrap @@ -66,6 +66,11 @@ under the License. cas-server-support-oidc-core + + org.slf4j + jcl-over-slf4j + test + org.slf4j slf4j-simple diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AuthModulePropertySourceMapper.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AuthModulePropertySourceMapper.java index 3387fbb822e..9ad28f5abea 100644 --- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AuthModulePropertySourceMapper.java +++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AuthModulePropertySourceMapper.java @@ -468,7 +468,7 @@ public Map map(final AuthModuleTO authModuleTO, final DuoMfaAuth props.setDuoIntegrationKey(conf.getIntegrationKey()); props.setDuoSecretKey(conf.getSecretKey()); - return prefix("cas.authn.mfa.duo.", WAConfUtils.asMap(props)); + return prefix("cas.authn.mfa.duo[].", WAConfUtils.asMap(props)); } @Override diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/PasswordManagementPropertySourceMapper.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/PasswordManagementPropertySourceMapper.java index d1dbbc5cd97..86677c3e97d 100644 --- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/PasswordManagementPropertySourceMapper.java +++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/PasswordManagementPropertySourceMapper.java @@ -53,6 +53,9 @@ public Map map( LOG.warn("Application context is not ready to bootstrap WA configuration"); return Map.of(); } + if (!passwordManagementTO.isEnabled()) { + return Map.of(); + } SyncopePasswordManagementProperties props = new SyncopePasswordManagementProperties(); props.setDomain(conf.getDomain()); @@ -62,9 +65,7 @@ public Map map( props.setSearchFilter(conf.getSearchFilter()); props.setHeaders(conf.getHeaders()); - Map mapped = prefix("cas.authn.pm.syncope.", WAConfUtils.asMap(props)); - mapped.put("cas.authn.pm.syncope.enabled", passwordManagementTO.isEnabled()); - return mapped; + return prefix("cas.authn.pm.syncope.", WAConfUtils.asMap(props)); } @Override @@ -72,6 +73,10 @@ public Map map( final PasswordManagementTO passwordManagementTO, final LDAPPasswordManagementConf conf) { + if (!passwordManagementTO.isEnabled()) { + return Map.of(); + } + LdapPasswordManagementProperties props = new LdapPasswordManagementProperties(); props.setName(passwordManagementTO.getKey()); props.setType(AbstractLdapProperties.LdapType.valueOf(conf.getLdapType().name())); @@ -79,9 +84,7 @@ public Map map( fill(props, conf); - Map mapped = prefix("cas.authn.pm.ldap[].", WAConfUtils.asMap(props)); - mapped.put("cas.authn.pm.ldap.enabled", passwordManagementTO.isEnabled()); - return mapped; + return prefix("cas.authn.pm.ldap[].", WAConfUtils.asMap(props)); } @Override @@ -89,6 +92,10 @@ public Map map( final PasswordManagementTO passwordManagementTO, final JDBCPasswordManagementConf conf) { + if (!passwordManagementTO.isEnabled()) { + return Map.of(); + } + JdbcPasswordManagementProperties props = new JdbcPasswordManagementProperties(); props.setSqlChangePassword(conf.getSqlChangePassword()); props.setSqlFindEmail(conf.getSqlFindEmail()); @@ -100,9 +107,7 @@ public Map map( props.setSqlUnlockAccount(conf.getSqlUnlockAccount()); fill(props, conf); - Map mapped = prefix("cas.authn.pm.jdbc.", WAConfUtils.asMap(props)); - mapped.put("cas.authn.pm.jdbc.enabled", passwordManagementTO.isEnabled()); - return mapped; + return prefix("cas.authn.pm.jdbc.", WAConfUtils.asMap(props)); } @Override @@ -110,6 +115,10 @@ public Map map( final PasswordManagementTO passwordManagementTO, final RESTPasswordManagementConf conf) { + if (!passwordManagementTO.isEnabled()) { + return Map.of(); + } + RestfulPasswordManagementProperties props = new RestfulPasswordManagementProperties(); props.setEndpointPassword(conf.getEndpointPassword()); props.setEndpointUrlAccountUnlock(conf.getEndpointUrlAccountUnlock()); @@ -124,8 +133,6 @@ public Map map( props.setFieldNameUser(conf.getFieldNameUser()); props.setHeaders(conf.getHeaders()); - Map mapped = prefix("cas.authn.pm.rest.", WAConfUtils.asMap(props)); - mapped.put("cas.authn.pm.rest.enabled", passwordManagementTO.isEnabled()); - return mapped; + return prefix("cas.authn.pm.rest.", WAConfUtils.asMap(props)); } } diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/WAConfUtils.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/WAConfUtils.java index 69e907bafea..6a745bfd91e 100644 --- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/WAConfUtils.java +++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/WAConfUtils.java @@ -19,13 +19,6 @@ package org.apache.syncope.wa.bootstrap.mapping; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.PropertyNamingStrategies; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; import java.io.IOException; import java.io.Serializable; import java.io.StringWriter; @@ -37,13 +30,20 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.util.ResourceUtils; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.exc.StreamWriteException; +import tools.jackson.databind.PropertyNamingStrategies; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.databind.ser.std.SimpleFilterProvider; +import tools.jackson.databind.ser.std.StdSerializer; +import tools.jackson.dataformat.yaml.YAMLMapper; public final class WAConfUtils { private static class ResourceSerializer extends StdSerializer { - private static final long serialVersionUID = 7971411664567411958L; - ResourceSerializer() { this(null); } @@ -53,14 +53,19 @@ private static class ResourceSerializer extends StdSerializer { } @Override - public void serialize(final Resource value, + public void serialize( + final Resource value, final JsonGenerator jgen, - final SerializerProvider provider) throws IOException { + final SerializationContext ctx) throws JacksonException { if (value instanceof ClassPathResource) { jgen.writeString(ResourceUtils.CLASSPATH_URL_PREFIX + value.getFilename()); } else { - jgen.writeString(value.getURI().toString()); + try { + jgen.writeString(value.getURI().toString()); + } catch (IOException e) { + throw new StreamWriteException(jgen, "During Resource serialization", e); + } } } } @@ -68,14 +73,16 @@ public void serialize(final Resource value, private static final YAMLMapper YAML_MAPPER; static { - YAML_MAPPER = new YAMLMapper(); - YAML_MAPPER.setFilterProvider(new SimpleFilterProvider().setFailOnUnknownId(false)); - YAML_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); - YAML_MAPPER.setPropertyNamingStrategy(PropertyNamingStrategies.KEBAB_CASE); - SimpleModule module = new SimpleModule(); module.addSerializer(Resource.class, new ResourceSerializer()); - YAML_MAPPER.registerModule(module); + + YAML_MAPPER = YAMLMapper.builder(). + addModule(module). + filterProvider(new SimpleFilterProvider().setFailOnUnknownId(false)). + propertyNamingStrategy(PropertyNamingStrategies.KEBAB_CASE). + changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)). + changeDefaultPropertyInclusion(incl -> incl.withContentInclusion(JsonInclude.Include.NON_NULL)). + build(); } public static Map asMap(final Serializable properties) { diff --git a/wa/pom.xml b/wa/pom.xml index e5f35224c2d..d89a77767fc 100644 --- a/wa/pom.xml +++ b/wa/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope WA diff --git a/wa/starter/pom.xml b/wa/starter/pom.xml index 6c6bac3ece0..09642ac7d15 100644 --- a/wa/starter/pom.xml +++ b/wa/starter/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-wa - 4.1.0-SNAPSHOT + 5.0.0-SNAPSHOT Apache Syncope WA Spring Boot Starter @@ -43,6 +43,15 @@ under the License. spring-cloud-starter-bootstrap + + org.springframework.boot + spring-boot-jdbc + + + org.springframework.boot + spring-boot-data-jdbc + + org.apache.syncope.wa syncope-wa-bootstrap @@ -443,19 +452,20 @@ under the License. org.springframework.boot - spring-boot-starter-actuator + spring-boot-starter-security org.springframework.boot - spring-boot-starter-security + spring-boot-starter-validation + org.springframework.boot - spring-boot-starter-validation + spring-boot-health - com.fasterxml.jackson.module + tools.jackson.module jackson-module-kotlin diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java index 5a438833b50..9f7232767b9 100644 --- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java +++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java @@ -29,23 +29,14 @@ import org.apereo.cas.support.saml.idp.metadata.generator.SamlIdPMetadataGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; -import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration; -import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; -import org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration; -import org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration; -import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; -import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration; import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; -import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.data.jdbc.autoconfigure.DataJdbcRepositoriesAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.health.DataSourceHealthContributorAutoConfiguration; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.cloud.context.refresh.ContextRefresher; import org.springframework.context.annotation.EnableAspectJAutoProxy; @@ -57,22 +48,10 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication(exclude = { - /* - * List of Spring Boot classes that we want to disable and remove from auto-configuration. - */ - HibernateJpaAutoConfiguration.class, - JerseyAutoConfiguration.class, - GroovyTemplateAutoConfiguration.class, - GsonAutoConfiguration.class, - JmxAutoConfiguration.class, DataSourceAutoConfiguration.class, DataSourceHealthContributorAutoConfiguration.class, - DataSourceTransactionManagerAutoConfiguration.class, - RedisAutoConfiguration.class, - RedisRepositoriesAutoConfiguration.class, - MongoAutoConfiguration.class, - MongoDataAutoConfiguration.class, - CassandraAutoConfiguration.class, + DataJdbcRepositoriesAutoConfiguration.class, + JmxAutoConfiguration.class, CasGoogleAuthenticatorLdapAutoConfiguration.class }) @EnableConfigurationProperties({ WAProperties.class, CasConfigurationProperties.class }) diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/actuate/SyncopeCoreHealthIndicator.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/actuate/SyncopeCoreHealthIndicator.java index a2aeeaaf9cf..3ce1bc436b0 100644 --- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/actuate/SyncopeCoreHealthIndicator.java +++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/actuate/SyncopeCoreHealthIndicator.java @@ -22,9 +22,9 @@ import org.apache.syncope.wa.bootstrap.WARestClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.actuate.health.Health; -import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.boot.actuate.health.Status; +import org.springframework.boot.health.contributor.Health; +import org.springframework.boot.health.contributor.HealthIndicator; +import org.springframework.boot.health.contributor.Status; public class SyncopeCoreHealthIndicator implements HealthIndicator { diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManager.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManager.java index dc92465ea59..b9da347a2ab 100644 --- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManager.java +++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManager.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.wa.starter.audit; -import com.fasterxml.jackson.core.JsonProcessingException; import java.time.OffsetDateTime; import java.util.Map; import org.apache.commons.lang3.Strings; @@ -68,7 +67,7 @@ protected void saveAuditRecord(final AuditActionContext audit) { result); auditEvent.setOpEvent(opEvent); waRestClient.getService(AuditService.class).create(auditEvent); - } catch (JsonProcessingException e) { + } catch (Exception e) { LOG.error("During serialization", e); } } diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java index f15398dff1b..83bc4b6254f 100644 --- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java +++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java @@ -119,7 +119,6 @@ import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.transaction.support.TransactionOperations; import org.springframework.web.client.RestTemplate; @@ -432,14 +431,13 @@ public PasswordManagementService jdbcPasswordChangeService( PasswordManagementProperties pm = casProperties.getAuthn().getPm(); if (pm.getCore().isEnabled() && StringUtils.isNotBlank(pm.getJdbc().getUrl())) { - PasswordEncoder encoder = PasswordEncoderUtils.newPasswordEncoder( - pm.getJdbc().getPasswordEncoder(), ctx); return new JdbcPasswordManagementService( passwordManagementCipherExecutor, casProperties, jdbcPasswordManagementDataSource, jdbcPasswordManagementTransactionTemplate, - passwordHistoryService, encoder); + passwordHistoryService, + PasswordEncoderUtils.newPasswordEncoder(pm.getJdbc().getPasswordEncoder(), ctx)); } return new NoOpPasswordManagementService(passwordManagementCipherExecutor, casProperties); @@ -456,7 +454,8 @@ public PasswordManagementService restPasswordChangeService( @Qualifier(PasswordHistoryService.BEAN_NAME) final PasswordHistoryService passwordHistoryService) { - if (casProperties.getAuthn().getPm().getCore().isEnabled()) { + PasswordManagementProperties pm = casProperties.getAuthn().getPm(); + if (pm.getCore().isEnabled() && StringUtils.isNotBlank(pm.getRest().getEndpointUrlChange())) { return new RestPasswordManagementService( passwordManagementCipherExecutor, casProperties, @@ -483,19 +482,19 @@ public PasswordManagementService passwordChangeService( @Qualifier("restPasswordChangeService") final PasswordManagementService restPasswordManagementService) { - if (ctx.getEnvironment().getProperty("cas.authn.pm.syncope.enabled", Boolean.class, Boolean.FALSE)) { + if (!(syncopePasswordManagementService instanceof NoOpPasswordManagementService)) { return syncopePasswordManagementService; } - if (ctx.getEnvironment().getProperty("cas.authn.pm.ldap.enabled", Boolean.class, Boolean.FALSE)) { + if (!(ldapPasswordManagementService instanceof NoOpPasswordManagementService)) { return ldapPasswordManagementService; } - if (ctx.getEnvironment().getProperty("cas.authn.pm.jdbc.enabled", Boolean.class, Boolean.FALSE)) { + if (!(jdbcPasswordManagementService instanceof NoOpPasswordManagementService)) { return jdbcPasswordManagementService; } - if (ctx.getEnvironment().getProperty("cas.authn.pm.rest.enabled", Boolean.class, Boolean.FALSE)) { + if (!(restPasswordManagementService instanceof NoOpPasswordManagementService)) { return restPasswordManagementService; } diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/events/WAEventRepository.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/events/WAEventRepository.java index b2d10f8a56c..aa93e35f225 100644 --- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/events/WAEventRepository.java +++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/events/WAEventRepository.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.wa.starter.events; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.json.JsonMapper; import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZoneId; @@ -27,6 +25,7 @@ import java.util.Map; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.to.AuditEventTO; import org.apache.syncope.common.lib.types.OpEvent; import org.apache.syncope.common.rest.api.service.AuditService; @@ -36,12 +35,14 @@ import org.apereo.cas.support.events.dao.CasEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.json.JsonMapper; public class WAEventRepository extends AbstractCasEventRepository { private static final Logger LOG = LoggerFactory.getLogger(WAEventRepository.class); - private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + private static final JsonMapper MAPPER = new SyncopeJsonMapper(); private final WARestClient waRestClient; @@ -95,7 +96,7 @@ public CasEvent saveInternal(final CasEvent event) { OpEvent.Outcome.SUCCESS); auditEvent.setOpEvent(opEvent); waRestClient.getService(AuditService.class).create(auditEvent); - } catch (JsonProcessingException e) { + } catch (JacksonException e) { LOG.error("During serialization", e); } return event; diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java index 17ca8fe4879..98b018b6f85 100644 --- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java +++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.wa.starter; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.core.Response; import java.time.LocalDateTime; @@ -33,6 +31,7 @@ import org.apache.syncope.common.keymaster.client.api.ServiceOps; import org.apache.syncope.common.keymaster.client.api.model.NetworkService; import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; import org.apache.syncope.common.lib.to.AttrRepoTO; import org.apache.syncope.common.lib.to.AuditConfTO; import org.apache.syncope.common.lib.to.AuditEventTO; @@ -56,6 +55,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; +import tools.jackson.jakarta.rs.json.JacksonJsonProvider; public class SyncopeCoreTestingServer implements ApplicationListener { @@ -393,7 +393,7 @@ public void onApplicationEvent(final ContextRefreshedEvent event) { sf.setResourceProvider( ImpersonationService.class, new SingletonResourceProvider(new StubImpersonationService(), true)); - sf.setProviders(List.of(new JacksonJsonProvider(JsonMapper.builder().findAndAddModules().build()))); + sf.setProviders(List.of(new JacksonJsonProvider(new SyncopeJsonMapper()))); sf.create(); // 2. register Core in Keymaster