diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2addc10811d..fbc74d27374 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -18,12 +18,10 @@ name: "CodeQL" on: push: - branches: [master,pr-*] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: analyze: @@ -45,26 +43,19 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4.37.6 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -89,4 +80,4 @@ jobs: mvn -U -T 1C -PskipTests,all - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4.37.6 diff --git a/.github/workflows/crosschecks.yml b/.github/workflows/crosschecks.yml index d382b4fbd5a..75f327687c1 100644 --- a/.github/workflows/crosschecks.yml +++ b/.github/workflows/crosschecks.yml @@ -18,12 +18,10 @@ name: "Cross Checks" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: crosschecks: @@ -33,27 +31,21 @@ jobs: fail-fast: false matrix: language: ['java'] - java: [ '21', '25-ea' ] + java: [ '21', '27-ea' ] os: [ubuntu-latest, windows-latest, macos-latest] steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: ${{ matrix.java }} - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: Validate @@ -64,7 +56,7 @@ jobs: - name: Start PostgreSQL on Windows if: runner.os == 'Windows' run: | - sc.exe config postgresql-x64-14 start=auto - sc.exe start postgresql-x64-14 + sc.exe config postgresql-x64-17 start=auto + sc.exe start postgresql-x64-17 - name: Verify run: mvn -f fit/core-reference/pom.xml verify '-Dit.test=RESTITCase' '-Dinvoker.streamLogs=true' '-Dmodernizer.skip=true' '-Drat.skip=true' '-Dcheckstyle.skip=true' '-Djacoco.skip=true' diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml index 5a10de05be0..18728688ce3 100644 --- a/.github/workflows/dockerhub.yml +++ b/.github/workflows/dockerhub.yml @@ -19,9 +19,7 @@ name: "Push to DockerHub" on: push: branches: - - master - tags: - - syncope-4.0.* + - 4_0_X jobs: push2DockerHub: @@ -55,22 +53,16 @@ jobs: - name: Disk space after run: df . -h - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all,docker' - name: 'Push to DockerHub' diff --git a/.github/workflows/fit_Elasticsearch.yml b/.github/workflows/fit_Elasticsearch.yml index b2f24b4e4e5..a05421eae00 100644 --- a/.github/workflows/fit_Elasticsearch.yml +++ b/.github/workflows/fit_Elasticsearch.yml @@ -18,12 +18,10 @@ name: "FIT Elasticsearch" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_Elasticsearch: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'Elasticsearch / PostgreSQL / JSON' diff --git a/.github/workflows/fit_OpenSearch.yml b/.github/workflows/fit_OpenSearch.yml index 25d5851d46e..4e0eec36620 100644 --- a/.github/workflows/fit_OpenSearch.yml +++ b/.github/workflows/fit_OpenSearch.yml @@ -18,12 +18,10 @@ name: "FIT OpenSearch" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_OpenSearch: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'zulu' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'OpenSearch / PostgreSQL / JSON' diff --git a/.github/workflows/fit_Payara.yml b/.github/workflows/fit_Payara.yml index 7b2d697106b..e61ceb1ed68 100644 --- a/.github/workflows/fit_Payara.yml +++ b/.github/workflows/fit_Payara.yml @@ -18,12 +18,10 @@ name: "FIT Payara" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_Payara: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'Payara / PostgreSQL / JSON' diff --git a/.github/workflows/fit_Tomcat_LiveSync.yml b/.github/workflows/fit_Tomcat_LiveSync.yml index 7481cae0ddc..3e1d6f61359 100644 --- a/.github/workflows/fit_Tomcat_LiveSync.yml +++ b/.github/workflows/fit_Tomcat_LiveSync.yml @@ -18,12 +18,10 @@ name: "FIT Tomcat LiveSync" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_Tomcat_LiveSync: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'Tomcat / PostgreSQL / Debezium' diff --git a/.github/workflows/fit_Tomcat_PostgreSQL_JSON.yml b/.github/workflows/fit_Tomcat_PostgreSQL_JSON.yml index 2d9a5b8bcbd..ed2f4aed112 100644 --- a/.github/workflows/fit_Tomcat_PostgreSQL_JSON.yml +++ b/.github/workflows/fit_Tomcat_PostgreSQL_JSON.yml @@ -18,12 +18,10 @@ name: "FIT Tomcat PostgreSQL JSON" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_Tomcat_PostgreSQL_JSON: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'Tomcat / PostgreSQL / JSON' diff --git a/.github/workflows/fit_Tomcat_PostgreSQL_XML.yml b/.github/workflows/fit_Tomcat_PostgreSQL_XML.yml index b3153d898c4..bf9cc65f255 100644 --- a/.github/workflows/fit_Tomcat_PostgreSQL_XML.yml +++ b/.github/workflows/fit_Tomcat_PostgreSQL_XML.yml @@ -18,12 +18,10 @@ name: "FIT Tomcat PostgreSQL XML" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_Tomcat_PostgreSQL_XML: @@ -31,9 +29,9 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.0 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 @@ -41,12 +39,6 @@ jobs: uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'Tomcat / PostgreSQL / XML' diff --git a/.github/workflows/fit_Tomcat_PostgreSQL_YAML.yml b/.github/workflows/fit_Tomcat_PostgreSQL_YAML.yml index ba19bdd2d0c..f77bc07fc4b 100644 --- a/.github/workflows/fit_Tomcat_PostgreSQL_YAML.yml +++ b/.github/workflows/fit_Tomcat_PostgreSQL_YAML.yml @@ -18,12 +18,10 @@ name: "FIT Tomcat PostgreSQL YAML" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_Tomcat_PostgreSQL_YAML: @@ -31,9 +29,9 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.0 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 @@ -41,12 +39,6 @@ jobs: uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'Tomcat / PostgreSQL / YAML' diff --git a/.github/workflows/fit_WA_OIDCC4UI.yml b/.github/workflows/fit_WA_OIDCC4UI.yml index fc045e60f44..f284bb596fe 100644 --- a/.github/workflows/fit_WA_OIDCC4UI.yml +++ b/.github/workflows/fit_WA_OIDCC4UI.yml @@ -18,12 +18,10 @@ name: "FIT WA OIDCC4UI" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_WA_OIDCC4UI: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'WA / OIDCC4UI' diff --git a/.github/workflows/fit_WA_OpenFGA.yml b/.github/workflows/fit_WA_OpenFGA.yml index 56680e70c06..a4d8c254288 100644 --- a/.github/workflows/fit_WA_OpenFGA.yml +++ b/.github/workflows/fit_WA_OpenFGA.yml @@ -18,12 +18,10 @@ name: "FIT WA OpenFGA" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_WA_OpenFGA: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'Unit Tests: OpenFGA' diff --git a/.github/workflows/fit_WA_SAML2SP4UI.yml b/.github/workflows/fit_WA_SAML2SP4UI.yml index 93881c49d34..ed5367aa755 100644 --- a/.github/workflows/fit_WA_SAML2SP4UI.yml +++ b/.github/workflows/fit_WA_SAML2SP4UI.yml @@ -18,12 +18,10 @@ name: "FIT WA SAML2SP4UI" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_WA_SAML2SP4UI: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'WA / SAML2SP4UI' diff --git a/.github/workflows/fit_WA_SRA_CASClient.yml b/.github/workflows/fit_WA_SRA_CASClient.yml index f32f124ff1d..7956c8f88c1 100644 --- a/.github/workflows/fit_WA_SRA_CASClient.yml +++ b/.github/workflows/fit_WA_SRA_CASClient.yml @@ -18,12 +18,10 @@ name: "FIT WA SRA CASClient" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_WA_SRA_CASClient: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'WA / SRA / CAS Client' diff --git a/.github/workflows/fit_WA_SRA_OAuth2.yml b/.github/workflows/fit_WA_SRA_OAuth2.yml index e016731bf4b..56cde3c5ee0 100644 --- a/.github/workflows/fit_WA_SRA_OAuth2.yml +++ b/.github/workflows/fit_WA_SRA_OAuth2.yml @@ -18,12 +18,10 @@ name: "FIT WA SRA OAuth2" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_WA_SRA_OAuth2: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'WA / SRA / OAuth 2.0' diff --git a/.github/workflows/fit_WA_SRA_OIDC.yml b/.github/workflows/fit_WA_SRA_OIDC.yml index 0360b68d726..367aa133109 100644 --- a/.github/workflows/fit_WA_SRA_OIDC.yml +++ b/.github/workflows/fit_WA_SRA_OIDC.yml @@ -18,12 +18,10 @@ name: "FIT WA SRA OIDC" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_WA_SRA_OIDC: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'WA / SRA / OpenID Connect 1.0' diff --git a/.github/workflows/fit_WA_SRA_SAML2.yml b/.github/workflows/fit_WA_SRA_SAML2.yml index b60f464740f..97e6db5d11f 100644 --- a/.github/workflows/fit_WA_SRA_SAML2.yml +++ b/.github/workflows/fit_WA_SRA_SAML2.yml @@ -18,12 +18,10 @@ name: "FIT WA SRA SAML2" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_WA_SRA_SAML2: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'WA / SRA / SAML 2.0' diff --git a/.github/workflows/fit_Wildfly.yml b/.github/workflows/fit_Wildfly.yml index 5e165174730..af2ed68f645 100644 --- a/.github/workflows/fit_Wildfly.yml +++ b/.github/workflows/fit_Wildfly.yml @@ -18,12 +18,10 @@ name: "FIT Wildfly" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_Wildfly: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'Wildfly / PostgreSQL / JSON' diff --git a/.github/workflows/fit_Zookeeper.yml b/.github/workflows/fit_Zookeeper.yml index c2ff40d05e9..f0ee747871f 100644 --- a/.github/workflows/fit_Zookeeper.yml +++ b/.github/workflows/fit_Zookeeper.yml @@ -18,12 +18,10 @@ name: "FIT Zookeeper" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: fit_Zookeeper: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'Zookeeper / PostgreSQL / JSON' diff --git a/.github/workflows/mariadb.yml b/.github/workflows/mariadb.yml index 8c2e2b7b1ea..2047e3bfbc0 100644 --- a/.github/workflows/mariadb.yml +++ b/.github/workflows/mariadb.yml @@ -18,12 +18,10 @@ name: "MariaDB" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: mariadb: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'Unit Tests: MariaDB' diff --git a/.github/workflows/mysql.yml b/.github/workflows/mysql.yml index 30997151f9b..b241f1de1ca 100644 --- a/.github/workflows/mysql.yml +++ b/.github/workflows/mysql.yml @@ -18,12 +18,10 @@ name: "MySQL" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: mysql: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'Unit Tests: MySQL' diff --git a/.github/workflows/neo4j.yml b/.github/workflows/neo4j.yml index d698236c3fc..e07afe9cd91 100644 --- a/.github/workflows/neo4j.yml +++ b/.github/workflows/neo4j.yml @@ -18,12 +18,10 @@ name: "Neo4j" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: neo4j: @@ -31,22 +29,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'Unit Tests: Neo4j' diff --git a/.github/workflows/oracle.yml b/.github/workflows/oracle.yml index 128a47b4d51..ffeb2ff5d09 100644 --- a/.github/workflows/oracle.yml +++ b/.github/workflows/oracle.yml @@ -18,35 +18,29 @@ name: "Oracle" on: push: - branches: ['master', 'pr-*'] + branches: [4_0_X] pull_request: # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 13 * * 4' + branches: [4_0_X] jobs: oracle: runs-on: ubuntu-latest steps: + - name: Free disk space + run: curl -fsSL https://raw.githubusercontent.com/kou/arrow/e49d8ae15583ceff03237571569099a6ad62be32/ci/scripts/util_free_space.sh | bash - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7.0.1 - name: Setup Java JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 21 - name: Setup Maven - uses: stCarolas/setup-maven@v5 + uses: stCarolas/setup-maven@v5.1 with: maven-version: 3.9.6 - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Build run: mvn -U -T 1C -P 'skipTests,all' - name: 'Unit Tests: Oracle' diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index 135a6d56a22..01a31424521 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -21,6 +21,6 @@ under the License. org.apache.maven.extensions maven-build-cache-extension - 1.2.0 + 1.2.3 diff --git a/CHANGES b/CHANGES index a7087493681..6bb9b3613c7 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,168 @@ Apache Syncope - CHANGES Licensed under Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0 -------------------------------------------------------------------------------- +Release Notes - Syncope - Version 4.0.7 +================================================================================ + +** Bug + * [SYNCOPE-1970] - Scheduled task Groovy implementation not updated when running in cluster + * [SYNCOPE-1977] - Error after Macro compose + +** Improvement + * [SYNCOPE-1978] - Search in auditevent based on username + * [SYNCOPE-1981] - Search audit events by who (the principal that performed the operation) + * [SYNCOPE-1982] - Slow query on OraclePlainSchemaRepoExtImpl + * [SYNCOPE-1983] - Make approval required for user self-operations + +Release Notes - Syncope - Version 4.0.6 +================================================================================ + +** Bug + * [SYNCOPE-1744] - Empty value in template when notification is sent on DELETE event + * [SYNCOPE-1921] - LDAPMembershipPropagationActions deletes memberships of groups not managed by Syncope + * [SYNCOPE-1960] - Cannot remove aux class from user, group or any object + +** Improvement + * [SYNCOPE-1958] - Fail early on invalid password + +Release Notes - Syncope - Version 4.0.5 +================================================================================ + +** Bug + * [SYNCOPE-1945] - Tasks result not visible until the refresh is triggered + * [SYNCOPE-1956] - Dropdown multivalue PlainAttr not updated via Console + * [SYNCOPE-1957] - Wrong validation triggered while updating users, groups or any objects after auxiliary class change + +** Improvement + * [SYNCOPE-1949] - Allow to search AuthProfile owners + * [SYNCOPE-1950] - Allow configuration of OIDC Token Expiration Policy in WA + * [SYNCOPE-1951] - Searching Realms by attributes + * [SYNCOPE-1953] - Improve job stopping + +** Task + * [SYNCOPE-1947] - Job actuator + +Release Notes - Syncope - Version 4.0.4 +================================================================================ + +** Bug + * [SYNCOPE-1933] - Console: only first dynamic group is shown + * [SYNCOPE-1936] - WA: only SIGNATURE and CURRENT keys generated for OIDC + * [SYNCOPE-1937] - User password history length is not correctly trimmed + * [SYNCOPE-1942] - Menu localization doesn't work properly on admin console + * [SYNCOPE-1943] - French Canadian GUI failed with Groups contextual menu + * [SYNCOPE-1944] - Membership derived attributes are not computed from membership plain attributes + +** Improvement + * [SYNCOPE-1934] - Allow to select multiple AnyTypeClasses for Realm + +** Task + * [SYNCOPE-1946] - Console: access audit history for Realms + +Release Notes - Syncope - Version 4.0.3 +================================================================================ + +** Bug + * [SYNCOPE-1922] - Disallow searches by encrypted attribute + * [SYNCOPE-1924] - Bug in retainAll, removeAll, and removeIf methods of PropagationByResource + * [SYNCOPE-1925] - Contextual menu stays open after modal window is closed + * [SYNCOPE-1928] - Session timeout not working for Console + +** Improvement + * [SYNCOPE-1926] - Reduce number of update on Syncope after a SCIM PATCH + * [SYNCOPE-1927] - Macro Tasks: simplify binary form property binding + * [SYNCOPE-1929] - Console: allow to extend session duration + * [SYNCOPE-1930] - Exploit MariaDB with JSON + * [SYNCOPE-1931] - Allow for JEXL custom functions + * [SYNCOPE-1932] - Improve AES management + +** Task + * [SYNCOPE-1923] - Virtual Threads and JDK versions + +Release Notes - Syncope - Version 4.0.2 +================================================================================ + +** Bug + * [SYNCOPE-1905] - Error while pulling a specific group (single pull) from resource + * [SYNCOPE-1906] - Wrong result order if sorting by plain attribute + * [SYNCOPE-1910] - Tasks and Reports executed nevertheless inactive + * [SYNCOPE-1911] - Case sensisitve search failing with Elasticsearch or OpenSearch + * [SYNCOPE-1912] - Linked account password is not propagated on update + * [SYNCOPE-1913] - Missing dependency to resolve inline Groovy in Syncope WA + * [SYNCOPE-1915] - Failure when running the Persistence Storage Upgrader against Oracle DBMS + * [SYNCOPE-1919] - Notification Job occasionally stuck + * [SYNCOPE-1920] - Error in search panel while trying to add a condition after an attribute of type Long + +** New Feature + * [SYNCOPE-1916] - Metrics + +** Improvement + * [SYNCOPE-1907] - Run Groovy code in a sandbox + * [SYNCOPE-1908] - Improve Relationship management + * [SYNCOPE-1909] - Docker containers as unprivileged user + * [SYNCOPE-1914] - WA SAML 2.0: support IdP customization per Client Application + * [SYNCOPE-1917] - Manage bypassTrustedDeviceEnabled on Syncope WA + * [SYNCOPE-1918] - Push task with assign unmatching rule remove dynamic memberships + +Release Notes - Syncope - Version 4.0.1 +================================================================================ + +** Bug + * [SYNCOPE-1888] - Console: change required property for specific inputs in policy configuration + * [SYNCOPE-1890] - Configure max retry attempts for login with Syncope authentication module on WA + * [SYNCOPE-1891] - Missing mapped attribute setting for SAML2IdPAuthModuleConf + * [SYNCOPE-1894] - Mapping parsing error while schema contains a dot + * [SYNCOPE-1895] - Console CSV export does not consider search filter + * [SYNCOPE-1896] - Audit: not serializable exception for ConnectorObject and Any instances + * [SYNCOPE-1899] - Console: code editor does not persist changes + * [SYNCOPE-1902] - Can't set Unauthorized Redirect URL in access policy via Console + * [SYNCOPE-1904] - Console: cannot edit authentication.attributes under Keymaster > Parameters + +** Improvement + * [SYNCOPE-1886] - Elasticsearch and Opensearch query performance improvements + * [SYNCOPE-1887] - Enduser: require re-authentication for sensitive features + * [SYNCOPE-1889] - Improve group search using SCIM extension + * [SYNCOPE-1892] - Further Macro improvements + * [SYNCOPE-1893] - SCIM extension improvement + * [SYNCOPE-1897] - WA: mapping Google MFA crypto settings + * [SYNCOPE-1898] - Reset password with WA if mustChangePassword is true + * [SYNCOPE-1900] - Implement OIDC Back-Channel Logout for Console and Enduser + * [SYNCOPE-1903] - Add endpoint to verify the security answer during password reset in CAS + +Release Notes - Syncope - Version 4.0.0 +================================================================================ + +** Bug + * [SYNCOPE-1868] - NPE when performing provision without changing password + * [SYNCOPE-1871] - Realm selection shows only one of allowed sub-trees + * [SYNCOPE-1873] - LocalDateTime fields not properly handled in BeanPanel + * [SYNCOPE-1875] - SAML delegated authentication: metadata keeps getting re-generated + * [SYNCOPE-1878] - Priority propagation failing with NOT_ATTEMPTED + * [SYNCOPE-1879] - Serialization error while auditing Pull data + * [SYNCOPE-1881] - WA: SP Metadata not fetched from Core + * [SYNCOPE-1882] - Multivalue dropdown attributes show additional empty selectable value + * [SYNCOPE-1883] - Google MFA authentication does not validate scratch codes + * [SYNCOPE-1884] - Errors with Encrypted plain values + +** New Feature + * [SYNCOPE-1617] - Debezium support + * [SYNCOPE-1861] - OpenLDAP Sync Replication support + * [SYNCOPE-1874] - Realm attributes + +** Improvement + * [SYNCOPE-1621] - Allow export for individual items in XML + * [SYNCOPE-1841] - Read-only multi value schema must not show new and delete button + * [SYNCOPE-1869] - Support evaluation order for client apps + * [SYNCOPE-1870] - Generic exception handling (RestServiceExceptionMapper) + * [SYNCOPE-1872] - Allow setting of force execution for MFA authentication + * [SYNCOPE-1880] - SCIM endpoints ignore the Prefer header + * [SYNCOPE-1885] - Self password reset does not trigger notification tasks without Flowable extension + +** Task + * [SYNCOPE-1866] - Implement AuthProfile management in Enduser + * [SYNCOPE-1876] - Remove Virtual Attributes + * [SYNCOPE-1877] - Include SAML SP keystore and metadata in AuthModule configuration + Release Notes - Syncope - Version 4.0.0-M1 ================================================================================ diff --git a/NOTICE b/NOTICE index 956e233e6e7..b3a136c86ef 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Apache Syncope -Copyright 2012-2025 The Apache Software Foundation +Copyright 2012-2026 The Apache Software Foundation This product includes software developed by: The Apache Software Foundation (http://www.apache.org/). diff --git a/archetype/pom.xml b/archetype/pom.xml index 5ff22d221fa..dc42391e646 100644 --- a/archetype/pom.xml +++ b/archetype/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope - 4.0.0-SNAPSHOT + 4.0.8-SNAPSHOT Apache Syncope Archetype @@ -34,7 +34,7 @@ under the License. maven-archetype - 3.3.1 + 3.4.1 ${basedir}/.. diff --git a/archetype/src/main/resources/archetype-resources/enduser/pom.xml b/archetype/src/main/resources/archetype-resources/enduser/pom.xml index 9acecc71fd1..7a977b3a31c 100644 --- a/archetype/src/main/resources/archetype-resources/enduser/pom.xml +++ b/archetype/src/main/resources/archetype-resources/enduser/pom.xml @@ -44,8 +44,8 @@ under the License. - org.apache.syncope.client.idrepo - syncope-client-idrepo-enduser + org.apache.syncope.client.am + syncope-client-am-enduser diff --git a/archetype/src/main/resources/archetype-resources/fit/pom.xml b/archetype/src/main/resources/archetype-resources/fit/pom.xml index 42191e79e62..8c244ccf7a8 100644 --- a/archetype/src/main/resources/archetype-resources/fit/pom.xml +++ b/archetype/src/main/resources/archetype-resources/fit/pom.xml @@ -437,6 +437,13 @@ under the License. postgres + -Dfile.encoding=UTF-8 -server -Xms1536m -Xmx1536m + -XX:NewSize=256m -XX:MaxNewSize=256m -XX:+DisableExplicitGC + -Dsyncope.conf.dir=/opt/syncope/conf + -Dsyncope.connid.location=file:/opt/syncope/bundles + -Djava.security.egd=file:/dev/./urandom + -Djavax.net.ssl.trustStore=/opt/syncope/conf/keystore.p12 + -Djavax.net.ssl.trustStorePassword=password docker,postgresql,all jdbc:postgresql://postgres:5432/syncope?stringtype=unspecified syncope @@ -456,6 +463,7 @@ under the License. ${basedir}/../core/target/classes/domains/MasterContent.xml:/opt/syncope/conf/domains/MasterContent.xml:ro ${settings.localRepository}/${groupIdDocker}/syncope-core/${project.version}/syncope-core-${project.version}-classes.jar:/opt/syncope/lib/syncope-core-classes.jar:ro ${settings.localRepository}/${groupIdDocker}/syncope-common/${project.version}/syncope-common-${project.version}.jar:/opt/syncope/lib/syncope-common.jar:ro + ${basedir}/src/test/resources/keystore.p12:/opt/syncope/conf/keystore.p12:ro @@ -474,11 +482,6 @@ under the License. syncope - -Dfile.encoding=UTF-8 -server -Xms1536m -Xmx1536m - -XX:NewSize=256m -XX:MaxNewSize=256m -XX:+DisableExplicitGC - -Djava.security.egd=file:/dev/./urandom - -Djavax.net.ssl.trustStore=/opt/syncope/conf/keystore.p12 - -Djavax.net.ssl.trustStorePassword=password docker,all http://syncope-console:8080/syncope-console/ http://syncope:8080/syncope/rest/keymaster @@ -489,7 +492,6 @@ under the License. - ${basedir}/src/test/resources/keystore.p12:/opt/syncope/conf/keystore.p12:ro ${settings.localRepository}/${groupIdDocker}/syncope-console/${project.version}/syncope-console-${project.version}-classes.jar:/opt/syncope/lib/syncope-console-classes.jar:ro ${settings.localRepository}/${groupIdDocker}/syncope-common/${project.version}/syncope-common-${project.version}.jar:/opt/syncope/lib/syncope-common.jar:ro diff --git a/archetype/src/main/resources/archetype-resources/pom.xml b/archetype/src/main/resources/archetype-resources/pom.xml index ddbaf396583..25a71bc0e92 100644 --- a/archetype/src/main/resources/archetype-resources/pom.xml +++ b/archetype/src/main/resources/archetype-resources/pom.xml @@ -90,8 +90,8 @@ under the License. - org.apache.syncope.client.idrepo - syncope-client-idrepo-enduser + org.apache.syncope.client.am + syncope-client-am-enduser ${syncope.version} diff --git a/archetype/src/main/resources/archetype-resources/wa/pom.xml b/archetype/src/main/resources/archetype-resources/wa/pom.xml index c2619707b5b..399525e8c38 100644 --- a/archetype/src/main/resources/archetype-resources/wa/pom.xml +++ b/archetype/src/main/resources/archetype-resources/wa/pom.xml @@ -38,6 +38,13 @@ under the License. + + org.springframework + spring-framework-bom + ${spring-framework.version} + pom + import + org.springframework.boot spring-boot-dependencies diff --git a/client/am/console/pom.xml b/client/am/console/pom.xml index e49b624ca3f..64369a6fc0b 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.0.0-SNAPSHOT + 4.0.8-SNAPSHOT Apache Syncope Client AM Console diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/AMConsoleContext.java b/client/am/console/src/main/java/org/apache/syncope/client/console/AMConsoleContext.java index 46b94e1603e..8d8b53f3bc3 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/AMConsoleContext.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/AMConsoleContext.java @@ -33,7 +33,6 @@ import org.apache.syncope.client.console.rest.OIDCJWKSRestClient; import org.apache.syncope.client.console.rest.PolicyRestClient; import org.apache.syncope.client.console.rest.SAML2IdPEntityRestClient; -import org.apache.syncope.client.console.rest.SAML2SPEntityRestClient; import org.apache.syncope.client.console.rest.SRARouteRestClient; import org.apache.syncope.client.console.rest.SRAStatisticsRestClient; import org.apache.syncope.client.console.rest.WAConfigRestClient; @@ -100,12 +99,6 @@ public SAML2IdPEntityRestClient saml2IdPEntityRestClient() { return new SAML2IdPEntityRestClient(); } - @ConditionalOnMissingBean - @Bean - public SAML2SPEntityRestClient saml2SPEntityRestClient() { - return new SAML2SPEntityRestClient(); - } - @ConditionalOnMissingBean @Bean public SRARouteRestClient sraRouteRestClient() { diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel.java index f6e02fe5802..be7597329ef 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel.java @@ -19,16 +19,18 @@ package org.apache.syncope.client.console.authprofiles; import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; -import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.authprofiles.AuthProfileDirectoryPanel.AuthProfileProvider; import org.apache.syncope.client.console.commons.AMConstants; import org.apache.syncope.client.console.commons.DirectoryDataProvider; +import org.apache.syncope.client.console.commons.KeywordSearchEvent; import org.apache.syncope.client.console.panels.DirectoryPanel; import org.apache.syncope.client.console.panels.ModalDirectoryPanel; import org.apache.syncope.client.console.rest.AuthProfileRestClient; @@ -39,7 +41,6 @@ import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink; import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel; import org.apache.syncope.client.ui.commons.Constants; -import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; import org.apache.syncope.client.ui.commons.pages.BaseWebPage; import org.apache.syncope.common.lib.to.AuthProfileTO; import org.apache.syncope.common.lib.types.AMEntitlement; @@ -50,15 +51,13 @@ import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential; import org.apache.wicket.PageReference; import org.apache.wicket.ajax.AjaxRequestTarget; -import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy; +import org.apache.wicket.event.IEvent; import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; -import org.apache.wicket.extensions.wizard.WizardModel; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; -import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.ResourceModel; import org.apache.wicket.model.StringResourceModel; @@ -67,6 +66,8 @@ public class AuthProfileDirectoryPanel private static final long serialVersionUID = 2018518567549153364L; + private String keyword; + private final BaseModal authProfileModal; public AuthProfileDirectoryPanel( @@ -85,15 +86,9 @@ protected void onConfigure() { } }; authProfileModal.size(Modal.Size.Large); - authProfileModal.setWindowClosedCallback(target -> { - updateResultTable(target); - authProfileModal.show(false); - }); + setWindowClosedReloadCallback(authProfileModal, true); addOuterObject(authProfileModal); - addNewItemPanelBuilder(new CreateAuthProfileWizardBuilder(pageRef), true); - MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, AMEntitlement.AUTH_PROFILE_CREATE); - disableCheckBoxes(); initResultTable(); } @@ -442,6 +437,25 @@ public void onClick(final AjaxRequestTarget target, final AuthProfileTO ignore) return panel; } + @Override + public void onEvent(final IEvent event) { + if (event.getPayload() instanceof KeywordSearchEvent payload) { + keyword = payload.getKeyword(); + if (StringUtils.isNotBlank(keyword)) { + if (!Strings.CS.startsWith(keyword, "*")) { + keyword = "*" + keyword; + } + if (!Strings.CS.endsWith(keyword, "*")) { + keyword += "*"; + } + } + + updateResultTable(payload.getTarget()); + } else { + super.onEvent(event); + } + } + protected final class AuthProfileProvider extends DirectoryDataProvider { private static final long serialVersionUID = -185944053385660794L; @@ -454,12 +468,12 @@ private AuthProfileProvider(final int paginatorRows) { @Override public Iterator iterator(final long first, final long count) { int page = ((int) first / paginatorRows); - return restClient.list((page < 0 ? 0 : page) + 1, paginatorRows).iterator(); + return restClient.search(keyword, (page < 0 ? 0 : page) + 1, paginatorRows).iterator(); } @Override public long size() { - return restClient.count(); + return restClient.count(keyword); } @Override @@ -467,39 +481,4 @@ public IModel model(final AuthProfileTO object) { return new CompoundPropertyModel<>(object); } } - - private class CreateAuthProfileWizardBuilder extends AuthProfileWizardBuilder { - - private static final long serialVersionUID = -2478221092672979490L; - - private class NewAuthProfileStep extends AuthProfileWizardBuilder.Step { - - private static final long serialVersionUID = 6290450377240300418L; - - NewAuthProfileStep(final AuthProfileTO modelObject) { - super(modelObject); - - AjaxTextFieldPanel owner = new AjaxTextFieldPanel( - "bean", "owner", new PropertyModel<>(modelObject, "owner")); - owner.addRequiredLabel(); - addOrReplace(owner); - } - } - - CreateAuthProfileWizardBuilder(final PageReference pageRef) { - super(new AuthProfileTO(), new StepModel<>(), pageRef); - } - - @Override - protected WizardModel buildModelSteps(final AuthProfileTO modelObject, final WizardModel wizardModel) { - wizardModel.add(new NewAuthProfileStep(modelObject)); - return wizardModel; - } - - @Override - protected Serializable onApplyInternal(final AuthProfileTO modelObject) { - restClient.create(modelObject); - return modelObject; - } - } } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel.java index 475d1ad60d7..cfa57a04ea7 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel.java @@ -75,8 +75,6 @@ public AuthProfileItemDirectoryPanel( enableUtilityButton(); setFooterVisibility(false); - addNewItemPanelBuilder(new AuthProfileItemWizardBuilder(pageRef), true); - disableCheckBoxes(); initResultTable(); } @@ -90,9 +88,8 @@ public AuthProfileItemDirectoryPanel( @Override @SuppressWarnings("unchecked") public void onEvent(final IEvent event) { - if (event.getPayload() instanceof ExitEvent) { - AjaxRequestTarget target = ExitEvent.class.cast(event.getPayload()).getTarget(); - authProfileModal.close(target); + if (event.getPayload() instanceof ExitEvent exitEvent) { + authProfileModal.close(exitEvent.getTarget()); } else if (event.getPayload() instanceof final AjaxWizard.EditItemActionEvent payload) { payload.getTarget().ifPresent(actionTogglePanel::close); } @@ -194,12 +191,12 @@ protected Serializable onApplyInternal(final I modelObject) { List values = (List) wrapper.getPropertyValue("scratchCodes"); if (values != null) { List converted = values.stream().map(value -> { - if (value instanceof Integer) { - return Integer.class.cast(value); + if (value instanceof Integer integer) { + return integer; } - if (value instanceof String) { + if (value instanceof String string) { try { - return Integer.valueOf((String) value); + return Integer.valueOf(string); } catch (NumberFormatException e) { LOG.error("Could not convert to Integer: {}", value, e); } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfilePanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfilePanel.java new file mode 100644 index 00000000000..9c40fec62be --- /dev/null +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfilePanel.java @@ -0,0 +1,69 @@ +/* + * 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.client.console.authprofiles; + +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.client.console.commons.KeywordSearchEvent; +import org.apache.syncope.client.console.rest.AuthProfileRestClient; +import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; +import org.apache.wicket.PageReference; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.markup.html.form.AjaxButton; +import org.apache.wicket.event.Broadcast; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.panel.Panel; +import org.apache.wicket.model.Model; + +public class AuthProfilePanel extends Panel { + + private static final long serialVersionUID = -4716856239434102405L; + + public AuthProfilePanel(final String id, + final AuthProfileRestClient authProfileRestClient, + final PageReference pageRef) { + super(id); + + Model keywordModel = new Model<>(StringUtils.EMPTY); + + WebMarkupContainer searchBoxContainer = new WebMarkupContainer("searchBox"); + add(searchBoxContainer); + + Form form = new Form<>("form"); + searchBoxContainer.add(form); + + AjaxTextFieldPanel filter = new AjaxTextFieldPanel("filter", "filter", keywordModel, true); + form.add(filter.hideLabel().setOutputMarkupId(true).setRenderBodyOnly(true)); + + AjaxButton search = new AjaxButton("search") { + + private static final long serialVersionUID = 8390605330558248736L; + + @Override + protected void onSubmit(final AjaxRequestTarget target) { + send(AuthProfilePanel.this, Broadcast.DEPTH, new KeywordSearchEvent(target, keywordModel.getObject())); + } + }; + search.setOutputMarkupId(true); + form.add(search); + form.setDefaultButton(search); + + add(new AuthProfileDirectoryPanel("authProfiles", authProfileRestClient, pageRef).setOutputMarkupId(true)); + } +} diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java index 08475c41109..41a5ff99586 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java @@ -29,7 +29,7 @@ public abstract class AuthProfileWizardBuilder extends BaseAjaxWizardBuilder { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = -723891643398238220L; protected final StepModel model; @@ -46,7 +46,7 @@ protected WizardModel buildModelSteps(final T modelObject, final WizardModel wiz protected static class StepModel extends Model { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = -3300650579312254364L; private T initialModelObject; diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java index 87014925985..105b26fd4a8 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java @@ -97,10 +97,7 @@ public ClientAppDirectoryPanel( modal.addSubmitButton(); modal.size(Modal.Size.Large); - modal.setWindowClosedCallback(target -> { - updateResultTable(target); - modal.show(false); - }); + setWindowClosedReloadCallback(modal, true); setFooterVisibility(true); propertiesModal = new BaseModal<>(Constants.OUTER) { @@ -114,7 +111,7 @@ protected void onConfigure() { } }; propertiesModal.size(Modal.Size.Large); - propertiesModal.setWindowClosedCallback(target -> propertiesModal.show(false)); + setWindowClosedReloadCallback(propertiesModal); addOuterObject(propertiesModal); disableCheckBoxes(); @@ -231,8 +228,8 @@ protected void restore(final String json, final AjaxRequestTarget target) { historyModal.show(true); } - }, ActionLink.ActionType.VIEW_AUDIT_HISTORY, String.format("%s,%s", AMEntitlement.CLIENTAPP_READ, - IdRepoEntitlement.AUDIT_LIST)); + }, ActionLink.ActionType.VIEW_AUDIT_HISTORY, + "%s,%s".formatted(AMEntitlement.CLIENTAPP_READ, IdRepoEntitlement.AUDIT_LIST)); panel.add(new ActionLink<>() { diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java index 4694247dc7c..10044edcd27 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java @@ -27,6 +27,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.RandomStringUtils; @@ -40,8 +41,6 @@ import org.apache.syncope.client.console.rest.RealmRestClient; import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal; import org.apache.syncope.client.console.wicket.markup.html.form.AjaxSearchFieldPanel; -import org.apache.syncope.client.console.wicket.markup.html.form.BinaryFieldPanel; -import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel; import org.apache.syncope.client.console.wicket.markup.html.form.PolicyRenderer; import org.apache.syncope.client.ui.commons.Constants; import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior; @@ -50,6 +49,8 @@ import org.apache.syncope.client.ui.commons.markup.html.form.AjaxNumberFieldPanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; +import org.apache.syncope.client.ui.commons.markup.html.form.BinaryFieldPanel; +import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel; import org.apache.syncope.client.ui.commons.pages.BaseWebPage; import org.apache.syncope.client.ui.commons.panels.WizardModalPanel; import org.apache.syncope.client.ui.commons.wizards.AbstractModalPanelBuilder; @@ -59,6 +60,7 @@ import org.apache.syncope.common.lib.to.ClientAppTO; import org.apache.syncope.common.lib.to.OIDCRPClientAppTO; import org.apache.syncope.common.lib.to.RealmTO; +import org.apache.syncope.common.lib.to.SAML2SPClientAppTO; import org.apache.syncope.common.lib.types.ClientAppType; import org.apache.syncope.common.lib.types.LogoutType; import org.apache.syncope.common.lib.types.OIDCApplicationType; @@ -72,6 +74,7 @@ import org.apache.syncope.common.lib.types.PolicyType; import org.apache.syncope.common.lib.types.SAML2SPNameId; import org.apache.syncope.common.lib.types.XmlSecAlgorithm; +import org.apache.syncope.common.rest.api.service.SAML2IdPEntityService; import org.apache.wicket.Component; import org.apache.wicket.PageReference; import org.apache.wicket.ajax.AjaxRequestTarget; @@ -193,7 +196,7 @@ private class Profile extends AbstractModalPanel { protected Iterator getChoices(final String input) { return realmRestClient.search(fullRealmsTree ? RealmsUtils.buildBaseQuery() - : RealmsUtils.buildKeywordQuery(input)).getResult().stream(). + : RealmsUtils.buildNameQuery(input)).getResult().stream(). map(RealmTO::getFullPath).iterator(); } }; @@ -301,7 +304,7 @@ protected void onUpdate(final AjaxRequestTarget target) { AjaxTextFieldPanel clientSecret = new AjaxTextFieldPanel( "field", "clientSecret", new PropertyModel<>(clientAppTO, "clientSecret"), false); clientSecret.setChoices(List.of(RandomStringUtils.secure().nextNumeric(15))); - fields.add(clientSecret.setRequired(true)); + fields.add(clientSecret); AjaxTextFieldPanel idTokenIssuer = new AjaxTextFieldPanel( "field", "idTokenIssuer", new PropertyModel<>(clientAppTO, "idTokenIssuer"), false); @@ -462,6 +465,38 @@ public void setObject(final String object) { false); tokenEndpointAuthenticationMethod.setChoices(List.of(OIDCClientAuthenticationMethod.values())); fields.add(tokenEndpointAuthenticationMethod); + + AjaxTextFieldPanel accessTokenMaxTimeToLive = new AjaxTextFieldPanel( + "field", "accessTokenMaxTimeToLive", + new PropertyModel<>(clientAppTO, "accessTokenMaxTimeToLive"), false); + fields.add(accessTokenMaxTimeToLive); + + AjaxTextFieldPanel accessTokenTimeToKill = new AjaxTextFieldPanel( + "field", "accessTokenTimeToKill", new PropertyModel<>(clientAppTO, "accessTokenTimeToKill"), + false); + fields.add(accessTokenTimeToKill); + + AjaxNumberFieldPanel accessTokenMaxActiveTokens = new AjaxNumberFieldPanel.Builder() + .enableOnChange() + .build("field", "accessTokenMaxActiveTokens", Long.class, + new PropertyModel<>(clientAppTO, "accessTokenMaxActiveTokens")); + fields.add(accessTokenMaxActiveTokens); + + AjaxTextFieldPanel refreshTokenTimeToKill = new AjaxTextFieldPanel( + "field", "refreshTokenTimeToKill", + new PropertyModel<>(clientAppTO, "refreshTokenTimeToKill"), false); + fields.add(refreshTokenTimeToKill); + + AjaxNumberFieldPanel refreshTokenMaxActiveTokens = new AjaxNumberFieldPanel.Builder() + .enableOnChange() + .build("field", "refreshTokenMaxActiveTokens", Long.class, + new PropertyModel<>(clientAppTO, "refreshTokenMaxActiveTokens")); + fields.add(refreshTokenMaxActiveTokens); + + AjaxTextFieldPanel deviceTokenTimeToKill = new AjaxTextFieldPanel( + "field", "deviceTokenTimeToKill", new PropertyModel<>(clientAppTO, "deviceTokenTimeToKill"), + false); + fields.add(deviceTokenTimeToKill); break; case SAML2SP: @@ -469,6 +504,17 @@ public void setObject(final String object) { "field", "entityId", new PropertyModel<>(clientAppTO, "entityId"), false); fields.add(entityId.setRequired(true)); + fields.add(new AjaxTextFieldPanel("field", "idp", new PropertyModel<>(clientAppTO, "idp") { + + private static final long serialVersionUID = -1476737401674143862L; + + @Override + public String getObject() { + return Optional.ofNullable(((SAML2SPClientAppTO) clientAppTO).getIdp()). + orElse(SAML2IdPEntityService.DEFAULT_OWNER); + } + }, false)); + fields.add(new AjaxTextFieldPanel("field", "metadataLocation", new PropertyModel<>(clientAppTO, "metadataLocation"), false).setRequired(true)); diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMConstants.java b/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMConstants.java index 3f6af5ab8de..fcd7b3fc580 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMConstants.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMConstants.java @@ -36,8 +36,6 @@ public final class AMConstants { public static final String PREF_SAML2_IDP_ENTITY_PAGINATOR_ROWS = "saml2idpentity.properties.paginator.rows"; - public static final String PREF_SAML2_SP_ENTITY_PAGINATOR_ROWS = "saml2spentity.properties.paginator.rows"; - public static final String PREF_AUTHPROFILE_PAGINATOR_ROWS = "authprofile.paginator.rows"; public static final String PREF_AUTHPROFILE_IMPERSONATED_PAGINATOR_ROWS = "authprofile.impersonated.paginator.rows"; diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/SRA.java b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/SRA.java index 43bf65ab19a..3904aff8f78 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/SRA.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/SRA.java @@ -23,13 +23,13 @@ import java.util.List; import org.apache.syncope.client.console.BookmarkablePageLinkBuilder; import org.apache.syncope.client.console.SyncopeConsoleSession; -import org.apache.syncope.client.console.annotations.AMPage; import org.apache.syncope.client.console.panels.AMSessionPanel; import org.apache.syncope.client.console.panels.SRARouteDirectoryPanel; import org.apache.syncope.client.console.panels.SRAStatisticsPanel; import org.apache.syncope.client.console.rest.SRARouteRestClient; import org.apache.syncope.client.console.rest.SRASessionRestClient; import org.apache.syncope.client.ui.commons.Constants; +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.types.AMEntitlement; @@ -82,12 +82,9 @@ public void onClick(final AjaxRequestTarget target) { body.add(push); WebMarkupContainer content = new WebMarkupContainer("content"); - content.setOutputMarkupId(true); - AjaxBootstrapTabbedPanel tabbedPanel = - new AjaxBootstrapTabbedPanel<>("tabbedPanel", buildTabList(instances)); - content.add(tabbedPanel); + body.add(content.setOutputMarkupId(true)); - body.add(content); + content.add(new AjaxBootstrapTabbedPanel<>("tabbedPanel", buildTabList(instances))); } private List buildTabList(final List instances) { 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 54b8ea45dd7..9f7f0e4fb67 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 @@ -29,26 +29,29 @@ import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import org.apache.syncope.client.console.BookmarkablePageLinkBuilder; import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.SyncopeWebApplication; -import org.apache.syncope.client.console.annotations.AMPage; -import org.apache.syncope.client.console.authprofiles.AuthProfileDirectoryPanel; +import org.apache.syncope.client.console.authprofiles.AuthProfilePanel; import org.apache.syncope.client.console.clientapps.ClientApps; import org.apache.syncope.client.console.panels.AMSessionPanel; import org.apache.syncope.client.console.panels.AttrRepoDirectoryPanel; import org.apache.syncope.client.console.panels.AuthModuleDirectoryPanel; import org.apache.syncope.client.console.panels.OIDC; -import org.apache.syncope.client.console.panels.SAML2; +import org.apache.syncope.client.console.panels.SAML2IdPEntityDirectoryPanel; import org.apache.syncope.client.console.panels.WAConfigDirectoryPanel; import org.apache.syncope.client.console.panels.WAPushModalPanel; import org.apache.syncope.client.console.rest.AttrRepoRestClient; import org.apache.syncope.client.console.rest.AuthModuleRestClient; import org.apache.syncope.client.console.rest.AuthProfileRestClient; +import org.apache.syncope.client.console.rest.ClientAppRestClient; +import org.apache.syncope.client.console.rest.SAML2IdPEntityRestClient; import org.apache.syncope.client.console.rest.WAConfigRestClient; import org.apache.syncope.client.console.rest.WASessionRestClient; import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal; import org.apache.syncope.client.lib.WebClientBuilder; +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.types.AMEntitlement; @@ -82,6 +85,12 @@ public class WA extends BasePage { @SpringBean protected AttrRepoRestClient attrRepoRestClient; + @SpringBean + protected SAML2IdPEntityRestClient saml2IdPEntityRestClient; + + @SpringBean + protected ClientAppRestClient clientAppRestClient; + @SpringBean protected ServiceOps serviceOps; @@ -118,16 +127,13 @@ public void onClick(final AjaxRequestTarget target) { body.add(push); WebMarkupContainer content = new WebMarkupContainer("content"); - content.setOutputMarkupId(true); - AjaxBootstrapTabbedPanel tabbedPanel = - new AjaxBootstrapTabbedPanel<>("tabbedPanel", buildTabList(instances)); - content.add(tabbedPanel); + body.add(content.setOutputMarkupId(true)); - body.add(content); + content.add(new AjaxBootstrapTabbedPanel<>("tabbedPanel", buildTabList(instances))); if (!instances.isEmpty()) { - String actuatorEndpoint = StringUtils.appendIfMissing( - instances.getFirst().getAddress(), "/") + "actuator/env"; + String actuatorEndpoint = Strings.CS.appendIfMissing( + instances.getFirst().getAddress(), "/") + "actuator/env"; try { Response response = WebClientBuilder.build(actuatorEndpoint, SyncopeWebApplication.get().getAnonymousUser(), @@ -142,7 +148,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 = StringUtils.removeEnd(prefix.get("value").asText(), "/"); + waPrefix = Strings.CS.removeEnd(prefix.get("value").asText(), "/"); } } } @@ -154,7 +160,7 @@ public void onClick(final AjaxRequestTarget target) { } if (StringUtils.isBlank(waPrefix)) { - waPrefix = StringUtils.removeEnd(instances.getFirst().getAddress(), "/"); + waPrefix = Strings.CS.removeEnd(instances.getFirst().getAddress(), "/"); } } } @@ -204,7 +210,8 @@ public Panel getPanel(final String panelId) { @Override public Panel getPanel(final String panelId) { - return new SAML2(panelId, waPrefix, getPageReference()); + return new SAML2IdPEntityDirectoryPanel( + panelId, saml2IdPEntityRestClient, clientAppRestClient, waPrefix, getPageReference()); } }); @@ -237,7 +244,7 @@ public Panel getPanel(final String panelId) { @Override public Panel getPanel(final String panelId) { - return new AuthProfileDirectoryPanel(panelId, authProfileRestClient, getPageReference()); + return new AuthProfilePanel(panelId, authProfileRestClient, getPageReference()); } }); } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AMSessionDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AMSessionDirectoryPanel.java index 9db0f0b7136..88d118df6a8 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AMSessionDirectoryPanel.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AMSessionDirectoryPanel.java @@ -92,7 +92,7 @@ protected void onConfigure() { } }; viewModal.size(Modal.Size.Extra_large); - viewModal.setWindowClosedCallback(target -> viewModal.show(false)); + setWindowClosedReloadCallback(viewModal); addOuterObject(viewModal); initResultTable(); @@ -123,7 +123,7 @@ protected ActionsPanel getActions(final IModel model) { public void onClick(final AjaxRequestTarget target, final AMSession ignore) { viewModal.header(new ResourceModel("details")); target.add(viewModal.setContent( - new JsonEditorPanel(viewModal, Model.of(model.getObject().getJson()), true, pageRef))); + new JsonEditorPanel(viewModal, Model.of(model.getObject().getJson()), true, pageRef))); viewModal.show(true); } }, ActionLink.ActionType.VIEW, listEntitlement); @@ -167,8 +167,7 @@ protected String paginatorRowsKey() { @Override public void onEvent(final IEvent event) { - if (event.getPayload() instanceof AMSessionSearchEvent) { - AMSessionSearchEvent payload = AMSessionSearchEvent.class.cast(event.getPayload()); + if (event.getPayload() instanceof AMSessionSearchEvent payload) { keyword = payload.getKeyword(); updateResultTable(payload.getTarget()); diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel.java index 63bd50d47ea..a251eb8383a 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel.java @@ -183,8 +183,8 @@ protected void restore(final String json, final AjaxRequestTarget target) { historyModal.show(true); } - }, ActionLink.ActionType.VIEW_AUDIT_HISTORY, String.format("%s,%s", AMEntitlement.ATTR_REPO_READ, - IdRepoEntitlement.AUDIT_LIST)); + }, ActionLink.ActionType.VIEW_AUDIT_HISTORY, + "%s,%s".formatted(AMEntitlement.ATTR_REPO_READ, IdRepoEntitlement.AUDIT_LIST)); panel.add(new ActionLink<>() { diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.java index 70d25d0b95b..e6934448879 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.java @@ -187,8 +187,8 @@ protected void restore(final String json, final AjaxRequestTarget target) { historyModal.show(true); } - }, ActionLink.ActionType.VIEW_AUDIT_HISTORY, String.format("%s,%s", AMEntitlement.AUTH_MODULE_READ, - IdRepoEntitlement.AUDIT_LIST)); + }, ActionLink.ActionType.VIEW_AUDIT_HISTORY, + "%s,%s".formatted(AMEntitlement.AUTH_MODULE_READ, IdRepoEntitlement.AUDIT_LIST)); panel.add(new ActionLink<>() { 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 4bc40ab029f..b71c26bf7a5 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 @@ -21,9 +21,11 @@ import com.fasterxml.jackson.databind.json.JsonMapper; import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; import java.io.IOException; -import java.util.concurrent.atomic.AtomicReference; +import java.util.Optional; +import org.apache.commons.lang3.mutable.Mutable; import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.rest.OIDCJWKSRestClient; +import org.apache.syncope.client.console.rest.WAConfigRestClient; import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal; import org.apache.syncope.client.console.wicket.markup.html.form.JsonEditorPanel; import org.apache.syncope.client.ui.commons.Constants; @@ -54,6 +56,11 @@ public class OIDC extends Panel { @SpringBean protected OIDCJWKSRestClient oidcJWKSRestClient; + @SpringBean + protected WAConfigRestClient waConfigRestClient; + + protected final BaseModal generateModal = new BaseModal<>("generateModal"); + protected final BaseModal viewModal = new BaseModal<>("viewModal") { private static final long serialVersionUID = 389935548143327858L; @@ -75,14 +82,14 @@ public OIDC(final String id, final String waPrefix, final PageReference pageRef) super(id); setOutputMarkupId(true); - add(viewModal); - viewModal.size(Modal.Size.Extra_large); - viewModal.setWindowClosedCallback(target -> viewModal.show(false)); - WebMarkupContainer container = new WebMarkupContainer("container"); add(container.setOutputMarkupId(true)); - AtomicReference oidcjwksto = oidcJWKSRestClient.get(); + Mutable oidcjwksto = oidcJWKSRestClient.get(); + + add(viewModal); + viewModal.size(Modal.Size.Extra_large); + viewModal.setWindowClosedCallback(target -> viewModal.show(false)); view = new AjaxLink<>("view") { @@ -96,7 +103,7 @@ public void onClick(final AjaxRequestTarget target) { writeValueAsString(MAPPER.readTree(oidcjwksto.get().getJson())); } catch (IOException e) { LOG.error("Could not pretty-print", e); - pretty = oidcjwksto.get().getJson(); + pretty = Optional.ofNullable(oidcjwksto.get()).map(OIDCJWKSTO::getJson).orElse(null); } viewModal.header(Model.of("JSON Web Key Sets")); @@ -123,18 +130,10 @@ protected void onComponentTag(final ComponentTag tag) { @Override public void onClick(final AjaxRequestTarget target) { - try { - oidcjwksto.set(oidcJWKSRestClient.generate()); - generate.setEnabled(false); - view.setEnabled(true); - - SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); - target.add(container); - } catch (Exception e) { - LOG.error("While generating OIDC JWKS", e); - SyncopeConsoleSession.get().onException(e); - } - ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target); + generateModal.header(Model.of("Generate JSON Web Key Sets")); + target.add(generateModal.setContent(new OIDCJWKSGenerationPanel( + oidcJWKSRestClient, waConfigRestClient, generateModal, pageRef))); + generateModal.show(true); } @Override @@ -158,7 +157,7 @@ protected void onComponentTag(final ComponentTag tag) { public void onClick(final AjaxRequestTarget target) { try { oidcJWKSRestClient.delete(); - oidcjwksto.set(null); + oidcjwksto.setValue(null); generate.setEnabled(true); view.setEnabled(false); @@ -184,6 +183,17 @@ protected void onComponentTag(final ComponentTag tag) { container.add(delete.setOutputMarkupId(true)); MetaDataRoleAuthorizationStrategy.authorize(delete, ENABLE, AMEntitlement.OIDC_JWKS_DELETE); + generateModal.addSubmitButton(); + add(generateModal); + generateModal.setWindowClosedCallback(target -> { + oidcjwksto.setValue(oidcJWKSRestClient.get().get()); + view.setEnabled(oidcjwksto.get() != null); + delete.setEnabled(oidcjwksto.get() != null); + + target.add(container); + generateModal.show(false); + }); + String wellKnownURI = waPrefix + "/oidc/.well-known/openid-configuration"; container.add(new ExternalLink("wellKnownURI", wellKnownURI, wellKnownURI)); } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/OIDCJWKSGenerationPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/OIDCJWKSGenerationPanel.java new file mode 100644 index 00000000000..d5d18f96eb2 --- /dev/null +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/OIDCJWKSGenerationPanel.java @@ -0,0 +1,117 @@ +/* + * 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.client.console.panels; + +import java.util.List; +import org.apache.syncope.client.console.SyncopeConsoleSession; +import org.apache.syncope.client.console.rest.OIDCJWKSRestClient; +import org.apache.syncope.client.console.rest.WAConfigRestClient; +import org.apache.syncope.client.console.wicket.ajax.form.IndicatorAjaxEventBehavior; +import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal; +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.AjaxNumberFieldPanel; +import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; +import org.apache.syncope.client.ui.commons.pages.BaseWebPage; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.OIDCJWKSTO; +import org.apache.wicket.PageReference; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.model.Model; + +public class OIDCJWKSGenerationPanel extends AbstractModalPanel { + + private static final long serialVersionUID = -3372006007594607067L; + + protected final OIDCJWKSRestClient oidcJWKSRestClient; + + protected final Model jwksKeyIdM; + + protected final Model jwksTypeM; + + protected final Model jwksKeySizeM; + + public OIDCJWKSGenerationPanel( + final OIDCJWKSRestClient oidcJWKSRestClient, + final WAConfigRestClient waConfigRestClient, + final BaseModal modal, + final PageReference pageRef) { + + super(modal, pageRef); + this.oidcJWKSRestClient = oidcJWKSRestClient; + + jwksKeyIdM = Model.of("syncope"); + try { + jwksKeyIdM.setObject(waConfigRestClient.get("cas.authn.oidc.jwks.core.jwks-key-id").getValues().getFirst()); + } catch (SyncopeClientException e) { + LOG.error("While reading cas.authn.oidc.jwks.core.jwks-key-id", e); + } + add(new AjaxTextFieldPanel("jwksKeyId", "jwksKeyId", jwksKeyIdM).setRequired(true)); + + jwksTypeM = Model.of("rsa"); + try { + jwksTypeM.setObject(waConfigRestClient.get("cas.authn.oidc.jwks.core.jwks-type").getValues().getFirst()); + } catch (SyncopeClientException e) { + LOG.error("While reading cas.authn.oidc.jwks.core.jwks-type", e); + } + AjaxDropDownChoicePanel jwksType = new AjaxDropDownChoicePanel<>("jwksType", "jwksType", jwksTypeM). + setChoices(List.of("rsa", "ec")); + add(jwksType.setRequired(true)); + + jwksKeySizeM = Model.of(2048); + try { + jwksKeySizeM.setObject(Integer.valueOf( + waConfigRestClient.get("cas.authn.oidc.jwks.core.jwks-key-size").getValues().getFirst())); + } catch (SyncopeClientException e) { + LOG.error("While reading cas.authn.oidc.jwks.core.jwks-key-size", e); + } + AjaxNumberFieldPanel jwksKeySize = new AjaxNumberFieldPanel.Builder().step(128). + build("jwksKeySize", "jwksKeySize", Integer.class, jwksKeySizeM); + add(jwksKeySize.setRequired(true)); + + jwksType.add(new IndicatorAjaxEventBehavior(Constants.ON_CHANGE) { + + private static final long serialVersionUID = -4255753643957306394L; + + @Override + protected void onEvent(final AjaxRequestTarget target) { + if ("ec".equals(jwksTypeM.getObject())) { + jwksKeySizeM.setObject(256); + } else { + jwksKeySizeM.setObject(2048); + } + target.add(jwksKeySize); + } + }); + } + + @Override + public void onSubmit(final AjaxRequestTarget target) { + try { + oidcJWKSRestClient.generate(jwksKeyIdM.getObject(), jwksTypeM.getObject(), jwksKeySizeM.getObject()); + + SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); + modal.close(target); + } catch (Exception e) { + LOG.error("While generating OIDC JWKS", e); + SyncopeConsoleSession.get().onException(e); + } + ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target); + } +} diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SAML2.java b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SAML2.java deleted file mode 100644 index 04fa7d2c50a..00000000000 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SAML2.java +++ /dev/null @@ -1,81 +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.client.console.panels; - -import de.agilecoders.wicket.core.markup.html.bootstrap.tabs.AjaxBootstrapTabbedPanel; -import java.util.ArrayList; -import java.util.List; -import org.apache.syncope.client.console.SyncopeConsoleSession; -import org.apache.syncope.client.console.rest.SAML2IdPEntityRestClient; -import org.apache.syncope.client.console.rest.SAML2SPEntityRestClient; -import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.common.lib.types.AMEntitlement; -import org.apache.wicket.PageReference; -import org.apache.wicket.extensions.markup.html.tabs.AbstractTab; -import org.apache.wicket.extensions.markup.html.tabs.ITab; -import org.apache.wicket.markup.html.panel.Panel; -import org.apache.wicket.model.Model; -import org.apache.wicket.spring.injection.annot.SpringBean; - -public class SAML2 extends Panel { - - private static final long serialVersionUID = 7093557205333650002L; - - @SpringBean - protected SAML2IdPEntityRestClient saml2IdPEntityRestClient; - - @SpringBean - protected SAML2SPEntityRestClient saml2SPEntityRestClient; - - public SAML2(final String id, final String waPrefix, final PageReference pageRef) { - super(id); - - add(new AjaxBootstrapTabbedPanel<>("saml2", buildTabList(waPrefix, pageRef))); - } - - protected List buildTabList(final String waPrefix, final PageReference pageRef) { - List tabs = new ArrayList<>(); - - if (SyncopeConsoleSession.get().owns(AMEntitlement.SAML2_IDP_ENTITY_LIST, SyncopeConstants.ROOT_REALM)) { - tabs.add(new AbstractTab(Model.of("Identity Provider")) { - - private static final long serialVersionUID = 5211692813425391144L; - - @Override - public Panel getPanel(final String panelId) { - return new SAML2IdPEntityDirectoryPanel(panelId, saml2IdPEntityRestClient, waPrefix, pageRef); - } - }); - } - - if (SyncopeConsoleSession.get().owns(AMEntitlement.SAML2_SP_ENTITY_LIST, SyncopeConstants.ROOT_REALM)) { - tabs.add(new AbstractTab(Model.of("Service Provider")) { - - private static final long serialVersionUID = 5211692813425391144L; - - @Override - public Panel getPanel(final String panelId) { - return new SAML2SPEntityDirectoryPanel(panelId, saml2SPEntityRestClient, waPrefix, pageRef); - } - }); - } - - return tabs; - } -} diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPEntityDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPEntityDirectoryPanel.java index 2ec1e9ea21c..279fa58bb04 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPEntityDirectoryPanel.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPEntityDirectoryPanel.java @@ -23,18 +23,25 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; +import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.commons.AMConstants; import org.apache.syncope.client.console.commons.DirectoryDataProvider; import org.apache.syncope.client.console.commons.SortableDataProviderComparator; +import org.apache.syncope.client.console.pages.BasePage; import org.apache.syncope.client.console.panels.SAML2IdPEntityDirectoryPanel.SAML2IdPEntityProvider; +import org.apache.syncope.client.console.rest.ClientAppRestClient; import org.apache.syncope.client.console.rest.SAML2IdPEntityRestClient; import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink; import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel; import org.apache.syncope.client.console.wizards.SAML2IdPEntityWizardBuilder; import org.apache.syncope.client.ui.commons.Constants; import org.apache.syncope.client.ui.commons.wizards.AjaxWizard; +import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.to.SAML2IdPEntityTO; +import org.apache.syncope.common.lib.to.SAML2SPClientAppTO; import org.apache.syncope.common.lib.types.AMEntitlement; +import org.apache.syncope.common.lib.types.ClientAppType; +import org.apache.syncope.common.rest.api.service.SAML2IdPEntityService; import org.apache.wicket.PageReference; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.event.Broadcast; @@ -43,10 +50,12 @@ import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; import org.apache.wicket.markup.ComponentTag; +import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.ExternalLink; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.IModel; +import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.StringResourceModel; @@ -55,26 +64,54 @@ public class SAML2IdPEntityDirectoryPanel extends DirectoryPanel< private static final long serialVersionUID = -6535332920023200166L; + private static ExternalLink metadataLink(final String componentId, final String url) { + return new ExternalLink(componentId, Model.of(url), Model.of(url)) { + + private static final long serialVersionUID = -1919646533527005367L; + + @Override + protected void onComponentTag(final ComponentTag tag) { + super.onComponentTag(tag); + + tag.setName("a"); + if (url.startsWith("http")) { + tag.put("href", getDefaultModelObject().toString()); + tag.put("target", "_blank"); + } + } + }; + } + + private final LoadableDetachableModel> clientApps; + private final String metadataURL; public SAML2IdPEntityDirectoryPanel( final String id, final SAML2IdPEntityRestClient restClient, + final ClientAppRestClient clientAppRestClient, final String waPrefix, final PageReference pageRef) { super(id, restClient, pageRef); + + clientApps = new LoadableDetachableModel<>() { + + private static final long serialVersionUID = 7172461137064525667L; + + @Override + protected List load() { + return clientAppRestClient.list(ClientAppType.SAML2SP); + } + }; + this.metadataURL = waPrefix + "/idp/metadata"; disableCheckBoxes(); modal.size(Modal.Size.Large); modal.addSubmitButton(); - - modal.setWindowClosedCallback(target -> { - updateResultTable(target); - modal.show(false); - }); + setWindowClosedReloadCallback(modal, true); addNewItemPanelBuilder(new SAML2IdPEntityWizardBuilder(new SAML2IdPEntityTO(), restClient, pageRef), false); @@ -98,24 +135,16 @@ public void populateItem( final String componentId, final IModel rowModel) { - cellItem.add(new ExternalLink( - componentId, - Model.of(metadataURL), - Model.of(metadataURL)) { - - private static final long serialVersionUID = -1919646533527005367L; - - @Override - protected void onComponentTag(final ComponentTag tag) { - super.onComponentTag(tag); - - tag.setName("a"); - if (metadataURL.startsWith("http")) { - tag.put("href", getDefaultModelObject().toString()); - tag.put("target", "_blank"); - } - } - }); + if (SAML2IdPEntityService.DEFAULT_OWNER.equals(rowModel.getObject().getKey())) { + cellItem.add(metadataLink(componentId, metadataURL)); + } else { + clientApps.getObject().stream(). + filter(app -> rowModel.getObject().getKey().equals(app.getIdp())). + findFirst().ifPresentOrElse( + app -> cellItem.add(metadataLink( + componentId, metadataURL + "?service=" + app.getClientAppId())), + () -> cellItem.add(new Label(componentId, Model.of()))); + } } }); @@ -138,6 +167,26 @@ public void onClick(final AjaxRequestTarget target, final SAML2IdPEntityTO ignor } }, ActionLink.ActionType.EDIT, AMEntitlement.SAML2_IDP_ENTITY_SET); + if (!SAML2IdPEntityService.DEFAULT_OWNER.equals(model.getObject().getKey())) { + panel.add(new ActionLink<>() { + + private static final long serialVersionUID = -3722207913631435501L; + + @Override + public void onClick(final AjaxRequestTarget target, final SAML2IdPEntityTO ignore) { + try { + restClient.delete(model.getObject().getKey()); + SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); + target.add(container); + } catch (SyncopeClientException e) { + LOG.error("While deleting IdP {}", model.getObject().getKey(), e); + SyncopeConsoleSession.get().onException(e); + } + ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target); + } + }, ActionLink.ActionType.DELETE, AMEntitlement.SAML2_IDP_ENTITY_DELETE, true); + } + return panel; } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SAML2SPEntityDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SAML2SPEntityDirectoryPanel.java deleted file mode 100644 index 10f99409127..00000000000 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SAML2SPEntityDirectoryPanel.java +++ /dev/null @@ -1,213 +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.client.console.panels; - -import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import org.apache.syncope.client.console.SyncopeConsoleSession; -import org.apache.syncope.client.console.commons.AMConstants; -import org.apache.syncope.client.console.commons.DirectoryDataProvider; -import org.apache.syncope.client.console.commons.SortableDataProviderComparator; -import org.apache.syncope.client.console.panels.SAML2SPEntityDirectoryPanel.SAML2SPEntityProvider; -import org.apache.syncope.client.console.rest.SAML2SPEntityRestClient; -import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink; -import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel; -import org.apache.syncope.client.console.wizards.SAML2SPEntityWizardBuilder; -import org.apache.syncope.client.ui.commons.Constants; -import org.apache.syncope.client.ui.commons.pages.BaseWebPage; -import org.apache.syncope.client.ui.commons.wizards.AjaxWizard; -import org.apache.syncope.common.lib.to.SAML2SPEntityTO; -import org.apache.syncope.common.lib.types.AMEntitlement; -import org.apache.wicket.PageReference; -import org.apache.wicket.ajax.AjaxRequestTarget; -import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy; -import org.apache.wicket.event.Broadcast; -import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator; -import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn; -import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; -import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; -import org.apache.wicket.markup.ComponentTag; -import org.apache.wicket.markup.html.link.ExternalLink; -import org.apache.wicket.markup.repeater.Item; -import org.apache.wicket.model.CompoundPropertyModel; -import org.apache.wicket.model.IModel; -import org.apache.wicket.model.Model; -import org.apache.wicket.model.StringResourceModel; - -public class SAML2SPEntityDirectoryPanel extends DirectoryPanel< - SAML2SPEntityTO, SAML2SPEntityTO, SAML2SPEntityProvider, SAML2SPEntityRestClient> { - - private static final long serialVersionUID = -3890622383545171017L; - - private final String waPrefix; - - public SAML2SPEntityDirectoryPanel( - final String id, - final SAML2SPEntityRestClient restClient, - final String waPrefix, - final PageReference pageRef) { - - super(id, restClient, pageRef); - this.waPrefix = waPrefix; - - disableCheckBoxes(); - - modal.size(Modal.Size.Large); - modal.addSubmitButton(); - - modal.setWindowClosedCallback(target -> { - updateResultTable(target); - modal.show(false); - }); - - addNewItemPanelBuilder(new SAML2SPEntityWizardBuilder(new SAML2SPEntityTO(), restClient, pageRef), true); - - MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, AMEntitlement.SAML2_SP_ENTITY_SET); - - initResultTable(); - } - - @Override - protected List> getColumns() { - List> columns = new ArrayList<>(); - - columns.add(new PropertyColumn<>( - new StringResourceModel(Constants.KEY_FIELD_NAME, this), - Constants.KEY_FIELD_NAME, Constants.KEY_FIELD_NAME)); - - columns.add(new AbstractColumn<>(Model.of("URL")) { - - private static final long serialVersionUID = -7226955670801277153L; - - @Override - public void populateItem( - final Item> cellItem, - final String componentId, - final IModel rowModel) { - - String metadataURL = waPrefix + "/sp/" + rowModel.getObject().getKey() + "/metadata"; - cellItem.add(new ExternalLink( - componentId, - Model.of(metadataURL), - Model.of(metadataURL)) { - - private static final long serialVersionUID = -1919646533527005367L; - - @Override - protected void onComponentTag(final ComponentTag tag) { - super.onComponentTag(tag); - - tag.setName("a"); - if (metadataURL.startsWith("http")) { - tag.put("href", getDefaultModelObject().toString()); - tag.put("target", "_blank"); - } - } - }); - } - }); - - return columns; - } - - @Override - protected ActionsPanel getActions(final IModel model) { - ActionsPanel panel = super.getActions(model); - - panel.add(new ActionLink<>() { - - private static final long serialVersionUID = -3722207913631435501L; - - @Override - public void onClick(final AjaxRequestTarget target, final SAML2SPEntityTO ignore) { - send(SAML2SPEntityDirectoryPanel.this, Broadcast.EXACT, - new AjaxWizard.EditItemActionEvent<>( - restClient.get(model.getObject().getKey()), target)); - } - }, ActionLink.ActionType.EDIT, AMEntitlement.SAML2_SP_ENTITY_SET); - - panel.add(new ActionLink<>() { - - private static final long serialVersionUID = -3722207913631435501L; - - @Override - public void onClick(final AjaxRequestTarget target, final SAML2SPEntityTO ignore) { - try { - restClient.delete(model.getObject().getKey()); - - SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); - target.add(container); - } catch (Exception e) { - LOG.error("While deleting {}", model.getObject().getKey(), e); - SyncopeConsoleSession.get().onException(e); - } - ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target); - } - }, ActionLink.ActionType.DELETE, AMEntitlement.AUTH_MODULE_DELETE, true); - - return panel; - } - - @Override - protected Collection getBatches() { - return List.of(); - } - - @Override - protected SAML2SPEntityProvider dataProvider() { - return new SAML2SPEntityProvider(rows); - } - - @Override - protected String paginatorRowsKey() { - return AMConstants.PREF_SAML2_SP_ENTITY_PAGINATOR_ROWS; - } - - protected final class SAML2SPEntityProvider extends DirectoryDataProvider { - - private static final long serialVersionUID = 5282134321828253058L; - - private final SortableDataProviderComparator comparator; - - public SAML2SPEntityProvider(final int paginatorRows) { - super(paginatorRows); - comparator = new SortableDataProviderComparator<>(this); - } - - @Override - public Iterator iterator(final long first, final long count) { - List sps = restClient.list(); - sps.sort(comparator); - return sps.subList((int) first, (int) first + (int) count).iterator(); - } - - @Override - public long size() { - return restClient.list().size(); - } - - @Override - public IModel model(final SAML2SPEntityTO metadata) { - return new CompoundPropertyModel<>(metadata); - } - } -} diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SRARouteDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SRARouteDirectoryPanel.java index a116ccf53fc..280026d2e8f 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SRARouteDirectoryPanel.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SRARouteDirectoryPanel.java @@ -61,13 +61,10 @@ public SRARouteDirectoryPanel(final String id, final SRARouteRestClient restClie modal.size(Modal.Size.Large); modal.addSubmitButton(); - - modal.setWindowClosedCallback(target -> { - updateResultTable(target); - modal.show(false); - }); + setWindowClosedReloadCallback(modal, true); addNewItemPanelBuilder(new SRARouteWizardBuilder(new SRARouteTO(), restClient, pageRef), true); + initResultTable(); } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/WAConfigDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/WAConfigDirectoryPanel.java index 36c160f67b5..bd008fdf79a 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/WAConfigDirectoryPanel.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/WAConfigDirectoryPanel.java @@ -78,7 +78,11 @@ public ActionsPanel getActions(final IModel model) { public void onClick(final AjaxRequestTarget target, final Attr ignore) { target.add(modal); modal.header(new StringResourceModel("any.edit")); - modal.setContent(new WAConfigModalPanel(modal, model.getObject(), AjaxWizard.Mode.EDIT, pageRef)); + modal.setContent(new WAConfigModalPanel( + modal, + ((WAConfigRestClient) restClient).get(model.getObject().getSchema()), + AjaxWizard.Mode.EDIT, + pageRef)); modal.show(true); } }, ActionLink.ActionType.EDIT, AMEntitlement.WA_CONFIG_SET); diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.java index 2995d794ca8..ca413343d8f 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.java @@ -26,7 +26,6 @@ import org.apache.syncope.client.console.rest.AttrRepoRestClient; import org.apache.syncope.client.console.rest.PolicyRestClient; import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal; -import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel; import org.apache.syncope.client.ui.commons.Constants; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel; @@ -34,6 +33,7 @@ import org.apache.syncope.client.ui.commons.markup.html.form.AjaxNumberFieldPanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; +import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel; import org.apache.syncope.client.ui.commons.pages.BaseWebPage; import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO; import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf.PrincipalAttrRepoMergingStrategy; @@ -79,7 +79,7 @@ public AttrReleasePolicyModalPanel( add(new AjaxGridFieldPanel<>( "releaseAttrs", "releaseAttrs", - new PropertyModel<>(model.getObject().getConf(), "releaseAttrs"))); + new PropertyModel<>(model.getObject().getConf(), "releaseAttrs"), false)); AjaxTextFieldPanel allowedAttr = new AjaxTextFieldPanel("panel", "allowedAttrs", new Model<>()); add(new MultiFieldPanel.Builder( diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AuthPolicyModalPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AuthPolicyModalPanel.java index 76833d35742..e4edc93d09e 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AuthPolicyModalPanel.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AuthPolicyModalPanel.java @@ -87,6 +87,12 @@ public AuthPolicyModalPanel( new PropertyModel<>(model.getObject().getConf(), "bypassEnabled"), false)); + add(new AjaxCheckBoxPanel( + "forceMfaExecution", + "forceMfaExecution", + new PropertyModel<>(model.getObject().getConf(), "forceMfaExecution"), + false)); + add(new AjaxTextFieldPanel( "bypassPrincipalAttributeName", "bypassPrincipalAttributeName", @@ -98,6 +104,12 @@ public AuthPolicyModalPanel( "bypassPrincipalAttributeValue", new PropertyModel<>(model.getObject().getConf(), "bypassPrincipalAttributeValue"), false)); + + add(new AjaxCheckBoxPanel( + "bypassTrustedDeviceEnabled", + "bypassTrustedDeviceEnabled", + new PropertyModel<>(model.getObject().getConf(), "bypassTrustedDeviceEnabled"), + false)); } @Override diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AuthProfileRestClient.java b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AuthProfileRestClient.java index d2b062bc114..99f8cb18ce8 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AuthProfileRestClient.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AuthProfileRestClient.java @@ -20,28 +20,29 @@ import java.util.List; import org.apache.syncope.common.lib.to.AuthProfileTO; +import org.apache.syncope.common.rest.api.beans.AuthProfileQuery; import org.apache.syncope.common.rest.api.service.AuthProfileService; public class AuthProfileRestClient extends BaseRestClient { private static final long serialVersionUID = -7379778542101161274L; - public long count() { - return getService(AuthProfileService.class).list(1, 1).getTotalCount(); + public long count(final String keyword) { + return getService(AuthProfileService.class). + search(new AuthProfileQuery.Builder().page(1).size(0).keyword(keyword).build()). + getTotalCount(); } - public List list(final int page, final int size) { - return getService(AuthProfileService.class).list(page, size).getResult(); + public List search(final String keyword, final int page, final int size) { + return getService(AuthProfileService.class). + search(new AuthProfileQuery.Builder().page(page).size(size).keyword(keyword).build()). + getResult(); } public AuthProfileTO read(final String key) { return getService(AuthProfileService.class).read(key); } - public void create(final AuthProfileTO authProfile) { - getService(AuthProfileService.class).create(authProfile); - } - public void update(final AuthProfileTO authProfile) { getService(AuthProfileService.class).update(authProfile); } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/OIDCJWKSRestClient.java b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/OIDCJWKSRestClient.java index 8743bd71106..70a94e40309 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/OIDCJWKSRestClient.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/OIDCJWKSRestClient.java @@ -19,7 +19,8 @@ package org.apache.syncope.client.console.rest; import jakarta.ws.rs.core.Response; -import java.util.concurrent.atomic.AtomicReference; +import org.apache.commons.lang3.mutable.Mutable; +import org.apache.commons.lang3.mutable.MutableObject; import org.apache.syncope.common.lib.to.OIDCJWKSTO; import org.apache.syncope.common.rest.api.service.OIDCJWKSService; @@ -27,18 +28,18 @@ public class OIDCJWKSRestClient extends BaseRestClient { private static final long serialVersionUID = -1392090291817187902L; - public AtomicReference get() { - AtomicReference result = new AtomicReference<>(); + public Mutable get() { + MutableObject result = new MutableObject<>(); try { - result.set(getService(OIDCJWKSService.class).get()); + result.setValue(getService(OIDCJWKSService.class).get()); } catch (Exception e) { LOG.debug("While getting OIDC JKS", e); } return result; } - public OIDCJWKSTO generate() { - Response response = getService(OIDCJWKSService.class).generate("syncope", "RSA", 2048); + public OIDCJWKSTO generate(final String jwksKeyId, final String jwksType, final int jwksKeySize) { + Response response = getService(OIDCJWKSService.class).generate(jwksKeyId, jwksType, jwksKeySize); return response.readEntity(OIDCJWKSTO.class); } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/SAML2IdPEntityRestClient.java b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/SAML2IdPEntityRestClient.java index 9c66b1e7844..26b7a13c364 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/SAML2IdPEntityRestClient.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/SAML2IdPEntityRestClient.java @@ -38,4 +38,8 @@ public SAML2IdPEntityTO get(final String key) { public void set(final SAML2IdPEntityTO entityTO) { getService(SAML2IdPEntityService.class).set(entityTO); } + + public void delete(final String key) { + getService(SAML2IdPEntityService.class).delete(key); + } } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/SAML2SPEntityRestClient.java b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/SAML2SPEntityRestClient.java deleted file mode 100644 index 42873a25a30..00000000000 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/SAML2SPEntityRestClient.java +++ /dev/null @@ -1,45 +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.client.console.rest; - -import java.util.ArrayList; -import java.util.List; -import org.apache.syncope.common.lib.to.SAML2SPEntityTO; -import org.apache.syncope.common.rest.api.service.SAML2SPEntityService; - -public class SAML2SPEntityRestClient extends BaseRestClient { - - private static final long serialVersionUID = -1392090291817187902L; - - public List list() { - return new ArrayList<>(getService(SAML2SPEntityService.class).list()); - } - - public SAML2SPEntityTO get(final String key) { - return getService(SAML2SPEntityService.class).get(key); - } - - public void set(final SAML2SPEntityTO entityTO) { - getService(SAML2SPEntityService.class).set(entityTO); - } - - public void delete(final String key) { - getService(SAML2SPEntityService.class).delete(key); - } -} 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 e313d0c022f..3aeabe26818 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 @@ -50,7 +50,7 @@ public SRAStatistics get(final List instances, final List s.getKey() + ":" + s.getValue()).toArray()); + client.query("tag", selected.stream().map(s -> s.getLeft() + ":" + s.getRight()).toArray()); } Response response = client.get(); diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WAConfigRestClient.java b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WAConfigRestClient.java index c803821bfce..4dce2e8b33e 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WAConfigRestClient.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WAConfigRestClient.java @@ -30,6 +30,10 @@ public List list() { return getService(WAConfigService.class).list(); } + public Attr get(final String schema) { + return getService(WAConfigService.class).get(schema); + } + public void set(final Attr value) { getService(WAConfigService.class).set(value); } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java index a0b7317bf42..92677033f03 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java @@ -19,12 +19,15 @@ package org.apache.syncope.client.console.wizards; import java.io.Serializable; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.List; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.console.SyncopeWebApplication; import org.apache.syncope.client.console.panels.BeanPanel; import org.apache.syncope.client.console.rest.AuthModuleRestClient; +import org.apache.syncope.client.console.wicket.markup.html.form.XMLEditorPanel; import org.apache.syncope.client.console.wizards.mapping.AuthModuleMappingPanel; import org.apache.syncope.client.ui.commons.Constants; import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior; @@ -32,10 +35,12 @@ import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxNumberFieldPanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; +import org.apache.syncope.client.ui.commons.markup.html.form.BinaryFieldPanel; import org.apache.syncope.client.ui.commons.wizards.AjaxWizard; import org.apache.syncope.common.lib.AbstractLDAPConf; import org.apache.syncope.common.lib.auth.AuthModuleConf; import org.apache.syncope.common.lib.auth.LDAPDependantAuthModuleConf; +import org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf; import org.apache.syncope.common.lib.to.AuthModuleTO; import org.apache.syncope.common.lib.types.AuthModuleState; import org.apache.wicket.PageReference; @@ -92,6 +97,7 @@ protected WizardModel buildModelSteps(final AuthModuleTO modelObject, final Wiza wizardModel.add(new Profile(modelObject, authModuleConfs, authModuleConfClass)); wizardModel.add(new Configuration(modelObject)); wizardModel.add(new AuthModuleConfLDAP(modelObject, authModuleConfClass)); + wizardModel.add(new AuthModuleConfSAML(modelObject, authModuleConfClass)); wizardModel.add(new Mapping(modelObject)); return wizardModel; } @@ -170,8 +176,69 @@ protected class Configuration extends WizardStep { private static final long serialVersionUID = -785981096328637758L; Configuration(final AuthModuleTO authModule) { - add(new BeanPanel<>("bean", new PropertyModel<>(authModule, "conf"), pageRef, "ldap"). - setRenderBodyOnly(true)); + add(new BeanPanel<>("bean", new PropertyModel<>(authModule, "conf"), pageRef, + "ldap", "keystore", "serviceProviderMetadata").setRenderBodyOnly(true)); + } + } + + protected class AuthModuleConfSAML extends WizardStep implements WizardModel.ICondition { + + private static final long serialVersionUID = 8139350816753061469L; + + private final Model> authModuleConfClass; + + AuthModuleConfSAML( + final AuthModuleTO authModule, + final Model> authModuleConfClass) { + + this.authModuleConfClass = authModuleConfClass; + + add(new BinaryFieldPanel( + "keystore", + "", + new PropertyModel<>(authModule, "conf.keystore"), + "application/x-java-keystore", + authModule.getKey()) { + + private static final long serialVersionUID = -3268213909514986831L; + + @Override + protected PageReference getPageReference() { + return pageRef; + } + }); + add(new XMLEditorPanel(null, new IModel() { + + private static final long serialVersionUID = -6439061267623081672L; + + @Override + public String getObject() { + SAML2IdPAuthModuleConf conf = (SAML2IdPAuthModuleConf) authModule.getConf(); + if (StringUtils.isEmpty(conf.getServiceProviderMetadata())) { + return null; + } + + return new String( + Base64.getDecoder().decode(conf.getServiceProviderMetadata()), + StandardCharsets.UTF_8); + } + + @Override + public void setObject(final String object) { + SAML2IdPAuthModuleConf conf = (SAML2IdPAuthModuleConf) authModule.getConf(); + if (StringUtils.isEmpty(object)) { + conf.setServiceProviderMetadata(null); + } else { + conf.setServiceProviderMetadata(Base64.getEncoder(). + encodeToString(object.getBytes(StandardCharsets.UTF_8))); + } + } + }, false, pageRef)); + } + + @Override + public boolean evaluate() { + return SAML2IdPAuthModuleConf.class.isAssignableFrom(authModuleConfClass.getObject()); } } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/SAML2EntityWizardBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/SAML2EntityWizardBuilder.java deleted file mode 100644 index 0e7393a5ab3..00000000000 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/SAML2EntityWizardBuilder.java +++ /dev/null @@ -1,65 +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.client.console.wizards; - -import org.apache.commons.lang3.StringUtils; -import org.apache.syncope.client.console.wicket.markup.html.form.XMLEditorPanel; -import org.apache.syncope.common.lib.to.SAML2EntityTO; -import org.apache.wicket.PageReference; -import org.apache.wicket.extensions.wizard.WizardStep; -import org.apache.wicket.model.PropertyModel; - -abstract class SAML2EntityWizardBuilder extends BaseAjaxWizardBuilder { - - private static final long serialVersionUID = 1L; - - protected SAML2EntityWizardBuilder(final T defaultItem, final PageReference pageRef) { - super(defaultItem, pageRef); - } - - protected class Metadata extends WizardStep { - - private static final long serialVersionUID = -3043839139187792810L; - - Metadata(final T entity, final PageReference pageRef) { - add(new XMLEditorPanel(null, new PropertyModel<>(entity, "metadata"), false, pageRef)); - } - - @Override - public String getTitle() { - return "Metadata"; - } - } - - protected abstract class Pem extends WizardStep { - - private static final long serialVersionUID = -3043839139187792810L; - - protected final String property; - - Pem(final String property) { - this.property = property; - } - - @Override - public String getTitle() { - return StringUtils.capitalize(property); - } - } -} diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/SAML2IdPEntityWizardBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/SAML2IdPEntityWizardBuilder.java index 9d88b52989a..2e3c7ced70f 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/SAML2IdPEntityWizardBuilder.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/SAML2IdPEntityWizardBuilder.java @@ -21,14 +21,17 @@ import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.util.Base64; +import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.console.rest.SAML2IdPEntityRestClient; import org.apache.syncope.client.console.wicket.markup.html.form.TextEditorPanel; +import org.apache.syncope.client.console.wicket.markup.html.form.XMLEditorPanel; import org.apache.syncope.common.lib.to.SAML2IdPEntityTO; import org.apache.wicket.PageReference; import org.apache.wicket.extensions.wizard.WizardModel; +import org.apache.wicket.extensions.wizard.WizardStep; import org.apache.wicket.model.PropertyModel; -public class SAML2IdPEntityWizardBuilder extends SAML2EntityWizardBuilder { +public class SAML2IdPEntityWizardBuilder extends BaseAjaxWizardBuilder { private static final long serialVersionUID = -8013493490328546125L; @@ -100,14 +103,35 @@ protected WizardModel buildModelSteps(final SAML2IdPEntityTO modelObject, final return wizardModel; } - protected class TextPem extends Pem { + protected class Metadata extends WizardStep { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = -3043839139187792810L; + + Metadata(final SAML2IdPEntityTO entity, final PageReference pageRef) { + add(new XMLEditorPanel(null, new PropertyModel<>(entity, "metadata"), false, pageRef)); + } + + @Override + public String getTitle() { + return "Metadata"; + } + } + + protected class TextPem extends WizardStep { + + private static final long serialVersionUID = -3713108738476604821L; + + protected final String property; public TextPem(final SAML2IdPEntityTO entity, final String property, final PageReference pageRef) { - super(property); + this.property = property; add(new TextEditorPanel(null, new PropertyModel<>(entity, property), false, pageRef)); } + + @Override + public String getTitle() { + return StringUtils.capitalize(property); + } } } diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/SAML2SPEntityWizardBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/SAML2SPEntityWizardBuilder.java deleted file mode 100644 index d3262189457..00000000000 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/SAML2SPEntityWizardBuilder.java +++ /dev/null @@ -1,111 +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.client.console.wizards; - -import jakarta.ws.rs.core.MediaType; -import java.io.Serializable; -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import org.apache.syncope.client.console.rest.SAML2SPEntityRestClient; -import org.apache.syncope.client.console.wicket.markup.html.form.BinaryFieldPanel; -import org.apache.syncope.client.ui.commons.Constants; -import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; -import org.apache.syncope.client.ui.commons.wizards.AjaxWizard; -import org.apache.syncope.common.lib.to.SAML2SPEntityTO; -import org.apache.wicket.PageReference; -import org.apache.wicket.extensions.wizard.WizardModel; -import org.apache.wicket.extensions.wizard.WizardStep; -import org.apache.wicket.model.PropertyModel; - -public class SAML2SPEntityWizardBuilder extends SAML2EntityWizardBuilder { - - private static final long serialVersionUID = 2400472385439304277L; - - protected final SAML2SPEntityRestClient saml2SPEntityRestClient; - - public SAML2SPEntityWizardBuilder( - final SAML2SPEntityTO defaultItem, - final SAML2SPEntityRestClient saml2SPEntityRestClient, - final PageReference pageRef) { - - super(defaultItem, pageRef); - this.saml2SPEntityRestClient = saml2SPEntityRestClient; - } - - @Override - protected Serializable onApplyInternal(final SAML2SPEntityTO modelObject) { - if (modelObject.getMetadata() != null) { - modelObject.setMetadata(Base64.getEncoder().encodeToString( - modelObject.getMetadata().getBytes(StandardCharsets.UTF_8))); - } - saml2SPEntityRestClient.set(modelObject); - return modelObject; - } - - @Override - protected WizardModel buildModelSteps(final SAML2SPEntityTO modelObject, final WizardModel wizardModel) { - if (modelObject.getMetadata() != null) { - modelObject.setMetadata(new String(Base64.getDecoder().decode( - modelObject.getMetadata()), StandardCharsets.UTF_8)); - } - - wizardModel.add(new Profile(modelObject, mode == AjaxWizard.Mode.CREATE)); - wizardModel.add(new Metadata(modelObject, pageRef)); - wizardModel.add(new BinaryPem(modelObject, "keystore", pageRef)); - return wizardModel; - } - - protected static class Profile extends WizardStep { - - private static final long serialVersionUID = -3043839139187792810L; - - Profile(final SAML2SPEntityTO entity, final boolean isNew) { - AjaxTextFieldPanel key = new AjaxTextFieldPanel( - Constants.KEY_FIELD_NAME, Constants.KEY_FIELD_NAME, - new PropertyModel<>(entity, Constants.KEY_FIELD_NAME)); - key.addRequiredLabel(); - key.setEnabled(isNew); - add(key); - } - } - - protected class BinaryPem extends Pem { - - private static final long serialVersionUID = 1L; - - public BinaryPem(final SAML2SPEntityTO entity, final String property, final PageReference pageRef) { - super(property); - - add(new BinaryFieldPanel( - "content", - "", - new PropertyModel<>(entity, property), - MediaType.APPLICATION_OCTET_STREAM, - entity.getKey()) { - - private static final long serialVersionUID = -3268213909514986831L; - - @Override - protected PageReference getPageReference() { - return pageRef; - } - }); - } - } -} diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfilePanel.html similarity index 63% rename from client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails.html rename to client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfilePanel.html index b5d83e86fef..8d2a5af99eb 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails.html +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfilePanel.html @@ -18,22 +18,19 @@ --> -
-
- [name] -
-
- [readonly] -
-
- [resource] -
-
- [anyType] -
-
- [extAttrName] -
+
+
+
+ [FILTER] + + + +
+
+ +
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.properties index 229c25a2629..e3ba1847eb2 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.properties @@ -81,3 +81,4 @@ userInfoSigningAlg=UserInfo Signing Algorithm userInfoEncryptedResponseAlg=UserInfo Encrypted Response Algorithm userInfoEncryptedResponseEncoding=UserInfo Encrypted Response Encoding evaluationOrder=Evaluation Order +idp=Identity Provider diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_fr_CA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_fr_CA.properties index b8f5fc87984..2bb0a441b38 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_fr_CA.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_fr_CA.properties @@ -81,3 +81,4 @@ userInfoSigningAlg=UserInfo Signing Algorithm userInfoEncryptedResponseAlg=UserInfo Encrypted Response Algorithm userInfoEncryptedResponseEncoding=UserInfo Encrypted Response Encoding evaluationOrder=Evaluation Order +idp=Identity Provider diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_it.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_it.properties index 90bfaf968a8..2ad1b0f69bd 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_it.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_it.properties @@ -81,3 +81,4 @@ userInfoSigningAlg=Algoritmo di firma UserInfo userInfoEncryptedResponseAlg=Algoritmo di cifratura risposta UserInfo userInfoEncryptedResponseEncoding=Codifica di cifratura risposta UserInfo evaluationOrder=Ordine di valutazione +idp=Identity Provider diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ja.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ja.properties index 428c4dce872..d0868ef9f22 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ja.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ja.properties @@ -81,3 +81,4 @@ userInfoSigningAlg=UserInfo Signing Algorithm userInfoEncryptedResponseAlg=UserInfo Encrypted Response Algorithm userInfoEncryptedResponseEncoding=UserInfo Encrypted Response Encoding evaluationOrder=Evaluation Order +idp=Identity Provider diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_pt_BR.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_pt_BR.properties index 54383631234..1bd4e93e86b 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_pt_BR.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_pt_BR.properties @@ -81,3 +81,4 @@ userInfoSigningAlg=UserInfo Signing Algorithm userInfoEncryptedResponseAlg=UserInfo Encrypted Response Algorithm userInfoEncryptedResponseEncoding=UserInfo Encrypted Response Encoding evaluationOrder=Evaluation Order +idp=Identity Provider diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ru.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ru.properties index 09307c88255..bc0ab53d9db 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ru.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ru.properties @@ -82,3 +82,4 @@ userInfoSigningAlg=UserInfo Signing Algorithm userInfoEncryptedResponseAlg=UserInfo Encrypted Response Algorithm userInfoEncryptedResponseEncoding=UserInfo Encrypted Response Encoding evaluationOrder=Evaluation Order +idp=Identity Provider diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/OIDC.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/OIDC.html index 8a4de0f0453..f67ee751dcf 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/OIDC.html +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/OIDC.html @@ -49,6 +49,7 @@

Well-Known URI

+
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/OIDCJWKSGenerationPanel.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/OIDCJWKSGenerationPanel.html new file mode 100644 index 00000000000..cce9c66bf52 --- /dev/null +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/OIDCJWKSGenerationPanel.html @@ -0,0 +1,27 @@ + + + +
+ + + +
+
+ diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/policies/AuthPolicyModalPanel.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/policies/AuthPolicyModalPanel.html index e66a972c85e..e02ca5f7b01 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/policies/AuthPolicyModalPanel.html +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/policies/AuthPolicyModalPanel.html @@ -27,11 +27,17 @@
+
+ +
+
+ +
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration.properties index 8d2c500e11a..cbd6b3af519 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration.properties @@ -16,7 +16,6 @@ # under the License. plainAttrs=Plain Attributes derAttrs=Derived Attributes -virAttrs=Virtual Attributes features=Features matchingCond=Matching Condition userMatchingCond=User Matching Condition diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_fr_CA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_fr_CA.properties index e791574a14e..68f0f52287f 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_fr_CA.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_fr_CA.properties @@ -15,9 +15,8 @@ # specific language governing permissions and limitations # under the License. plainAttrs=Attributs simples -derAttrs=Attributs dérivés -virAttrs=Attributs virtuels -features=Caractéristiques +derAttrs=Attributs d\u00e9riv\u00e9s +features=Caract\u00e9ristiques matchingCond=Condition d'appariement userMatchingCond=Condition d'appariement des utilisateurs groupMatchingCond=Condition d'appariement de groupe diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_it.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_it.properties index 6688f852c27..f5186538835 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_it.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_it.properties @@ -16,7 +16,6 @@ # under the License. plainAttrs=Attributi derAttrs=Attributi Derivati -virAttrs=Attributi Virtuali features=Features matchingCond=Condizione di matching userMatchingCond=Condizione di matching utente diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_ja.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_ja.properties index ba85e50448b..83a1dacdd58 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_ja.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_ja.properties @@ -16,7 +16,6 @@ # under the License. plainAttrs=\u30d7\u30ec\u30fc\u30f3\u5c5e\u6027 derAttrs=\u6d3e\u751f\u5c5e\u6027 -virAttrs=\u4eee\u60f3\u5c5e\u6027 features=\u6a5f\u80fd matchingCond=\u4e00\u81f4\u6761\u4ef6 userMatchingCond=\u30e6\u30fc\u30b6\u2015\u4e00\u81f4\u6761\u4ef6 diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_pt_BR.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_pt_BR.properties index 8d2c500e11a..cbd6b3af519 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_pt_BR.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_pt_BR.properties @@ -16,7 +16,6 @@ # under the License. plainAttrs=Plain Attributes derAttrs=Derived Attributes -virAttrs=Virtual Attributes features=Features matchingCond=Matching Condition userMatchingCond=User Matching Condition diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_ru.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_ru.properties index 721b3e531a1..3abdee76868 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_ru.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_ru.properties @@ -17,7 +17,6 @@ # plainAttrs=\u041f\u0440\u043e\u0441\u0442\u044b\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b derAttrs=\u041f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u044b\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b -virAttrs=\u0412\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b features=\u0421\u0432\u043e\u0439\u0441\u0442\u0432\u0430 matchingCond=\u041a\u0440\u0438\u0442\u0435\u0440\u0438\u0439 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044f userMatchingCond=\u041a\u0440\u0438\u0442\u0435\u0440\u0438\u0439 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044f \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 diff --git a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/panels/any/VirAttrs.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$AuthModuleConfSAML.html similarity index 80% rename from client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/panels/any/VirAttrs.html rename to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$AuthModuleConfSAML.html index 5968a336a1f..ddacc6fb1b1 100644 --- a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/panels/any/VirAttrs.html +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$AuthModuleConfSAML.html @@ -1,4 +1,3 @@ - - -
- -
- - - \ No newline at end of file + + +
+ + +
+ + diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration.properties index 8d2c500e11a..cbd6b3af519 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration.properties @@ -16,7 +16,6 @@ # under the License. plainAttrs=Plain Attributes derAttrs=Derived Attributes -virAttrs=Virtual Attributes features=Features matchingCond=Matching Condition userMatchingCond=User Matching Condition diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_fr_CA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_fr_CA.properties index e791574a14e..68f0f52287f 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_fr_CA.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_fr_CA.properties @@ -15,9 +15,8 @@ # specific language governing permissions and limitations # under the License. plainAttrs=Attributs simples -derAttrs=Attributs dérivés -virAttrs=Attributs virtuels -features=Caractéristiques +derAttrs=Attributs d\u00e9riv\u00e9s +features=Caract\u00e9ristiques matchingCond=Condition d'appariement userMatchingCond=Condition d'appariement des utilisateurs groupMatchingCond=Condition d'appariement de groupe diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_it.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_it.properties index 6688f852c27..f5186538835 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_it.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_it.properties @@ -16,7 +16,6 @@ # under the License. plainAttrs=Attributi derAttrs=Attributi Derivati -virAttrs=Attributi Virtuali features=Features matchingCond=Condizione di matching userMatchingCond=Condizione di matching utente diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_ja.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_ja.properties index ba85e50448b..83a1dacdd58 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_ja.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_ja.properties @@ -16,7 +16,6 @@ # under the License. plainAttrs=\u30d7\u30ec\u30fc\u30f3\u5c5e\u6027 derAttrs=\u6d3e\u751f\u5c5e\u6027 -virAttrs=\u4eee\u60f3\u5c5e\u6027 features=\u6a5f\u80fd matchingCond=\u4e00\u81f4\u6761\u4ef6 userMatchingCond=\u30e6\u30fc\u30b6\u2015\u4e00\u81f4\u6761\u4ef6 diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_pt_BR.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_pt_BR.properties index 8d2c500e11a..cbd6b3af519 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_pt_BR.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_pt_BR.properties @@ -16,7 +16,6 @@ # under the License. plainAttrs=Plain Attributes derAttrs=Derived Attributes -virAttrs=Virtual Attributes features=Features matchingCond=Matching Condition userMatchingCond=User Matching Condition diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_ru.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_ru.properties index 721b3e531a1..3abdee76868 100644 --- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_ru.properties +++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Configuration_ru.properties @@ -17,7 +17,6 @@ # plainAttrs=\u041f\u0440\u043e\u0441\u0442\u044b\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b derAttrs=\u041f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u044b\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b -virAttrs=\u0412\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b features=\u0421\u0432\u043e\u0439\u0441\u0442\u0432\u0430 matchingCond=\u041a\u0440\u0438\u0442\u0435\u0440\u0438\u0439 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044f userMatchingCond=\u041a\u0440\u0438\u0442\u0435\u0440\u0438\u0439 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044f \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/SAML2EntityWizardBuilder$Metadata.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/SAML2IdPEntityWizardBuilder$Metadata.html similarity index 100% rename from client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/SAML2EntityWizardBuilder$Metadata.html rename to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/SAML2IdPEntityWizardBuilder$Metadata.html diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/SAML2EntityWizardBuilder$Pem.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/SAML2IdPEntityWizardBuilder$TextPem.html similarity index 100% rename from client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/SAML2EntityWizardBuilder$Pem.html rename to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/SAML2IdPEntityWizardBuilder$TextPem.html diff --git a/client/am/enduser/pom.xml b/client/am/enduser/pom.xml new file mode 100644 index 00000000000..2d559722197 --- /dev/null +++ b/client/am/enduser/pom.xml @@ -0,0 +1,84 @@ + + + + + 4.0.0 + + + org.apache.syncope.client + syncope-client-am + 4.0.8-SNAPSHOT + + + Apache Syncope Client AM Enduser + Apache Syncope Client AM Enduser + org.apache.syncope.client.am + syncope-client-am-enduser + jar + + + ${basedir}/../../.. + + + + + jakarta.servlet + jakarta.servlet-api + provided + + + + org.apache.syncope.client.idrepo + syncope-client-idrepo-enduser + ${project.version} + + + org.apache.syncope.client.am + syncope-client-am-lib + ${project.version} + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + + + + src/main/resources + true + + org/apache/syncope/**/*.properties + + + + src/main/resources + false + + org/apache/syncope/**/*.properties + + + + + diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMVirSchemaDetailsPanelProvider.java b/client/am/enduser/src/main/java/org/apache/syncope/client/enduser/AMEnduserContext.java similarity index 61% rename from client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMVirSchemaDetailsPanelProvider.java rename to client/am/enduser/src/main/java/org/apache/syncope/client/enduser/AMEnduserContext.java index ecc06051c7b..3b840e520fd 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMVirSchemaDetailsPanelProvider.java +++ b/client/am/enduser/src/main/java/org/apache/syncope/client/enduser/AMEnduserContext.java @@ -16,18 +16,19 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.client.console.commons; +package org.apache.syncope.client.enduser; -import org.apache.syncope.client.console.panels.VirSchemaDetails; -import org.apache.syncope.common.lib.to.VirSchemaTO; -import org.apache.wicket.markup.html.panel.Panel; +import org.apache.syncope.client.enduser.rest.AuthProfileRestClient; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; -public class IdMVirSchemaDetailsPanelProvider implements VirSchemaDetailsPanelProvider { +@Configuration(proxyBeanMethods = false) +public class AMEnduserContext { - private static final long serialVersionUID = -173141337550903982L; - - @Override - public Panel get(final String id, final VirSchemaTO schemaTO) { - return new VirSchemaDetails("details", schemaTO); + @ConditionalOnMissingBean + @Bean + public AuthProfileRestClient authProfileRestClient() { + return new AuthProfileRestClient(); } } diff --git a/client/am/enduser/src/main/java/org/apache/syncope/client/enduser/pages/AuthProfile.java b/client/am/enduser/src/main/java/org/apache/syncope/client/enduser/pages/AuthProfile.java new file mode 100644 index 00000000000..50a584d4f83 --- /dev/null +++ b/client/am/enduser/src/main/java/org/apache/syncope/client/enduser/pages/AuthProfile.java @@ -0,0 +1,220 @@ +/* + * 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.client.enduser.pages; + +import java.util.List; +import java.util.stream.Collectors; +import org.apache.syncope.client.enduser.rest.AuthProfileRestClient; +import org.apache.syncope.client.ui.commons.Constants; +import org.apache.syncope.client.ui.commons.annotations.AMPage; +import org.apache.syncope.client.ui.commons.markup.html.form.IndicatingOnConfirmAjaxLink; +import org.apache.syncope.common.lib.to.AuthProfileTO; +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.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.markup.html.navigation.paging.AjaxPagingNavigator; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.repeater.Item; +import org.apache.wicket.markup.repeater.data.DataView; +import org.apache.wicket.markup.repeater.data.ListDataProvider; +import org.apache.wicket.request.mapper.parameter.PageParameters; +import org.apache.wicket.spring.injection.annot.SpringBean; + +@AMPage(label = "Auth Profile", icon = "fa fa-passport", listEntitlement = "") +public class AuthProfile extends BaseReauthPage { + + private static final long serialVersionUID = -3147262161518280928L; + + protected static final String AUTH_PROFILE = "page.authProfile"; + + protected static final int ROWS_PER_PAGE = 5; + + @SpringBean + protected AuthProfileRestClient restClient; + + public AuthProfile(final PageParameters parameters) { + super(parameters, AUTH_PROFILE); + + AuthProfileTO authProfile = restClient.read(); + + WebMarkupContainer container = new WebMarkupContainer("content"); + contentWrapper.add(container.setOutputMarkupId(true)); + + DataView impersonationAccounts = new DataView<>( + "impersonationAccounts", new ListDataProvider<>( + authProfile == null ? List.of() : authProfile.getImpersonationAccounts())) { + + private static final long serialVersionUID = 6127875313385810666L; + + @Override + public void populateItem(final Item item) { + item.add(new Label("impersonated", item.getModelObject().getImpersonated())); + item.add(new IndicatingOnConfirmAjaxLink<>( + "impersonationAccountDelete", Constants.CONFIRM_DELETE, true) { + + private static final long serialVersionUID = 1632838687547839512L; + + @Override + public void onClick(final AjaxRequestTarget target) { + if (authProfile != null) { + authProfile.getImpersonationAccounts().remove(item.getModelObject()); + restClient.update(authProfile); + target.add(container); + } + } + }); + } + }; + impersonationAccounts.setItemsPerPage(ROWS_PER_PAGE); + container.add(impersonationAccounts.setOutputMarkupPlaceholderTag(true)); + container.add(new AjaxPagingNavigator("impersonationAccountsNavigator", impersonationAccounts)); + + DataView googleMfaAuthTokens = new DataView<>( + "googleMfaAuthTokens", new ListDataProvider<>( + authProfile == null ? List.of() : authProfile.getGoogleMfaAuthTokens())) { + + private static final long serialVersionUID = 6127875313385810666L; + + @Override + public void populateItem(final Item item) { + item.add(new Label("otp", item.getModelObject().getOtp())); + item.add(new Label("issueDate", item.getModelObject().getIssueDate())); + item.add(new IndicatingOnConfirmAjaxLink<>( + "googleMfaAuthTokenDelete", Constants.CONFIRM_DELETE, true) { + + private static final long serialVersionUID = 1632838687547839512L; + + @Override + public void onClick(final AjaxRequestTarget target) { + if (authProfile != null) { + authProfile.getGoogleMfaAuthTokens().remove(item.getModelObject()); + restClient.update(authProfile); + target.add(container); + } + } + }); + } + }; + googleMfaAuthTokens.setItemsPerPage(ROWS_PER_PAGE); + container.add(googleMfaAuthTokens.setOutputMarkupPlaceholderTag(true)); + container.add(new AjaxPagingNavigator("googleMfaAuthTokensNavigator", googleMfaAuthTokens)); + + DataView googleMfaAuthAccounts = new DataView<>( + "googleMfaAuthAccounts", new ListDataProvider<>( + authProfile == null ? List.of() : authProfile.getGoogleMfaAuthAccounts())) { + + private static final long serialVersionUID = 6127875313385810666L; + + @Override + public void populateItem(final Item item) { + item.add(new Label("id", item.getModelObject().getId())); + item.add(new Label("name", item.getModelObject().getName())); + item.add(new Label("secretKey", item.getModelObject().getSecretKey())); + item.add(new Label("validationCode", item.getModelObject().getValidationCode())); + item.add(new Label("scratchCodes", item.getModelObject().getScratchCodes().stream(). + map(String::valueOf).collect(Collectors.joining(", ")))); + item.add(new Label("registrationDate", item.getModelObject().getRegistrationDate())); + item.add(new Label("source", item.getModelObject().getSource())); + item.add(new IndicatingOnConfirmAjaxLink<>( + "googleMfaAuthAccountDelete", Constants.CONFIRM_DELETE, true) { + + private static final long serialVersionUID = 1632838687547839512L; + + @Override + public void onClick(final AjaxRequestTarget target) { + if (authProfile != null) { + authProfile.getGoogleMfaAuthAccounts().remove(item.getModelObject()); + restClient.update(authProfile); + target.add(container); + } + } + }); + } + }; + googleMfaAuthAccounts.setItemsPerPage(ROWS_PER_PAGE); + container.add(googleMfaAuthAccounts.setOutputMarkupPlaceholderTag(true)); + container.add(new AjaxPagingNavigator("googleMfaAuthAccountsNavigator", googleMfaAuthAccounts)); + + DataView mfaTrustedDevices = new DataView<>( + "mfaTrustedDevices", new ListDataProvider<>( + authProfile == null ? List.of() : authProfile.getMfaTrustedDevices())) { + + private static final long serialVersionUID = 6127875313385810666L; + + @Override + public void populateItem(final Item item) { + item.add(new Label("id", item.getModelObject().getId())); + item.add(new Label("name", item.getModelObject().getName())); + item.add(new Label("deviceFingerprint", item.getModelObject().getDeviceFingerprint())); + item.add(new Label("recordDate", item.getModelObject().getRecordDate())); + item.add(new Label("expirationDate", item.getModelObject().getExpirationDate())); + item.add(new IndicatingOnConfirmAjaxLink<>( + "mfaTrustedDeviceDelete", Constants.CONFIRM_DELETE, true) { + + private static final long serialVersionUID = 1632838687547839512L; + + @Override + public void onClick(final AjaxRequestTarget target) { + if (authProfile != null) { + authProfile.getMfaTrustedDevices().remove(item.getModelObject()); + restClient.update(authProfile); + target.add(container); + } + } + }); + } + }; + mfaTrustedDevices.setItemsPerPage(ROWS_PER_PAGE); + container.add(mfaTrustedDevices.setOutputMarkupPlaceholderTag(true)); + container.add(new AjaxPagingNavigator("mfaTrustedDevicesNavigator", mfaTrustedDevices)); + + DataView webAuthnDeviceCredentials = new DataView<>( + "webAuthnDeviceCredentials", new ListDataProvider<>( + authProfile == null ? List.of() : authProfile.getWebAuthnDeviceCredentials())) { + + private static final long serialVersionUID = 6127875313385810666L; + + @Override + public void populateItem(final Item item) { + item.add(new Label("identifier", item.getModelObject().getIdentifier())); + item.add(new IndicatingOnConfirmAjaxLink<>( + "webAuthnDeviceCredentialDelete", Constants.CONFIRM_DELETE, true) { + + private static final long serialVersionUID = 1632838687547839512L; + + @Override + public void onClick(final AjaxRequestTarget target) { + if (authProfile != null) { + authProfile.getWebAuthnDeviceCredentials().remove(item.getModelObject()); + restClient.update(authProfile); + target.add(container); + } + } + }); + } + }; + webAuthnDeviceCredentials.setItemsPerPage(ROWS_PER_PAGE); + container.add(webAuthnDeviceCredentials.setOutputMarkupPlaceholderTag(true)); + container.add(new AjaxPagingNavigator("webAuthnDeviceCredentialsNavigator", webAuthnDeviceCredentials)); + } +} diff --git a/client/am/enduser/src/main/java/org/apache/syncope/client/enduser/rest/AuthProfileRestClient.java b/client/am/enduser/src/main/java/org/apache/syncope/client/enduser/rest/AuthProfileRestClient.java new file mode 100644 index 00000000000..2714954ff59 --- /dev/null +++ b/client/am/enduser/src/main/java/org/apache/syncope/client/enduser/rest/AuthProfileRestClient.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.client.enduser.rest; + +import org.apache.syncope.common.lib.to.AuthProfileTO; +import org.apache.syncope.common.rest.api.service.AuthProfileSelfService; + +public class AuthProfileRestClient extends BaseRestClient { + + private static final long serialVersionUID = 4139153766778113329L; + + public AuthProfileTO read() { + try { + return getService(AuthProfileSelfService.class).read(); + } catch (Exception e) { + LOG.debug("While attempting to read the auth profile", e); + return null; + } + } + + public void update(final AuthProfileTO authProfile) { + getService(AuthProfileSelfService.class).update(authProfile); + } + + public void delete() { + getService(AuthProfileSelfService.class).delete(); + } +} diff --git a/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile.html b/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile.html new file mode 100644 index 00000000000..fdbe3b190ab --- /dev/null +++ b/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile.html @@ -0,0 +1,257 @@ + + + + +
+
+
+
+
+
+

+
+
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + + + +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+ diff --git a/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile.properties b/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile.properties new file mode 100644 index 00000000000..5bf4119b60e --- /dev/null +++ b/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile.properties @@ -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. +page.authProfile=Auth Profile +impersonation.accounts.title=Impersonation Accounts +google.mfa.authtokens.title=Google MFA Tokens +google.mfa.authaccounts.title=Google MFA Accounts +mfa.trusted.devices.title=MFA Trusted Devices +webauthn.device.credentials.title=WebAuthn Device Credentials +otp=OTP +issueDate=Issue Date +impersonated=Impersonated +id=Id +name=Name +secretKey=Secret Key +validationCode=Validation Code +scratchCodes=Scratch Codes +registrationDate=Registration Date +source=Source +deviceFingerprint=Fingerprint +recordDate=Record Date +expirationDate=Expiration Date diff --git a/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile_it.properties b/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile_it.properties new file mode 100644 index 00000000000..bb47fe6606f --- /dev/null +++ b/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile_it.properties @@ -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. +page.authProfile=Auth Profile +impersonation.accounts.title=Account di impersonificazione +google.mfa.authtokens.title=Token MFA Google +google.mfa.authaccounts.title=Account MFA Google +mfa.trusted.devices.title=Dispositivi MFA +webauthn.device.credentials.title=Credenziali dispositivi WebAuthn +otp=OTP +issueDate=Data di emissione +impersonated=Impersonificato +id=Id +name=Nome +secretKey=Chiave segreta +validationCode=Codice di validazione +scratchCodes=Codici Scratch +registrationDate=Data di registrazione +source=Origine +deviceFingerprint=Fingerprint +recordDate=Data di registrazione +expirationDate=Data di scadenza diff --git a/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile_ja.properties b/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile_ja.properties new file mode 100644 index 00000000000..5bf4119b60e --- /dev/null +++ b/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile_ja.properties @@ -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. +page.authProfile=Auth Profile +impersonation.accounts.title=Impersonation Accounts +google.mfa.authtokens.title=Google MFA Tokens +google.mfa.authaccounts.title=Google MFA Accounts +mfa.trusted.devices.title=MFA Trusted Devices +webauthn.device.credentials.title=WebAuthn Device Credentials +otp=OTP +issueDate=Issue Date +impersonated=Impersonated +id=Id +name=Name +secretKey=Secret Key +validationCode=Validation Code +scratchCodes=Scratch Codes +registrationDate=Registration Date +source=Source +deviceFingerprint=Fingerprint +recordDate=Record Date +expirationDate=Expiration Date diff --git a/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile_pt_BR.properties b/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile_pt_BR.properties new file mode 100644 index 00000000000..5bf4119b60e --- /dev/null +++ b/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile_pt_BR.properties @@ -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. +page.authProfile=Auth Profile +impersonation.accounts.title=Impersonation Accounts +google.mfa.authtokens.title=Google MFA Tokens +google.mfa.authaccounts.title=Google MFA Accounts +mfa.trusted.devices.title=MFA Trusted Devices +webauthn.device.credentials.title=WebAuthn Device Credentials +otp=OTP +issueDate=Issue Date +impersonated=Impersonated +id=Id +name=Name +secretKey=Secret Key +validationCode=Validation Code +scratchCodes=Scratch Codes +registrationDate=Registration Date +source=Source +deviceFingerprint=Fingerprint +recordDate=Record Date +expirationDate=Expiration Date diff --git a/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile_ru.properties b/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile_ru.properties new file mode 100644 index 00000000000..5bf4119b60e --- /dev/null +++ b/client/am/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/AuthProfile_ru.properties @@ -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. +page.authProfile=Auth Profile +impersonation.accounts.title=Impersonation Accounts +google.mfa.authtokens.title=Google MFA Tokens +google.mfa.authaccounts.title=Google MFA Accounts +mfa.trusted.devices.title=MFA Trusted Devices +webauthn.device.credentials.title=WebAuthn Device Credentials +otp=OTP +issueDate=Issue Date +impersonated=Impersonated +id=Id +name=Name +secretKey=Secret Key +validationCode=Validation Code +scratchCodes=Scratch Codes +registrationDate=Registration Date +source=Source +deviceFingerprint=Fingerprint +recordDate=Record Date +expirationDate=Expiration Date diff --git a/client/am/lib/pom.xml b/client/am/lib/pom.xml index e3827702361..7c568962672 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.0.0-SNAPSHOT + 4.0.8-SNAPSHOT Apache Syncope Client AM Lib diff --git a/client/am/pom.xml b/client/am/pom.xml index dfbac940de8..834887868d2 100644 --- a/client/am/pom.xml +++ b/client/am/pom.xml @@ -24,7 +24,7 @@ under the License. org.apache.syncope syncope-client - 4.0.0-SNAPSHOT + 4.0.8-SNAPSHOT Apache Syncope Client AM @@ -40,5 +40,6 @@ under the License. lib console + enduser diff --git a/client/idm/console/pom.xml b/client/idm/console/pom.xml index 7f318e6f919..9339968db55 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.0.0-SNAPSHOT + 4.0.8-SNAPSHOT Apache Syncope Client IdM Console @@ -69,6 +69,7 @@ under the License. true + --load-path=${rootpom.basedir}/client/idrepo/common-ui/src/main/resources/META-INF/resources/ui-commons/css ${basedir}/src/main/resources/META-INF/resources/css:${project.build.outputDirectory}/META-INF/resources/css diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/IdMConsoleContext.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/IdMConsoleContext.java index ae5666f6d27..57d46b8f0f3 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/IdMConsoleContext.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/IdMConsoleContext.java @@ -29,11 +29,9 @@ import org.apache.syncope.client.console.commons.IdMImplementationInfoProvider; import org.apache.syncope.client.console.commons.IdMPolicyTabProvider; import org.apache.syncope.client.console.commons.IdMStatusProvider; -import org.apache.syncope.client.console.commons.IdMVirSchemaDetailsPanelProvider; import org.apache.syncope.client.console.commons.ImplementationInfoProvider; import org.apache.syncope.client.console.commons.PolicyTabProvider; import org.apache.syncope.client.console.commons.StatusProvider; -import org.apache.syncope.client.console.commons.VirSchemaDetailsPanelProvider; import org.apache.syncope.client.console.init.ClassPathScanImplementationContributor; import org.apache.syncope.client.console.init.ClassPathScanImplementationLookup; import org.apache.syncope.client.console.init.IdMClassPathScanImplementationContributor; @@ -88,11 +86,6 @@ public StatusProvider statusProvider(final ReconStatusUtils reconStatusUtils) { return new IdMStatusProvider(reconStatusUtils); } - @Bean - public VirSchemaDetailsPanelProvider virSchemaDetailsPanelProvider() { - return new IdMVirSchemaDetailsPanelProvider(); - } - @Bean public ImplementationInfoProvider implementationInfoProvider( final ClassPathScanImplementationLookup lookup, diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/ConnectorDataProvider.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/ConnectorDataProvider.java index 5a652d80ced..893f674b92c 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/ConnectorDataProvider.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/ConnectorDataProvider.java @@ -19,7 +19,6 @@ package org.apache.syncope.client.console.commons; import java.io.Serializable; -import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; @@ -69,7 +68,7 @@ public ConnectorDataProvider( @Override public Iterator iterator(final long first, final long count) { - List result = Collections.emptyList(); + List result = List.of(); try { currentPage = ((int) first / paginatorRows); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionLinksProvider.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionLinksProvider.java index d685da5b5b2..247ab767f07 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionLinksProvider.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionLinksProvider.java @@ -126,7 +126,7 @@ public void onClick(final AjaxRequestTarget target, final UserTO ignore) { } }, ActionLink.ActionType.MANAGE_RESOURCES); manageResources.setEntitlements( - String.format("%s,%s", IdRepoEntitlement.USER_READ, IdRepoEntitlement.USER_UPDATE)); + "%s,%s".formatted(IdRepoEntitlement.USER_READ, IdRepoEntitlement.USER_UPDATE)); manageResources.setOnConfirm(false); manageResources.setRealms(realm, model.getObject().getDynRealms()); actions.add(manageResources); @@ -146,7 +146,7 @@ public void onClick(final AjaxRequestTarget target, final UserTO ignore) { modal.show(true); } }, ActionLink.ActionType.MANAGE_ACCOUNTS); - manageAccounts.setEntitlements(String.format("%s,%s,%s", + manageAccounts.setEntitlements("%s,%s,%s".formatted( IdRepoEntitlement.USER_READ, IdRepoEntitlement.USER_UPDATE, IdMEntitlement.RESOURCE_GET_CONNOBJECT)); manageAccounts.setOnConfirm(false); manageAccounts.setRealms(realm, model.getObject().getDynRealms()); @@ -167,7 +167,7 @@ public void onClick(final AjaxRequestTarget target, final UserTO ignore) { modal.show(true); } }, ActionLink.ActionType.MERGE_ACCOUNTS); - mergeAccounts.setEntitlements(String.format("%s,%s,%s,%s", + mergeAccounts.setEntitlements("%s,%s,%s,%s".formatted( IdRepoEntitlement.USER_READ, IdRepoEntitlement.USER_UPDATE, IdRepoEntitlement.USER_DELETE, IdMEntitlement.RESOURCE_GET_CONNOBJECT)); actions.add(mergeAccounts); @@ -207,7 +207,7 @@ public void onClick(final AjaxRequestTarget target, final GroupTO ignore) { } }, ActionLink.ActionType.MANAGE_RESOURCES); manageResources.setEntitlements( - String.format("%s,%s", IdRepoEntitlement.GROUP_READ, IdRepoEntitlement.GROUP_UPDATE)); + "%s,%s".formatted(IdRepoEntitlement.GROUP_READ, IdRepoEntitlement.GROUP_UPDATE)); manageResources.setOnConfirm(false); manageResources.setRealms(realm, modelObject.getDynRealms()); actions.add(manageResources); @@ -249,7 +249,7 @@ public void onClick(final AjaxRequestTarget target, final AnyObjectTO ignore) { } }, ActionLink.ActionType.MANAGE_RESOURCES); manageResources.setEntitlements( - String.format("%s,%s", AnyEntitlement.READ.getFor(type), AnyEntitlement.UPDATE.getFor(type))); + "%s,%s".formatted(AnyEntitlement.READ.getFor(type), AnyEntitlement.UPDATE.getFor(type))); manageResources.setRealms(realm, modelObject.getDynRealms()); actions.add(manageResources); return actions; diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionsProvider.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionsProvider.java index bc5462b12e7..a8cc861f998 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionsProvider.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionsProvider.java @@ -22,12 +22,11 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import org.apache.syncope.client.console.PreferenceManager; import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.pages.BasePage; import org.apache.syncope.client.console.panels.AnyDirectoryPanel; -import org.apache.syncope.client.console.panels.DisplayAttributesModalPanel; +import org.apache.syncope.client.console.panels.AnyDisplayAttributesModalPanel; import org.apache.syncope.client.console.rest.BaseRestClient; import org.apache.syncope.client.console.rest.ImplementationRestClient; import org.apache.syncope.client.console.rest.ReconciliationRestClient; @@ -83,7 +82,7 @@ public void add( final WebMarkupContainer container, final String type, final String realm, - final String fiql, + final Model fiql, final int rows, final List pSchemaNames, final List dSchemaNames, @@ -99,7 +98,7 @@ public void onEvent(final IEvent event) { if (event.getPayload() instanceof final AjaxWizard.NewItemCancelEvent newItemCancelEvent) { newItemCancelEvent.getTarget(). ifPresent(modal::close); - } else if (event.getPayload() instanceof final AjaxWizard.NewItemFinishEvent newItemFinishEvent) { + } else if (event.getPayload() instanceof final AjaxWizard.NewItemFinishEvent newItemFinishEvent) { AjaxWizard.NewItemFinishEvent payload = newItemFinishEvent; Optional target = payload.getTarget(); @@ -152,7 +151,7 @@ protected Panel customResultBody( @Override public void onClick(final AjaxRequestTarget target) { CSVPushSpec spec = csvPushSpec(type, pSchemaNames, dSchemaNames); - AnyQuery query = csvAnyQuery(realm, fiql, rows, panel.getDataProvider()); + AnyQuery query = csvAnyQuery(realm, fiql.getObject(), rows, panel.getDataProvider()); target.add(modal.setContent(new CSVPushWizardBuilder( spec, query, csvDownloadBehavior, reconciliationRestClient, implementationRestClient, pageRef). @@ -165,7 +164,7 @@ public void onClick(final AjaxRequestTarget target) { }; csvPushLink.setOutputMarkupPlaceholderTag(true).setVisible(wizardInModal).setEnabled(wizardInModal); MetaDataRoleAuthorizationStrategy.authorize(csvPushLink, Component.RENDER, - String.format("%s,%s", IdRepoEntitlement.IMPLEMENTATION_LIST, IdRepoEntitlement.TASK_EXECUTE)); + "%s,%s".formatted(IdRepoEntitlement.IMPLEMENTATION_LIST, IdRepoEntitlement.TASK_EXECUTE)); panel.addInnerObject(csvPushLink.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true)); csvPullLink = new AjaxLink<>("csvPull") { @@ -186,7 +185,7 @@ public void onClick(final AjaxRequestTarget target) { }; csvPullLink.setOutputMarkupPlaceholderTag(true).setVisible(wizardInModal).setEnabled(wizardInModal); MetaDataRoleAuthorizationStrategy.authorize(csvPullLink, Component.RENDER, - String.format("%s,%s", IdRepoEntitlement.IMPLEMENTATION_LIST, IdRepoEntitlement.TASK_EXECUTE)); + "%s,%s".formatted(IdRepoEntitlement.IMPLEMENTATION_LIST, IdRepoEntitlement.TASK_EXECUTE)); panel.addInnerObject(csvPullLink.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true)); } @@ -195,25 +194,18 @@ protected CSVPushSpec csvPushSpec( final List pSchemaNames, final List dSchemaNames) { - CSVPushSpec spec = new CSVPushSpec.Builder(type).build(); - spec.setFields(PreferenceManager.getList( - DisplayAttributesModalPanel.getPrefDetailView(type)). - stream().filter(name -> !Constants.KEY_FIELD_NAME.equalsIgnoreCase(name)). - collect(Collectors.toList())); - spec.setPlainAttrs(PreferenceManager.getList( - DisplayAttributesModalPanel.getPrefPlainAttributeView(type)). - stream().filter(pSchemaNames::contains).collect(Collectors.toList())); - spec.setDerAttrs(PreferenceManager.getList( - DisplayAttributesModalPanel.getPrefPlainAttributeView(type)). - stream().filter(dSchemaNames::contains).collect(Collectors.toList())); - return spec; + return new CSVPushSpec.Builder(type). + fields(PreferenceManager.getList(AnyDisplayAttributesModalPanel.getPrefDetailView(type)). + stream().filter(name -> !Constants.KEY_FIELD_NAME.equalsIgnoreCase(name)).toList()). + plainAttrs(PreferenceManager.getList(AnyDisplayAttributesModalPanel.getPrefPlainAttributeView(type)). + stream().filter(pSchemaNames::contains).toList()). + derAttrs(PreferenceManager.getList(AnyDisplayAttributesModalPanel.getPrefPlainAttributeView(type)). + stream().filter(dSchemaNames::contains).toList()). + build(); } protected CSVPullSpec csvPullSpec(final String type, final String realm) { - CSVPullSpec spec = new CSVPullSpec(); - spec.setAnyTypeKey(type); - spec.setDestinationRealm(realm); - return spec; + return new CSVPullSpec.Builder(type, realm).build(); } protected AnyQuery csvAnyQuery( diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMExternalResourceProvider.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMExternalResourceProvider.java index bb92a285545..53b47ff6859 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMExternalResourceProvider.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMExternalResourceProvider.java @@ -22,6 +22,7 @@ import java.util.stream.Collectors; import org.apache.syncope.client.console.rest.ResourceRestClient; import org.apache.syncope.common.lib.to.ResourceTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; public class IdMExternalResourceProvider implements ExternalResourceProvider { @@ -34,7 +35,18 @@ public IdMExternalResourceProvider(final ResourceRestClient resourceRestClient) } @Override - public List get() { - return resourceRestClient.list().stream().map(ResourceTO::getKey).collect(Collectors.toList()); + public List get(final String anyType) { + return resourceRestClient.list().stream(). + filter(r -> AnyTypeKind.GROUP.name().equals(anyType) || r.getProvision(anyType).isPresent()). + map(ResourceTO::getKey). + collect(Collectors.toList()); + } + + @Override + public List getForRealms() { + return resourceRestClient.list().stream(). + filter(r -> r.getOrgUnit() != null). + map(ResourceTO::getKey). + collect(Collectors.toList()); } } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMImplementationInfoProvider.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMImplementationInfoProvider.java index ed6b564466f..fcb5ca29c26 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMImplementationInfoProvider.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMImplementationInfoProvider.java @@ -19,7 +19,7 @@ package org.apache.syncope.client.console.commons; import java.util.ArrayList; -import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; import org.apache.syncope.client.console.init.ClassPathScanImplementationLookup; @@ -67,7 +67,7 @@ public List getClasses(final ImplementationTO implementation, final View default: } - Collections.sort(classes); + classes.sort(Comparator.naturalOrder()); } else { classes = super.getClasses(implementation, viewMode); } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMStatusProvider.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMStatusProvider.java index 05725f7b644..e55bbcff55b 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMStatusProvider.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMStatusProvider.java @@ -22,15 +22,11 @@ import java.util.Collection; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.commons.lang3.tuple.Triple; import org.apache.syncope.client.console.panels.ListViewPanel; import org.apache.syncope.client.console.status.ReconStatusUtils; import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink; import org.apache.syncope.client.ui.commons.status.ConnObjectWrapper; import org.apache.syncope.common.lib.to.AnyTO; -import org.apache.syncope.common.lib.to.ConnObject; import org.apache.syncope.common.lib.types.IdMEntitlement; public class IdMStatusProvider implements StatusProvider { @@ -44,24 +40,19 @@ public IdMStatusProvider(final ReconStatusUtils reconStatusUtils) { } @Override - public Optional> get( - final String anyTypeKey, final String connObjectKeyValue, final String resource) { - + public Optional get(final String anyTypeKey, final String connObjectKeyValue, final String resource) { return reconStatusUtils.getReconStatus(anyTypeKey, connObjectKeyValue, resource). - map(status -> Pair.of(status.getOnSyncope(), status.getOnResource())); + map(status -> new Info(status.getOnSyncope(), status.getOnResource())); } @Override - public List> get( - final AnyTO any, final Collection resources) { - + public List get(final AnyTO any, final Collection resources) { return reconStatusUtils.getReconStatuses( any.getType(), any.getKey(), any.getResources()).stream(). - map(status -> Triple.of( + map(status -> new InfoWithFailure( status.getRight().getOnSyncope(), new ConnObjectWrapper(any, status.getLeft(), status.getRight().getOnResource()), - null)). - collect(Collectors.toList()); + null)).toList(); } @Override diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/pages/Remediations.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/pages/Remediations.java index c3089acba56..17775f54b69 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/pages/Remediations.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/pages/Remediations.java @@ -38,8 +38,7 @@ public Remediations(final PageParameters parameters) { body.add(BookmarkablePageLinkBuilder.build("dashboard", "dashboardBr", Dashboard.class)); WebMarkupContainer content = new WebMarkupContainer("content"); - content.setOutputMarkupId(true); - body.add(content); + body.add(content.setOutputMarkupId(true)); content.add(new RemediationDirectoryPanel("remediations", remediationRestClient, getPageReference())); } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectDetails.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectDetails.java index 1fae48528ef..288c2ef0d0c 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectDetails.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectDetails.java @@ -19,6 +19,7 @@ package org.apache.syncope.client.console.panels; import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.client.console.commons.StatusProvider; import org.apache.syncope.client.console.wizards.any.ConnObjectPanel; import org.apache.syncope.common.lib.to.ConnObject; import org.apache.wicket.model.Model; @@ -34,9 +35,8 @@ public ConnObjectDetails(final ConnObject connObjectTO) { mlp.setFirstLevel(new ConnObjectPanel( MultilevelPanel.FIRST_LEVEL_ID, Pair.of(Model.of(), Model.of()), - Pair.of((ConnObject) null, connObjectTO), + new StatusProvider.Info(null, connObjectTO), true)); add(mlp); } - } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java index 5b552797d65..b059363ee55 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Optional; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.pages.BasePage; @@ -160,14 +161,13 @@ public void onClick(final AjaxRequestTarget target) { List listOfItems = reloadItems(resource.getKey(), anyType, null, null); - ListViewPanel.Builder builder = new ListViewPanel.Builder<>( - ConnObject.class, pageRef) { + ListViewPanel.Builder builder = new ListViewPanel.Builder<>(ConnObject.class, pageRef) { private static final long serialVersionUID = -8251750413385566738L; @Override protected Component getValueComponent(final String key, final ConnObject bean) { - if (StringUtils.equals(key, STATUS)) { + if (Strings.CS.equals(key, STATUS)) { ReconStatus status; try { status = reconciliationRestClient.status( @@ -231,7 +231,7 @@ public void onClick(final AjaxRequestTarget target, final ConnObject modelObject withChecks(ListViewPanel.CheckAvailability.NONE). setReuseItem(false); - if (!StringUtils.equals(anyType, SyncopeConstants.REALM_ANYTYPE)) { + if (!Strings.CS.equals(anyType, SyncopeConstants.REALM_ANYTYPE)) { builder.addAction(new ActionLink<>() { private static final long serialVersionUID = 6377238742125L; @@ -339,7 +339,7 @@ protected AbstractSearchPanel getSearchPanel(final String id, final String anyTy clause.setProperty(""); AnyTypeKind anyTypeKind = - StringUtils.equals(anyType, SyncopeConstants.REALM_ANYTYPE) || StringUtils.isEmpty(anyType) + Strings.CS.equals(anyType, SyncopeConstants.REALM_ANYTYPE) || StringUtils.isEmpty(anyType) ? AnyTypeKind.ANY_OBJECT : anyTypeRestClient.read(anyType).getKind(); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel.java index deff7f20574..777494f2d4d 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel.java @@ -23,7 +23,6 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.console.SyncopeConsoleSession; @@ -127,7 +126,7 @@ protected List> getColumns() { @Override protected Collection getBatches() { - return Collections.singletonList(ActionLink.ActionType.DELETE); + return List.of(ActionLink.ActionType.DELETE); } @Override @@ -158,7 +157,7 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { target.add(modal); } - }, ActionLink.ActionType.CREATE_RESOURCE, String.format("%s", IdMEntitlement.RESOURCE_CREATE)); + }, ActionLink.ActionType.CREATE_RESOURCE, "%s".formatted(IdMEntitlement.RESOURCE_CREATE)); panel.add(new ActionLink<>() { @@ -183,8 +182,8 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { modal.show(true); } - }, ActionLink.ActionType.EDIT, String.format("%s,%s", IdMEntitlement.CONNECTOR_READ, - IdMEntitlement.CONNECTOR_UPDATE)); + }, ActionLink.ActionType.EDIT, + "%s,%s".formatted(IdMEntitlement.CONNECTOR_READ, IdMEntitlement.CONNECTOR_UPDATE)); panel.add(new ActionLink<>() { @@ -226,7 +225,7 @@ protected void restore(final String json, final AjaxRequestTarget target) { } }, ActionLink.ActionType.VIEW_AUDIT_HISTORY, - String.format("%s,%s", IdMEntitlement.CONNECTOR_READ, IdRepoEntitlement.AUDIT_LIST)); + "%s,%s".formatted(IdMEntitlement.CONNECTOR_READ, IdRepoEntitlement.AUDIT_LIST)); panel.add(new ActionLink<>() { @@ -236,7 +235,7 @@ protected void restore(final String json, final AjaxRequestTarget target) { public void onClick(final AjaxRequestTarget target, final Serializable ignore) { try { restClient.delete(((ConnInstanceTO) model.getObject()).getKey()); - target.appendJavaScript(String.format("jsPlumb.remove('%s');", + target.appendJavaScript("jsPlumb.remove('%s');".formatted( ((ConnInstanceTO) model.getObject()).getKey())); SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnidLocations.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnidLocations.java index a7aa03650b7..673be16f932 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnidLocations.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnidLocations.java @@ -23,7 +23,6 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import org.apache.syncope.client.console.SyncopeConsoleSession; @@ -124,14 +123,14 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { modal.show(true); } - }, ActionLink.ActionType.CREATE, String.format("%s", IdMEntitlement.CONNECTOR_CREATE)); + }, ActionLink.ActionType.CREATE, "%s".formatted(IdMEntitlement.CONNECTOR_CREATE)); return panel; } @Override protected Collection getBatches() { - return Collections.emptyList(); + return List.of(); } protected static class ConnidLocationsDataProvider extends DirectoryDataProvider { diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java index 9cd543ca023..e2419cc0dec 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java @@ -114,8 +114,8 @@ public LinkedAccountModalPanel( roleRestClient, anyTypeRestClient.listAnyTypes().stream().map(AnyTypeTO::getKey).collect(Collectors.toList())); LinkedAccountFormLayoutInfo linkedAccountFormLayoutInfo = - anyLayout.getUser() instanceof IdMUserFormLayoutInfo - ? IdMUserFormLayoutInfo.class.cast(anyLayout.getUser()).getLinkedAccountFormLayoutInfo() + anyLayout.getUser() instanceof IdMUserFormLayoutInfo idMUFLI + ? idMUFLI.getLinkedAccountFormLayoutInfo() : new LinkedAccountFormLayoutInfo(); try { @@ -298,7 +298,7 @@ public void onClick(final AjaxRequestTarget target, final LinkedAccountTO linked new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target)); } }, ActionLink.ActionType.RECONCILIATION_PUSH, - String.format("%s,%s", IdRepoEntitlement.USER_READ, IdRepoEntitlement.TASK_EXECUTE)); + "%s,%s".formatted(IdRepoEntitlement.USER_READ, IdRepoEntitlement.TASK_EXECUTE)); builder.addAction(new ActionLink<>() { @@ -324,7 +324,7 @@ public void onClick(final AjaxRequestTarget target, final LinkedAccountTO linked new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target)); } }, ActionLink.ActionType.RECONCILIATION_PULL, - String.format("%s,%s", IdRepoEntitlement.USER_READ, IdRepoEntitlement.TASK_EXECUTE)); + "%s,%s".formatted(IdRepoEntitlement.USER_READ, IdRepoEntitlement.TASK_EXECUTE)); if (!reconciliationOnly) { builder.addAction(new ActionLink<>() { diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/RemediationDirectoryPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/RemediationDirectoryPanel.java index 3d937cb5dd8..3d82ecfa5a8 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/RemediationDirectoryPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/RemediationDirectoryPanel.java @@ -24,6 +24,8 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.commons.DirectoryDataProvider; @@ -157,13 +159,14 @@ public void onClick(final AjaxRequestTarget target, final RemediationTO ignore) }, ActionLink.ActionType.VIEW_DETAILS, IdMEntitlement.REMEDIATION_READ); if (model.getObject().getOperation() == ResourceOperation.DELETE) { - String entitlements = StringUtils.join(new String[] { - IdMEntitlement.REMEDIATION_REMEDY, - AnyTypeKind.USER.name().equals(model.getObject().getAnyType()) - ? IdRepoEntitlement.USER_DELETE - : AnyTypeKind.GROUP.name().equals(model.getObject().getAnyType()) - ? IdRepoEntitlement.GROUP_DELETE - : AnyEntitlement.DELETE.getFor(model.getObject().getAnyType()) }, ","); + String entitlements = Stream.of( + IdMEntitlement.REMEDIATION_REMEDY, + AnyTypeKind.USER.name().equals(model.getObject().getAnyType()) + ? IdRepoEntitlement.USER_DELETE + : AnyTypeKind.GROUP.name().equals(model.getObject().getAnyType()) + ? IdRepoEntitlement.GROUP_DELETE + : AnyEntitlement.DELETE.getFor(model.getObject().getAnyType())). + collect(Collectors.joining(",")); panel.add(new ActionLink<>() { @@ -185,20 +188,22 @@ public void onClick(final AjaxRequestTarget target, final RemediationTO ignore) }, ActionLink.ActionType.CLOSE, entitlements, true); } else { String entitlements = model.getObject().getOperation() == ResourceOperation.CREATE - ? StringUtils.join(new String[] { - IdMEntitlement.REMEDIATION_REMEDY, - AnyTypeKind.USER.name().equals(model.getObject().getAnyType()) - ? IdRepoEntitlement.USER_CREATE - : AnyTypeKind.GROUP.name().equals(model.getObject().getAnyType()) - ? IdRepoEntitlement.GROUP_CREATE - : AnyEntitlement.CREATE.getFor(model.getObject().getAnyType()) }, ",") - : StringUtils.join(new String[] { - IdMEntitlement.REMEDIATION_REMEDY, - AnyTypeKind.USER.name().equals(model.getObject().getAnyType()) - ? IdRepoEntitlement.USER_UPDATE - : AnyTypeKind.GROUP.name().equals(model.getObject().getAnyType()) - ? IdRepoEntitlement.GROUP_UPDATE - : AnyEntitlement.UPDATE.getFor(model.getObject().getAnyType()) }, ","); + ? Stream.of( + IdMEntitlement.REMEDIATION_REMEDY, + AnyTypeKind.USER.name().equals(model.getObject().getAnyType()) + ? IdRepoEntitlement.USER_CREATE + : AnyTypeKind.GROUP.name().equals(model.getObject().getAnyType()) + ? IdRepoEntitlement.GROUP_CREATE + : AnyEntitlement.CREATE.getFor(model.getObject().getAnyType())). + collect(Collectors.joining(",")) + : Stream.of( + IdMEntitlement.REMEDIATION_REMEDY, + AnyTypeKind.USER.name().equals(model.getObject().getAnyType()) + ? IdRepoEntitlement.USER_UPDATE + : AnyTypeKind.GROUP.name().equals(model.getObject().getAnyType()) + ? IdRepoEntitlement.GROUP_UPDATE + : AnyEntitlement.UPDATE.getFor(model.getObject().getAnyType())). + collect(Collectors.joining(",")); panel.add(new ActionLink<>() { @@ -345,8 +350,7 @@ public RemediationProvider(final int paginatorRows) { @Override public Iterator iterator(final long first, final long count) { int page = ((int) first / paginatorRows); - return restClient.getRemediations((page < 0 ? 0 : page) + 1, - paginatorRows, getSort()).iterator(); + return restClient.getRemediations((page < 0 ? 0 : page) + 1, paginatorRows, getSort()).iterator(); } @Override diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDirectoryPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDirectoryPanel.java index b9ede15c03b..2b74a924934 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDirectoryPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDirectoryPanel.java @@ -23,7 +23,6 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.console.SyncopeConsoleSession; @@ -169,7 +168,7 @@ protected List> getColumns() { @Override protected Collection getBatches() { - return Collections.singletonList(ActionLink.ActionType.DELETE); + return List.of(ActionLink.ActionType.DELETE); } @Override @@ -201,7 +200,7 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { modal.show(true); } }, ActionLink.ActionType.EDIT, - String.format("%s,%s", IdMEntitlement.RESOURCE_READ, IdMEntitlement.RESOURCE_UPDATE)); + "%s,%s".formatted(IdMEntitlement.RESOURCE_READ, IdMEntitlement.RESOURCE_UPDATE)); panel.add(new ActionLink<>() { @@ -231,7 +230,7 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { provisionModal.show(true); } }, ActionLink.ActionType.MAPPING, - String.format("%s,%s", IdMEntitlement.RESOURCE_READ, IdMEntitlement.RESOURCE_UPDATE)); + "%s,%s".formatted(IdMEntitlement.RESOURCE_READ, IdMEntitlement.RESOURCE_UPDATE)); panel.add(new ActionLink<>() { @@ -347,7 +346,7 @@ protected void restore(final String json, final AjaxRequestTarget target) { historyModal.show(true); } }, ActionLink.ActionType.VIEW_AUDIT_HISTORY, - String.format("%s,%s", IdMEntitlement.RESOURCE_READ, IdRepoEntitlement.AUDIT_LIST)); + "%s,%s".formatted(IdMEntitlement.RESOURCE_READ, IdRepoEntitlement.AUDIT_LIST)); panel.add(new ActionLink<>() { @@ -359,8 +358,6 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { ResourceTO resource = restClient.read(key); resource.setKey("Copy of " + resource.getKey()); // reset some resource objects keys - resource.getProvisions().stream().filter(p -> p.getMapping() != null). - forEach(p -> p.getVirSchemas().clear()); target.add(modal.setContent(new ResourceWizardBuilder( resource, restClient, connectorRestClient, pageRef). build(BaseModal.CONTENT_ID, AjaxWizard.Mode.CREATE))); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/VirSchemaDetails.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/VirSchemaDetails.java deleted file mode 100644 index a2c59d7ea02..00000000000 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/VirSchemaDetails.java +++ /dev/null @@ -1,164 +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.client.console.panels; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import org.apache.syncope.client.console.SyncopeConsoleSession; -import org.apache.syncope.client.console.rest.ConnectorRestClient; -import org.apache.syncope.client.console.rest.ResourceRestClient; -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.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.SyncopeConstants; -import org.apache.syncope.common.lib.to.ResourceTO; -import org.apache.syncope.common.lib.to.VirSchemaTO; -import org.apache.syncope.common.lib.types.IdMEntitlement; -import org.apache.wicket.ajax.AjaxRequestTarget; -import org.apache.wicket.model.PropertyModel; -import org.apache.wicket.spring.injection.annot.SpringBean; - -public class VirSchemaDetails extends AbstractSchemaDetailsPanel { - - private static final long serialVersionUID = 5979623248182851337L; - - @SpringBean - protected ResourceRestClient resourceRestClient; - - @SpringBean - protected ConnectorRestClient connectorRestClient; - - protected final Map anyTypes = new HashMap<>(); - - protected final AjaxDropDownChoicePanel anyType; - - protected ResourceTO selectedResource; - - public VirSchemaDetails(final String id, final VirSchemaTO schemaTO) { - super(id, schemaTO); - - AjaxCheckBoxPanel readonly = new AjaxCheckBoxPanel("readonly", getString("readonly"), - new PropertyModel<>(schemaTO, "readonly")); - add(readonly); - - AjaxDropDownChoicePanel resource = new AjaxDropDownChoicePanel<>( - "resource", getString("resource"), new PropertyModel(schemaTO, "resource"), false). - setNullValid(false); - resource.setChoices(resourceRestClient.list().stream().map(ResourceTO::getKey).collect(Collectors.toList())); - resource.setOutputMarkupId(true); - resource.addRequiredLabel(); - if (resource.getModelObject() != null) { - populateAnyTypes(resource.getModelObject()); - } - add(resource); - - anyType = new AjaxDropDownChoicePanel<>( - "anyType", getString("anyType"), new PropertyModel(schemaTO, "anyType"), false). - setNullValid(false); - anyType.setChoices(new ArrayList<>(anyTypes.keySet())); - anyType.setOutputMarkupId(true); - anyType.setOutputMarkupPlaceholderTag(true); - anyType.addRequiredLabel(); - if (resource.getModelObject() == null) { - anyType.setEnabled(false); - } - add(anyType); - - AjaxTextFieldPanel extAttrName = new AjaxTextFieldPanel( - "extAttrName", getString("extAttrName"), new PropertyModel<>(schemaTO, "extAttrName")); - extAttrName.setOutputMarkupId(true); - extAttrName.addRequiredLabel(); - if (selectedResource != null) { - extAttrName.setChoices(getExtAttrNames()); - } - add(extAttrName); - - resource.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { - - private static final long serialVersionUID = -1107858522700306810L; - - @Override - protected void onUpdate(final AjaxRequestTarget target) { - anyTypes.clear(); - if (resource.getModelObject() != null) { - populateAnyTypes(resource.getModelObject()); - anyType.setEnabled(true); - } - anyType.setChoices(new ArrayList<>(anyTypes.keySet())); - anyType.setModelObject(null); - target.add(anyType); - } - }); - - anyType.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { - - private static final long serialVersionUID = -1107858522700306810L; - - @Override - protected void onUpdate(final AjaxRequestTarget target) { - if (selectedResource != null) { - String adminRealm = getAdminRealm(selectedResource.getConnector()); - - if (SyncopeConsoleSession.get().owns(IdMEntitlement.CONNECTOR_READ, adminRealm)) { - extAttrName.setChoices(getExtAttrNames()); - target.add(extAttrName); - } - } - } - }); - } - - protected String getAdminRealm(final String connectorKey) { - String adminRealm = null; - try { - adminRealm = connectorRestClient.read(connectorKey).getAdminRealm(); - } catch (Exception e) { - LOG.error("Could not read Admin Realm for External Resource {}", selectedResource.getKey()); - } - - return adminRealm; - } - - protected void populateAnyTypes(final String resourceKey) { - anyTypes.clear(); - if (resourceKey != null) { - ResourceTO resource = resourceRestClient.read(resourceKey); - String adminRealm = getAdminRealm(resource.getConnector()); - - if (SyncopeConsoleSession.get().owns(IdMEntitlement.RESOURCE_READ, adminRealm)) { - selectedResource = resource; - selectedResource.getProvisions().forEach( - provisionTO -> anyTypes.put(provisionTO.getAnyType(), provisionTO.getObjectClass())); - } - } - } - - protected List getExtAttrNames() { - return connectorRestClient.getExtAttrNames( - SyncopeConstants.ROOT_REALM, - anyTypes.get(anyType.getModelObject()), - selectedResource.getConnector(), - selectedResource.getConfOverride()); - } -} diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/search/ConnObjectSearchPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/search/ConnObjectSearchPanel.java index fcebeb51985..c06d26bbea7 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/search/ConnObjectSearchPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/search/ConnObjectSearchPanel.java @@ -34,7 +34,7 @@ import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.spring.injection.annot.SpringBean; -public class ConnObjectSearchPanel extends AbstractSearchPanel { +public class ConnObjectSearchPanel extends AbstractAnySearchPanel { private static final long serialVersionUID = 21020550706646L; 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 c4f3fe0faca..1d8b5693255 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 @@ -21,7 +21,7 @@ import com.fasterxml.jackson.databind.json.JsonMapper; import java.io.Serializable; import java.util.ArrayList; -import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Optional; @@ -216,20 +216,23 @@ protected class CorrelationRulePanel extends Panel { @Override public Boolean getObject() { AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf(); - return conf instanceof DefaultInboundCorrelationRuleConf - ? DefaultInboundCorrelationRuleConf.class.cast(conf).isOrSchemas() - : conf instanceof DefaultPushCorrelationRuleConf - ? DefaultPushCorrelationRuleConf.class.cast(conf).isOrSchemas() + return conf instanceof DefaultInboundCorrelationRuleConf dicrc + ? dicrc.isOrSchemas() + : conf instanceof DefaultPushCorrelationRuleConf dpcrc + ? dpcrc.isOrSchemas() : false; } @Override public void setObject(final Boolean object) { AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf(); - if (conf instanceof DefaultInboundCorrelationRuleConf) { - DefaultInboundCorrelationRuleConf.class.cast(conf).setOrSchemas(object); - } else if (conf instanceof DefaultPushCorrelationRuleConf) { - DefaultPushCorrelationRuleConf.class.cast(conf).setOrSchemas(object); + switch (conf) { + case DefaultInboundCorrelationRuleConf dicrc -> + dicrc.setOrSchemas(object); + case DefaultPushCorrelationRuleConf dpcrc -> + dpcrc.setOrSchemas(object); + default -> { + } } } }; @@ -244,10 +247,10 @@ public void setObject(final Boolean object) { private List schemas() { AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf(); - return conf instanceof DefaultInboundCorrelationRuleConf - ? DefaultInboundCorrelationRuleConf.class.cast(conf).getSchemas() - : conf instanceof DefaultPushCorrelationRuleConf - ? DefaultPushCorrelationRuleConf.class.cast(conf).getSchemas() + return conf instanceof DefaultInboundCorrelationRuleConf dicrc + ? dicrc.getSchemas() + : conf instanceof DefaultPushCorrelationRuleConf dpcrc + ? dpcrc.getSchemas() : List.of(); } @@ -331,7 +334,7 @@ private List getSchemas(final CorrelationRule rule) { choices.add(Constants.KEY_FIELD_NAME); choices.add(rule.getAnyType().equals(AnyTypeKind.USER.name()) ? Constants.USERNAME_FIELD_NAME : Constants.NAME_FIELD_NAME); - Collections.sort(choices); + choices.sort(Comparator.naturalOrder()); return choices; } } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java index 488abf4796b..9b8e411c01b 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java @@ -27,7 +27,6 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.cxf.jaxrs.client.WebClient; import org.apache.syncope.client.console.SyncopeConsoleSession; @@ -159,7 +158,7 @@ public boolean check(final String coreAddress, final String domain, final String Response response = client.path(key).get(); if (response.getStatus() == Response.Status.OK.getStatusCode()) { response = client.back(false).path("check"). - post(IOUtils.toString((InputStream) response.getEntity(), StandardCharsets.UTF_8)); + post(new String(((InputStream) response.getEntity()).readAllBytes(), StandardCharsets.UTF_8)); return response.getStatus() == Response.Status.NO_CONTENT.getStatusCode(); } return false; diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ResourceRestClient.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ResourceRestClient.java index c2db75abde5..b1db4c6f55b 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ResourceRestClient.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ResourceRestClient.java @@ -26,7 +26,6 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.cxf.jaxrs.client.WebClient; import org.apache.syncope.client.lib.WebClientBuilder; @@ -57,7 +56,7 @@ public boolean check(final String coreAddress, final String domain, final String Response response = client.path(key).get(); if (response.getStatus() == Response.Status.OK.getStatusCode()) { response = client.back(false).path("check"). - post(IOUtils.toString((InputStream) response.getEntity(), StandardCharsets.UTF_8)); + post(new String(((InputStream) response.getEntity()).readAllBytes(), StandardCharsets.UTF_8)); return response.getStatus() == Response.Status.NO_CONTENT.getStatusCode(); } return false; diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/AnyStatusDirectoryPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/AnyStatusDirectoryPanel.java index 18b42ed9455..0bee880cad1 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/AnyStatusDirectoryPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/AnyStatusDirectoryPanel.java @@ -257,9 +257,7 @@ public void onClick(final AjaxRequestTarget target, final StatusBean bean) { }, ActionLink.ActionType.RECONCILIATION_PULL, IdRepoEntitlement.TASK_EXECUTE); } - if (anyTO instanceof UserTO && !UserTO.class.cast(anyTO).getLinkedAccounts().isEmpty()) { - UserTO userTO = UserTO.class.cast(anyTO); - + if (anyTO instanceof UserTO userTO && userTO.getLinkedAccounts().isEmpty()) { if (!userTO.getLinkedAccounts().isEmpty() && userTO.getLinkedAccounts().stream().anyMatch(linkedAccountTO -> { return linkedAccountTO.getResource().equals(model.getObject().getResource()); @@ -272,13 +270,14 @@ public void onClick(final AjaxRequestTarget target, final StatusBean bean) { @Override public void onClick(final AjaxRequestTarget target, final StatusBean bean) { multiLevelPanelRef.next("ACCOUNTS", - new LinkedAccountsStatusModalPanel(Model.of(UserTO.class.cast(anyTO)), pageRef), + new LinkedAccountsStatusModalPanel(Model.of(userTO), pageRef), target); target.add(multiLevelPanelRef); AnyStatusDirectoryPanel.this.getTogglePanel().close(target); } }, ActionLink.ActionType.MANAGE_ACCOUNTS, - String.format("%s,%s,%s", IdRepoEntitlement.USER_READ, IdRepoEntitlement.USER_UPDATE, + "%s,%s,%s".formatted( + IdRepoEntitlement.USER_READ, IdRepoEntitlement.USER_UPDATE, IdMEntitlement.RESOURCE_GET_CONNOBJECT)); } } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/LinkedAccountStatusPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/LinkedAccountStatusPanel.java index 34b7b2519c3..4964091c5b0 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/LinkedAccountStatusPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/LinkedAccountStatusPanel.java @@ -20,10 +20,10 @@ import java.util.Optional; import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.client.console.commons.StatusProvider; import org.apache.syncope.client.console.panels.RemoteObjectPanel; import org.apache.syncope.client.console.wizards.any.ConnObjectPanel; import org.apache.syncope.client.ui.commons.Constants; -import org.apache.syncope.common.lib.to.ConnObject; import org.apache.syncope.common.lib.to.ReconStatus; import org.apache.wicket.model.Model; import org.apache.wicket.model.ResourceModel; @@ -54,14 +54,14 @@ public LinkedAccountStatusPanel( add(new ConnObjectPanel( REMOTE_OBJECT_PANEL_ID, Pair.of(Model.of(Constants.SYNCOPE), new ResourceModel("resource")), - getConnObjectTOs(), + getStatusProviderInfo(), false)); } @Override - protected Pair getConnObjectTOs() { + protected StatusProvider.Info getStatusProviderInfo() { Optional status = reconStatusUtils.getReconStatus(anyTypeKey, connObjectKeyValue, resource); - return status.map(reconStatus -> Pair.of(reconStatus.getOnSyncope(), reconStatus.getOnResource())).orElse(null); + return status.map(s -> new StatusProvider.Info(s.getOnSyncope(), s.getOnResource())).orElse(null); } } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconStatusPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconStatusPanel.java index ba0179f663c..d1dcfda06bf 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconStatusPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconStatusPanel.java @@ -20,10 +20,10 @@ import java.util.List; import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.client.console.commons.StatusProvider; import org.apache.syncope.client.console.panels.RemoteObjectPanel; import org.apache.syncope.client.console.wizards.any.ConnObjectPanel; import org.apache.syncope.client.ui.commons.Constants; -import org.apache.syncope.common.lib.to.ConnObject; import org.apache.syncope.common.lib.to.ReconStatus; import org.apache.wicket.model.Model; import org.apache.wicket.model.ResourceModel; @@ -54,18 +54,19 @@ public ReconStatusPanel( add(new ConnObjectPanel( REMOTE_OBJECT_PANEL_ID, Pair.of(Model.of(Constants.SYNCOPE), new ResourceModel("resource")), - getConnObjectTOs(), + getStatusProviderInfo(), false)); } @Override - protected Pair getConnObjectTOs() { + protected StatusProvider.Info getStatusProviderInfo() { List> statuses = reconStatusUtils.getReconStatuses(anyTypeKey, anyKey, List.of(resource)); return statuses.isEmpty() ? null - : Pair.of(statuses.getFirst().getRight().getOnSyncope(), - statuses.getFirst().getRight().getOnResource()); + : new StatusProvider.Info( + statuses.getFirst().getRight().getOnSyncope(), + statuses.getFirst().getRight().getOnResource()); } } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java index 98514763174..0f656fd1001 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java @@ -141,7 +141,7 @@ protected Iterator getChoices(final String input) { return (RealmsUtils.checkInput(input) ? (realmRestClient.search(fullRealmsTree ? RealmsUtils.buildBaseQuery() - : RealmsUtils.buildKeywordQuery(input)).getResult()) + : RealmsUtils.buildNameQuery(input)).getResult()) : List.of()).stream(). map(RealmTO::getFullPath).iterator(); } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ResourceStatusDirectoryPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ResourceStatusDirectoryPanel.java index 3617d836d54..efebe66dc3b 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ResourceStatusDirectoryPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ResourceStatusDirectoryPanel.java @@ -277,7 +277,7 @@ protected List getStatusBeans(final long first, final long count) { if (fiql != null && restClient != null) { int page = (int) first / paginatorRows; List result = restClient.search( - SyncopeConstants.ROOT_REALM, fiql, (page < 0 ? 0 : page) + 1, paginatorRows, getSort(), type); + SyncopeConstants.ROOT_REALM, fiql, (page < 0 ? 0 : page) + 1, paginatorRows, getSort()); statusBeans.addAll(result.stream().map(any -> StatusUtils.getStatusBean(any, resource.getKey(), @@ -291,7 +291,7 @@ protected List getStatusBeans(final long first, final long count) { @Override public long size() { return Optional.ofNullable(fiql). - map(s -> restClient.count(SyncopeConstants.ROOT_REALM, s, type)). + map(s -> restClient.count(SyncopeConstants.ROOT_REALM, s)). orElse(0L); } } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TabularTopology.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TabularTopology.java index 02ae460c80e..5e4960478e9 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TabularTopology.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TabularTopology.java @@ -21,11 +21,11 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.tabs.AjaxBootstrapTabbedPanel; import java.util.ArrayList; import java.util.List; -import org.apache.syncope.client.console.annotations.IdMPage; import org.apache.syncope.client.console.pages.BasePage; import org.apache.syncope.client.console.pages.Connectors; import org.apache.syncope.client.console.pages.Resources; import org.apache.syncope.client.console.panels.ConnidLocations; +import org.apache.syncope.client.ui.commons.annotations.IdMPage; import org.apache.syncope.common.lib.types.IdMEntitlement; import org.apache.wicket.extensions.markup.html.tabs.AbstractTab; import org.apache.wicket.extensions.markup.html.tabs.ITab; @@ -39,13 +39,10 @@ public class TabularTopology extends BasePage { private static final long serialVersionUID = -4434385801124981824L; public TabularTopology() { - TopologyWebSocketBehavior websocket = new TopologyWebSocketBehavior(); - body.add(websocket); - WebMarkupContainer content = new WebMarkupContainer("content"); - content.setOutputMarkupId(true); + body.add(content.setOutputMarkupId(true)); + content.add(new AjaxBootstrapTabbedPanel<>("tabbedPanel", buildTabList())); - body.add(content); } protected List buildTabList() { diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java index d22d706d51e..60e38417588 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java @@ -36,7 +36,6 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.cxf.jaxrs.client.WebClient; import org.apache.syncope.client.console.SyncopeConsoleSession; -import org.apache.syncope.client.console.annotations.IdMPage; import org.apache.syncope.client.console.pages.BasePage; import org.apache.syncope.client.console.rest.ConnectorRestClient; import org.apache.syncope.client.console.rest.ResourceRestClient; @@ -46,6 +45,9 @@ import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel; import org.apache.syncope.client.console.wizards.resources.AbstractResourceWizardBuilder.CreateEvent; import org.apache.syncope.client.ui.commons.Constants; +import org.apache.syncope.client.ui.commons.annotations.IdMPage; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.keymaster.client.api.ServiceOps; import org.apache.syncope.common.lib.to.ConnInstanceTO; import org.apache.syncope.common.lib.to.ResourceTO; import org.apache.syncope.common.lib.types.IdMEntitlement; @@ -71,6 +73,12 @@ public class Topology extends BasePage { public static final String CONNECTOR_SERVER_LOCATION_PREFIX = "connid://"; + @SpringBean + protected ServiceOps serviceOps; + + @SpringBean + protected ConfParamOps confParamOps; + @SpringBean protected ResourceRestClient resourceRestClient; @@ -158,8 +166,9 @@ public Topology() { body.add(modal.size(Modal.Size.Large)); modal.setWindowClosedCallback(target -> modal.show(false)); - TopologyWebSocketBehavior websocket = new TopologyWebSocketBehavior(); - body.add(websocket); + TopologyWebSocketBehavior websocket = new TopologyWebSocketBehavior( + serviceOps, confParamOps, connectorRestClient, resourceRestClient); + webSocketBehavior.add(websocket); togglePanel = new TopologyTogglePanel("toggle", getPageReference()); body.add(togglePanel); @@ -188,6 +197,26 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { } }, ActionLink.ActionType.ZOOM_OUT, IdMEntitlement.CONNECTOR_LIST).disableIndicator().hideLabel(); + zoomActionPanel.add(new ActionLink<>() { + + private static final long serialVersionUID = -3722207913631435501L; + + @Override + public void onClick(final AjaxRequestTarget target, final Serializable ignore) { + target.appendJavaScript("autoLayoutTree({ centerInView: false });"); + } + }, ActionLink.ActionType.AUTO_LAYOUT, IdMEntitlement.CONNECTOR_LIST).disableIndicator().hideLabel(); + + zoomActionPanel.add(new ActionLink<>() { + + private static final long serialVersionUID = -3722207913631435501L; + + @Override + public void onClick(final AjaxRequestTarget target, final Serializable ignore) { + target.appendJavaScript("recenterToTree();"); + } + }, ActionLink.ActionType.RECENTER, IdMEntitlement.CONNECTOR_LIST).disableIndicator().hideLabel(); + body.add(zoomActionPanel); // ----------------------------------------- @@ -240,7 +269,7 @@ protected void populateItem(final ListItem item) { topologynode.setX(x); topologynode.setY(y); - servers.put(String.class.cast(topologynode.getKey()), topologynode); + servers.put(topologynode.getKey(), topologynode); item.add(topologyNodePanel("cs", topologynode, false)); @@ -279,7 +308,7 @@ protected void populateItem(final ListItem item) { topologynode.setX(x); topologynode.setY(y); - servers.put(String.class.cast(topologynode.getKey()), topologynode); + servers.put(topologynode.getKey(), topologynode); item.add(topologyNodePanel("fp", topologynode, false)); @@ -336,7 +365,7 @@ protected void populateItem(final ListItem item) { topologynode.setX(x); topologynode.setY(y); - connectors.put(String.class.cast(topologynode.getKey()), topologynode); + connectors.put(topologynode.getKey(), topologynode); item.add(topologyNodePanel("conn", topologynode, conn.isErrored())); // Update connections @@ -455,6 +484,10 @@ public void renderHead(final Component component, final IHeaderResponse response jsPlumbConf.append(String.format(Locale.US, "activate(%.2f);", 0.68f)); createConnections(connections).forEach(jsPlumbConf::append); + // Apply the tree layout on first load (when no saved node positions exist yet). + jsPlumbConf.append("var __topo=getTopology();var __hasPos=false;" + + "for(var __k in __topo){if(__k!=='__zoom__'){__hasPos=true;break;}}" + + "if(!__hasPos){autoLayoutTree({ centerInView: false });}"); response.render(OnDomReadyHeaderItem.forScript(jsPlumbConf.toString())); } @@ -512,7 +545,7 @@ private static List createConnections(final Map list = new ArrayList<>(); targets.forEach((key, value) -> value.forEach((label, node) -> list.add( - String.format("connect('%s','%s','%s');", key, label, node.getKind())))); + "connect('%s','%s','%s');".formatted(key, label, node.getKind())))); return list; } @@ -530,8 +563,8 @@ private TopologyNodePanel topologyNodePanel(final String id, final TopologyNode @Override public void renderHead(final Component component, final IHeaderResponse response) { - response.render(OnDomReadyHeaderItem.forScript(String.format("setPosition('%s', %d, %d)", - node.getKey(), node.getX(), node.getY()))); + response.render(OnDomReadyHeaderItem.forScript("setPosition('%s', %d, %d)". + formatted(node.getKey(), node.getX(), node.getY()))); } }); @@ -547,10 +580,10 @@ protected String findIndicatorId() { @Override protected void onEvent(final AjaxRequestTarget target) { togglePanel.toggleWithContent(target, node); - target.appendJavaScript(String.format( + target.appendJavaScript( "$('.window').removeClass(\"active-window\").addClass(\"inactive-window\"); " + "$(document.getElementById('%s'))." - + "removeClass(\"inactive-window\").addClass(\"active-window\");", node.getKey())); + + "removeClass(\"inactive-window\").addClass(\"active-window\");".formatted(node.getKey())); } }); @@ -560,29 +593,25 @@ protected void onEvent(final AjaxRequestTarget target) { } @Override - @SuppressWarnings("unchecked") public void onEvent(final IEvent event) { super.onEvent(event); - if (event.getPayload() instanceof CreateEvent) { - CreateEvent resourceCreateEvent = CreateEvent.class.cast(event.getPayload()); - + if (event.getPayload() instanceof CreateEvent payload) { TopologyNode node = new TopologyNode( - resourceCreateEvent.getKey(), - resourceCreateEvent.getDisplayName(), - resourceCreateEvent.getKind()); + payload.getKey(), + payload.getDisplayName(), + payload.getKind()); newlyCreated.getModelObject().add(node); - resourceCreateEvent.getTarget().add(newlyCreatedContainer); + payload.getTarget().add(newlyCreatedContainer); - resourceCreateEvent.getTarget().appendJavaScript(String.format( + payload.getTarget().appendJavaScript( "window.Wicket.WebSocket.send('" - + "{\"kind\":\"%s\",\"target\":\"%s\",\"source\":\"%s\",\"scope\":\"%s\"}" - + "');", - SupportedOperation.ADD_ENDPOINT, - resourceCreateEvent.getKey(), - resourceCreateEvent.getParent(), - resourceCreateEvent.getKind())); + + "{\"kind\":\"%s\",\"target\":\"%s\",\"source\":\"%s\",\"scope\":\"%s\"}');".formatted( + SupportedOperation.ADD_ENDPOINT, + payload.getKey(), + payload.getParent(), + payload.getKind())); } } } 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 2ff8db53785..3cde0ec748f 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 @@ -35,17 +35,16 @@ import org.apache.syncope.client.console.tasks.PullTasks; import org.apache.syncope.client.console.tasks.PushTasks; import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal; -import org.apache.syncope.client.console.wicket.markup.html.form.IndicatingOnConfirmAjaxLink; import org.apache.syncope.client.console.wizards.resources.ConnectorWizardBuilder; import org.apache.syncope.client.console.wizards.resources.ResourceProvision; import org.apache.syncope.client.console.wizards.resources.ResourceProvisionPanel; import org.apache.syncope.client.console.wizards.resources.ResourceWizardBuilder; import org.apache.syncope.client.ui.commons.Constants; +import org.apache.syncope.client.ui.commons.markup.html.form.IndicatingOnConfirmAjaxLink; 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.to.ConnInstanceTO; -import org.apache.syncope.common.lib.to.Provision; import org.apache.syncope.common.lib.to.ResourceTO; import org.apache.syncope.common.lib.types.IdMEntitlement; import org.apache.syncope.common.lib.types.IdRepoEntitlement; @@ -240,8 +239,8 @@ private Fragment getConnectorFragment(final TopologyNode node, final PageReferen @Override public void onClick(final AjaxRequestTarget target) { try { - connectorRestClient.delete(String.class.cast(node.getKey())); - target.appendJavaScript(String.format("jsPlumb.remove('%s');", node.getKey())); + connectorRestClient.delete(node.getKey()); + target.appendJavaScript("jsPlumb.remove('%s');".formatted(node.getKey())); SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); toggle(target, false); } catch (SyncopeClientException e) { @@ -261,7 +260,7 @@ public void onClick(final AjaxRequestTarget target) { @Override public void onClick(final AjaxRequestTarget target) { final ResourceTO modelObject = new ResourceTO(); - modelObject.setConnector(String.class.cast(node.getKey())); + modelObject.setConnector(node.getKey()); modelObject.setConnectorDisplayName(node.getDisplayName()); final IModel model = new CompoundPropertyModel<>(modelObject); @@ -290,7 +289,7 @@ public String getAjaxIndicatorMarkupId() { @Override public void onClick(final AjaxRequestTarget target) { - ConnInstanceTO connInstance = connectorRestClient.read(String.class.cast(node.getKey())); + ConnInstanceTO connInstance = connectorRestClient.read(node.getKey()); final IModel model = new CompoundPropertyModel<>(connInstance); modal.setFormModel(model); @@ -360,7 +359,7 @@ public String getAjaxIndicatorMarkupId() { } }; MetaDataRoleAuthorizationStrategy.authorize(history, RENDER, - String.format("%s,%s", IdMEntitlement.CONNECTOR_READ, IdRepoEntitlement.AUDIT_LIST)); + "%s,%s".formatted(IdMEntitlement.CONNECTOR_READ, IdRepoEntitlement.AUDIT_LIST)); fragment.add(history); return fragment; @@ -377,7 +376,7 @@ private Fragment getResourceFragment(final TopologyNode node, final PageReferenc public void onClick(final AjaxRequestTarget target) { try { resourceRestClient.delete(node.getKey()); - target.appendJavaScript(String.format("jsPlumb.remove('%s');", node.getKey())); + target.appendJavaScript("jsPlumb.remove('%s');".formatted(node.getKey())); SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); toggle(target, false); } catch (SyncopeClientException e) { @@ -623,7 +622,7 @@ public String getAjaxIndicatorMarkupId() { } }; MetaDataRoleAuthorizationStrategy.authorize(history, RENDER, - String.format("%s,%s", IdMEntitlement.RESOURCE_READ, IdRepoEntitlement.AUDIT_LIST)); + "%s,%s".formatted(IdMEntitlement.RESOURCE_READ, IdRepoEntitlement.AUDIT_LIST)); fragment.add(history); AjaxLink clone = new IndicatingAjaxLink<>("clone") { @@ -636,12 +635,6 @@ public void onClick(final AjaxRequestTarget target) { ResourceTO resource = resourceRestClient.read(node.getKey()); resource.setKey("Copy of " + node.getKey()); - for (Provision provision : resource.getProvisions()) { - if (provision.getMapping() != null) { - provision.getMapping().getLinkingItems().clear(); - } - provision.getVirSchemas().clear(); - } target.add(modal.setContent(new ResourceWizardBuilder( resource, resourceRestClient, connectorRestClient, pageRef). build(BaseModal.CONTENT_ID, AjaxWizard.Mode.CREATE))); 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 d0cb924209b..3d695535333 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 @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; @@ -33,17 +34,16 @@ import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.rest.ConnectorRestClient; import org.apache.syncope.client.console.rest.ResourceRestClient; +import org.apache.syncope.client.console.wicket.ws.BasePageWebSocketBehavior; 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.wicket.protocol.ws.api.WebSocketBehavior; import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler; import org.apache.wicket.protocol.ws.api.message.TextMessage; -import org.apache.wicket.spring.injection.annot.SpringBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class TopologyWebSocketBehavior extends WebSocketBehavior { +public class TopologyWebSocketBehavior extends BasePageWebSocketBehavior.OnMessageChild { private static final long serialVersionUID = -1653665542635275551L; @@ -55,49 +55,13 @@ public class TopologyWebSocketBehavior extends WebSocketBehavior { protected static final String RESOURCE_TEST_TIMEOUT_PARAMETER = "resource.test.timeout"; - protected static void timeoutHandlingConnectionChecker( - final Checker checker, - final Integer timeout, - final Map responses, - final Set running) { - - String response = null; - try { - if (timeout == null || timeout < 0) { - LOG.debug("No timeouts for resource connection checking ... "); - response = checker.call(); - } else if (timeout > 0) { - LOG.debug("Timeouts provided for resource connection checking ... "); - response = SyncopeConsoleSession.get().execute(checker).get(timeout, TimeUnit.SECONDS); - } - } catch (InterruptedException | TimeoutException e) { - LOG.warn("Connection with {} timed out", checker.key); - response = String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", - TopologyNode.Status.UNREACHABLE, checker.key); - } catch (Exception e) { - LOG.error("Unexpected exception connecting to {}", checker.key, e); - response = String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", - TopologyNode.Status.FAILURE, checker.key); - } - - if (response != null) { - responses.put(checker.key, response); - } - - running.remove(checker.key); - } - - @SpringBean - protected ServiceOps serviceOps; + protected final ServiceOps serviceOps; - @SpringBean - protected ConfParamOps confParamOps; + protected final ConfParamOps confParamOps; - @SpringBean - protected ConnectorRestClient connectorRestClient; + protected final ConnectorRestClient connectorRestClient; - @SpringBean - protected ResourceRestClient resourceRestClient; + protected final ResourceRestClient resourceRestClient; protected final Map connectors = Collections.synchronizedMap(new HashMap<>()); @@ -117,7 +81,17 @@ protected static void timeoutHandlingConnectionChecker( protected Integer resourceTestTimeout = null; - public TopologyWebSocketBehavior() { + public TopologyWebSocketBehavior( + final ServiceOps serviceOps, + final ConfParamOps confParamOps, + final ConnectorRestClient connectorRestClient, + final ResourceRestClient resourceRestClient) { + + this.serviceOps = serviceOps; + this.confParamOps = confParamOps; + this.connectorRestClient = connectorRestClient; + this.resourceRestClient = resourceRestClient; + coreAddress = serviceOps.get(NetworkService.Type.CORE).getAddress(); domain = SyncopeConsoleSession.get().getDomain(); jwt = SyncopeConsoleSession.get().getJWT(); @@ -132,67 +106,97 @@ public TopologyWebSocketBehavior() { } } + protected void timeoutHandlingConnectionChecker( + final Checker checker, + final Integer timeout, + final Map responses, + final Set running) { + + String response; + try { + if (timeout == null || timeout < 0) { + LOG.debug("No timeouts for resource connection checking ... "); + response = checker.call(); + } else { + LOG.debug("Timeouts provided for resource connection checking ... "); + response = SyncopeConsoleSession.get().execute(checker).get(timeout, TimeUnit.SECONDS); + } + } catch (InterruptedException | TimeoutException e) { + LOG.warn("Connection with {} timed out", checker.key); + response = "{ \"status\": \"%s\", \"target\": \"%s\"}". + formatted(TopologyNode.Status.UNREACHABLE, checker.key); + } catch (Exception e) { + LOG.error("Unexpected exception connecting to {}", checker.key, e); + response = "{ \"status\": \"%s\", \"target\": \"%s\"}". + formatted(TopologyNode.Status.FAILURE, checker.key); + } + + Optional.ofNullable(response).ifPresent(r -> responses.put(checker.key, r)); + + running.remove(checker.key); + } + @Override protected void onMessage(final WebSocketRequestHandler handler, final TextMessage message) { try { JsonNode obj = MAPPER.readTree(message.getText()); switch (Topology.SupportedOperation.valueOf(obj.get("kind").asText())) { - case CHECK_CONNECTOR: + case CHECK_CONNECTOR -> { String ckey = obj.get("target").asText(); if (connectors.containsKey(ckey)) { handler.push(connectors.get(ckey)); } else { - handler.push(String.format( - "{ \"status\": \"%s\", \"target\": \"%s\"}", TopologyNode.Status.UNKNOWN, ckey)); + handler.push("{ \"status\": \"%s\", \"target\": \"%s\"}". + formatted(TopologyNode.Status.UNKNOWN, ckey)); } if (runningConnCheck.contains(ckey)) { LOG.debug("Running connection check for connector {}", ckey); } else { try { - SyncopeConsoleSession.get().execute(() -> timeoutHandlingConnectionChecker( - new ConnectorChecker(ckey), connectorTestTimeout, connectors, runningConnCheck)); + timeoutHandlingConnectionChecker( + new ConnectorChecker(ckey), connectorTestTimeout, connectors, runningConnCheck); runningConnCheck.add(ckey); } catch (Exception e) { LOG.error("Unexpected error", e); } } - break; + } - case CHECK_RESOURCE: + case CHECK_RESOURCE -> { String rkey = obj.get("target").asText(); if (resources.containsKey(rkey)) { handler.push(resources.get(rkey)); } else { - handler.push(String.format( - "{ \"status\": \"%s\", \"target\": \"%s\"}", TopologyNode.Status.UNKNOWN, rkey)); + handler.push("{ \"status\": \"%s\", \"target\": \"%s\"}". + formatted(TopologyNode.Status.UNKNOWN, rkey)); } if (runningResCheck.contains(rkey)) { LOG.debug("Running connection check for resource {}", rkey); } else { try { - SyncopeConsoleSession.get().execute(() -> timeoutHandlingConnectionChecker( - new ResourceChecker(rkey), resourceTestTimeout, resources, runningResCheck)); + timeoutHandlingConnectionChecker( + new ResourceChecker(rkey), resourceTestTimeout, resources, runningResCheck); runningResCheck.add(rkey); } catch (Exception e) { LOG.error("Unexpected error", e); } } - break; + } - case ADD_ENDPOINT: - handler.appendJavaScript(String.format("addEndpoint('%s', '%s', '%s');", + case ADD_ENDPOINT -> + handler.appendJavaScript("addEndpoint('%s', '%s', '%s');".formatted( obj.get("source").asText(), obj.get("target").asText(), obj.get("scope").asText())); - break; - default: + default -> { + } } } catch (IOException e) { LOG.error("Error managing websocket message", e); @@ -207,7 +211,7 @@ public boolean resCheckDone(final Collection resources) { return this.resources.keySet().containsAll(resources); } - private abstract class Checker implements Callable { + protected abstract class Checker implements Callable { protected final String key; @@ -216,7 +220,7 @@ private abstract class Checker implements Callable { } } - private class ConnectorChecker extends Checker { + protected class ConnectorChecker extends Checker { ConnectorChecker(final String key) { super(key); @@ -225,18 +229,17 @@ private class ConnectorChecker extends Checker { @Override public String call() { try { - return String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", + return "{ \"status\": \"%s\", \"target\": \"%s\"}".formatted( connectorRestClient.check(coreAddress, domain, jwt, key) ? TopologyNode.Status.REACHABLE : TopologyNode.Status.UNREACHABLE, key); } catch (Exception e) { LOG.warn("Error checking connection for {}", key, e); - return String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", - TopologyNode.Status.FAILURE, key); + return "{ \"status\": \"%s\", \"target\": \"%s\"}".formatted(TopologyNode.Status.FAILURE, key); } } } - private class ResourceChecker extends Checker { + protected class ResourceChecker extends Checker { ResourceChecker(final String key) { super(key); @@ -245,13 +248,12 @@ private class ResourceChecker extends Checker { @Override public String call() { try { - return String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", + return "{ \"status\": \"%s\", \"target\": \"%s\"}".formatted( resourceRestClient.check(coreAddress, domain, jwt, key) ? TopologyNode.Status.REACHABLE : TopologyNode.Status.UNREACHABLE, key); } catch (Exception e) { LOG.warn("Error checking connection for {}", key, e); - return String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", - TopologyNode.Status.FAILURE, key); + return "{ \"status\": \"%s\", \"target\": \"%s\"}".formatted(TopologyNode.Status.FAILURE, key); } } } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/list/ConnConfPropertyListView.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/list/ConnConfPropertyListView.java index f45b5d8472f..2bac130c1c1 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/list/ConnConfPropertyListView.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/list/ConnConfPropertyListView.java @@ -25,7 +25,6 @@ import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.console.commons.IdMConstants; -import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel; 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.markup.html.form.AbstractFieldPanel; @@ -34,6 +33,7 @@ import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPasswordFieldPanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel; +import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel; import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.ComponentTag; @@ -191,7 +191,7 @@ public void renderHead(final IHeaderResponse response) { super.renderHead(response); if (getModelObject().isEmpty()) { response.render(OnDomReadyHeaderItem.forScript( - String.format("$('#emptyPlaceholder').append(\"%s\")", getString("property.empty.list")))); + "$('#emptyPlaceholder').append(\"%s\")".formatted(getString("property.empty.list")))); } } } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/widgets/RemediationsWidget.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/widgets/RemediationsWidget.java index 3fac6f481b3..5bd8c174166 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/widgets/RemediationsWidget.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/widgets/RemediationsWidget.java @@ -20,102 +20,37 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.image.Icon; import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType; -import java.time.Duration; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.List; import org.apache.syncope.client.console.BookmarkablePageLinkBuilder; import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.pages.Remediations; import org.apache.syncope.client.console.rest.RemediationRestClient; -import org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior; import org.apache.syncope.client.ui.commons.annotations.ExtWidget; -import org.apache.syncope.common.lib.to.RemediationTO; import org.apache.syncope.common.lib.types.IdMEntitlement; import org.apache.wicket.PageReference; -import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy; -import org.apache.wicket.extensions.markup.html.repeater.util.SortParam; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.link.AbstractLink; import org.apache.wicket.markup.html.link.BookmarkablePageLink; -import org.apache.wicket.model.IModel; -import org.apache.wicket.model.util.ListModel; import org.apache.wicket.spring.injection.annot.SpringBean; @ExtWidget(priority = 0) -public class RemediationsWidget extends ExtAlertWidget { +public class RemediationsWidget extends ExtAlertWidget { private static final long serialVersionUID = 1817429725840355068L; @SpringBean protected RemediationRestClient remediationRestClient; - protected final List lastRemediations = new ArrayList<>(); + protected final boolean authorized; public RemediationsWidget(final String id, final PageReference pageRef) { super(id, pageRef); - setOutputMarkupId(true); - - latestAlertsList.add(new IndicatorAjaxTimerBehavior(Duration.of(30, ChronoUnit.SECONDS)) { - - private static final long serialVersionUID = 7298597675929755960L; - - @Override - protected void onTimer(final AjaxRequestTarget target) { - if (!latestAlerts.getObject().equals(lastRemediations)) { - refreshLatestAlerts(target); - } - } - }); - } - - public final void refreshLatestAlerts(final AjaxRequestTarget target) { - latestAlerts.getObject().clear(); - latestAlerts.getObject().addAll(lastRemediations); - - long latestAlertSize = getLatestAlertsSize(); - linkAlertsNumber.setDefaultModelObject(latestAlertSize); - target.add(linkAlertsNumber); - - headerAlertsNumber.setDefaultModelObject(latestAlertSize); - target.add(headerAlertsNumber); - - target.add(latestAlertsList); - - lastRemediations.clear(); - lastRemediations.addAll(latestAlerts.getObject()); + authorized = SyncopeConsoleSession.get().owns(IdMEntitlement.REMEDIATION_LIST); } @Override protected long getLatestAlertsSize() { - return SyncopeConsoleSession.get().owns(IdMEntitlement.REMEDIATION_LIST) - && SyncopeConsoleSession.get().owns(IdMEntitlement.REMEDIATION_READ) - ? remediationRestClient.countRemediations() - : 0L; - } - - @Override - protected IModel> getLatestAlerts() { - return new ListModel<>() { - - private static final long serialVersionUID = 541491929575585613L; - - @Override - public List getObject() { - List updatedRemediations; - if (SyncopeConsoleSession.get().owns(IdMEntitlement.REMEDIATION_LIST) - && SyncopeConsoleSession.get().owns(IdMEntitlement.REMEDIATION_READ)) { - - updatedRemediations = remediationRestClient.getRemediations( - 1, MAX_SIZE, new SortParam<>("instant", true)); - } else { - updatedRemediations = List.of(); - } - - return updatedRemediations; - } - }; + return authorized ? remediationRestClient.countRemediations() : 0L; } @Override diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountDetailsPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountDetailsPanel.java index 466b5d2c236..b91ca7d243c 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountDetailsPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountDetailsPanel.java @@ -23,9 +23,10 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.mutable.Mutable; +import org.apache.commons.lang3.mutable.MutableObject; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.client.console.rest.ResourceRestClient; import org.apache.syncope.client.lib.SyncopeClient; @@ -159,12 +160,12 @@ private void setConnObjectFieldChoices( final String resource, final String searchTerm) { - AtomicReference resourceRemoteKey = new AtomicReference<>(ConnIdSpecialName.NAME); + Mutable resourceRemoteKey = new MutableObject<>(ConnIdSpecialName.NAME); try { resourceRestClient.read(resource).getProvision(AnyTypeKind.USER.name()). flatMap(provision -> Optional.ofNullable(provision.getMapping())). flatMap(ItemContainer::getConnObjectKeyItem). - ifPresent(connObjectKeyItem -> resourceRemoteKey.set(connObjectKeyItem.getExtAttrName())); + ifPresent(connObjectKeyItem -> resourceRemoteKey.setValue(connObjectKeyItem.getExtAttrName())); } catch (Exception ex) { LOG.error("While reading mapping for resource {}", resource, ex); } @@ -182,7 +183,7 @@ private void setConnObjectFieldChoices( connObjectKeyFieldValues = items.getRight().stream(). map(item -> item.getAttr(resourceRemoteKey.get()). - map(attr -> attr.getValues().getFirst()).orElse(null)). + map(attr -> attr.getValues().getFirst()).orElse(null)). filter(Objects::nonNull). collect(Collectors.toList()); ajaxTextFieldPanel.setChoices(connObjectKeyFieldValues); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPlainAttrsPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPlainAttrsPanel.java index 1c5ef73c088..c926f94b4cc 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPlainAttrsPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPlainAttrsPanel.java @@ -25,14 +25,16 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.console.commons.LinkedAccountPlainAttrProperty; -import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel; 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.markup.html.form.AbstractFieldPanel; import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel; +import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel; import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion; import org.apache.syncope.client.ui.commons.wizards.AjaxWizard; import org.apache.syncope.client.ui.commons.wizards.any.EntityWrapper; @@ -42,6 +44,7 @@ import org.apache.syncope.common.lib.to.LinkedAccountTO; import org.apache.syncope.common.lib.to.PlainSchemaTO; import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.syncope.common.lib.types.SchemaType; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.extensions.markup.html.tabs.AbstractTab; @@ -81,8 +84,8 @@ public LinkedAccountPlainAttrsPanel( this.linkedAccountTO = modelObject.getInnerObject(); this.fixedAttrs.addAll(this.linkedAccountTO.getPlainAttrs().stream(). - filter(attrTO -> checkIsReadonlyAttr(attrTO.getSchema())). - toList()); + filter(attr -> isReadonly(attr.getSchema())). + toList()); this.userTO = userTO; add(new Accordion("plainSchemas", List.of(new AbstractTab( @@ -97,6 +100,10 @@ public WebMarkupContainer getPanel(final String panelId) { }), Model.of(0)).setOutputMarkupId(true)); } + private boolean isReadonly(final String schema) { + return schemas.isEmpty() ? true : !schemas.get(schema).isReadonly(); + } + @Override protected FormComponent checkboxToggle( final Attr attrTO, @@ -115,11 +122,10 @@ protected FormComponent checkboxToggle( return newProperty; }); - final BootstrapToggleConfig config = new BootstrapToggleConfig(). + BootstrapToggleConfig config = new BootstrapToggleConfig(). withOnStyle(BootstrapToggleConfig.Style.success). withOffStyle(BootstrapToggleConfig.Style.danger). withSize(BootstrapToggleConfig.Size.mini); - return new BootstrapToggle("externalAction", new PropertyModel<>(property, "overridable"), config) { private static final long serialVersionUID = -875219845189261873L; @@ -166,8 +172,8 @@ protected void onComponentTag(final ComponentTag tag) { private void updateAccountPlainSchemas(final LinkedAccountPlainAttrProperty property, final Boolean modelObject) { Set withoutCurrentSchema = new HashSet<>(linkedAccountTO.getPlainAttrs().stream(). - filter(attrTO -> !attrTO.getSchema().equals(property.getSchema()) - && checkIsReadonlyAttr(attrTO.getSchema())). + filter(attr -> !attr.getSchema().equals(property.getSchema()) + && isReadonly(attr.getSchema())). collect(Collectors.toSet())); linkedAccountTO.getPlainAttrs().clear(); linkedAccountTO.getPlainAttrs().addAll(withoutCurrentSchema); @@ -186,28 +192,28 @@ protected SchemaType getSchemaType() { @Override protected void setAttrs() { - List attrs = new ArrayList<>(); + List plainAttrs = new ArrayList<>(); List notReadonlyValues = schemas.values().stream(). - filter(schema -> checkIsReadonlyAttr(schema.getKey())). - collect(Collectors.toList()); + filter(schema -> !schema.isReadonly()). + toList(); setFixedAttr(notReadonlyValues); Map attrMap = EntityTOUtils.buildAttrMap(fixedAttrs); - attrs.addAll(notReadonlyValues.stream(). - map(schema -> { - Attr attrTO = new Attr(); - attrTO.setSchema(schema.getKey()); - if (attrMap.get(schema.getKey()) == null || attrMap.get(schema.getKey()).getValues().isEmpty()) { - attrTO.getValues().add(""); - } else { - attrTO = attrMap.get(schema.getKey()); - } - return attrTO; - }). - toList()); + plainAttrs.addAll(notReadonlyValues.stream().map(schema -> { + Attr attr = Optional.ofNullable(attrMap.get(schema.getKey())).orElseGet(() -> { + Attr newAttr = new Attr(); + newAttr.setSchema(schema.getKey()); + return newAttr; + }); + if ((schema.getType() != AttrSchemaType.Dropdown || !schema.isMultivalue()) && attr.getValues().isEmpty()) { + attr.getValues().add(StringUtils.EMPTY); + } + + return attr; + }).toList()); fixedAttrs.clear(); - fixedAttrs.addAll(attrs); + fixedAttrs.addAll(plainAttrs); } @Override @@ -221,10 +227,6 @@ private void setFixedAttr(final Collection values) { () -> userTO.getPlainAttr(schema.getKey()).ifPresent(fixedAttrs::add))); } - private boolean checkIsReadonlyAttr(final String schema) { - return schemas.isEmpty() ? true : !schemas.get(schema).isReadonly(); - } - private class PlainSchemasOwn extends PlainSchemas> { private static final long serialVersionUID = -4730563859116024676L; diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountWizardBuilder.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountWizardBuilder.java index 22fcd75b055..ebf2c51c1f9 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountWizardBuilder.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountWizardBuilder.java @@ -118,9 +118,8 @@ private void fixPlainAttrs(final LinkedAccountTO linkedAccountTO) { @Override protected Serializable getCreateCustomPayloadEvent(final Serializable afterObject, final AjaxRequestTarget target) { - LinkedAccountTO linkedAccountTO = LinkedAccountTO.class.cast(afterObject); return new CreateEvent( - linkedAccountTO.getConnObjectKeyValue(), + LinkedAccountTO.class.cast(afterObject).getConnObjectKeyValue(), model.getObject(), target); } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java index 96cc365bece..c788b115fc1 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java @@ -33,7 +33,6 @@ import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.wicket.Component; import org.apache.wicket.PageReference; -import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.event.IEvent; import org.apache.wicket.extensions.wizard.WizardStep; import org.apache.wicket.markup.html.WebMarkupContainer; @@ -97,16 +96,12 @@ public MergeLinkedAccountsSearchPanel( @Override public void onEvent(final IEvent event) { - if (event.getPayload() instanceof SearchClausePanel.SearchEvent) { - AjaxRequestTarget target = SearchClausePanel.SearchEvent.class.cast(event.getPayload()).getTarget(); + if (event.getPayload() instanceof SearchClausePanel.SearchEvent payload) { String fiql = "username!~" + wizardModel.getBaseUser().getUsername() + ';' + SearchUtils.buildFIQL(userSearchPanel.getModel().getObject(), SyncopeClient.getUserSearchConditionBuilder()); - userDirectoryPanel.search(fiql, target); - } else if (event.getPayload() instanceof final AnySelectionDirectoryPanel.ItemSelection itemSelection) { - AnySelectionDirectoryPanel.ItemSelection payload = - itemSelection; - + userDirectoryPanel.search(fiql, payload.getTarget()); + } else if (event.getPayload() instanceof final AnySelectionDirectoryPanel.ItemSelection payload) { AnyTO sel = payload.getSelection(); wizardModel.setMergingUser(userRestClient.read(sel.getKey())); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/Resources.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/Resources.java index ac1d1e131cd..029b76b97a3 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/Resources.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/Resources.java @@ -18,20 +18,63 @@ */ package org.apache.syncope.client.console.wizards.any; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.console.SyncopeWebApplication; -import org.apache.syncope.client.ui.commons.wizards.any.AbstractResources; +import org.apache.syncope.client.ui.commons.ajax.markup.html.LabelInfo; +import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel; import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper; +import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper; import org.apache.syncope.common.lib.to.AnyTO; import org.apache.syncope.common.lib.types.IdMEntitlement; import org.apache.wicket.authroles.authorization.strategies.role.metadata.ActionPermissions; import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy; +import org.apache.wicket.extensions.wizard.WizardModel; +import org.apache.wicket.extensions.wizard.WizardStep; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.model.PropertyModel; +import org.apache.wicket.model.util.ListModel; -public class Resources extends AbstractResources { +public class Resources extends WizardStep implements WizardModel.ICondition { private static final long serialVersionUID = 702900610508752856L; + protected final AnyTO anyTO; + + protected final ListModel available; + public Resources(final AnyWrapper modelObject) { - super(modelObject); + anyTO = modelObject.getInnerObject(); + + if (modelObject instanceof UserWrapper userWrapper + && userWrapper.getPreviousUserTO() != null + && !modelObject.getInnerObject().getResources().equals( + userWrapper.getPreviousUserTO().getResources())) { + + add(new LabelInfo("changed", StringUtils.EMPTY)); + } else { + add(new Label("changed", StringUtils.EMPTY)); + } + + this.setOutputMarkupId(true); + this.available = new ListModel<>(List.of()); + + add(new AjaxPalettePanel.Builder().build("resources", new PropertyModel<>(anyTO, "resources") { + + private static final long serialVersionUID = 3799387950428254072L; + + @Override + public List getObject() { + return new ArrayList<>(anyTO.getResources()); + } + + @Override + public void setObject(final List object) { + anyTO.getResources().clear(); + anyTO.getResources().addAll(object); + } + }, available).hideLabel().setOutputMarkupId(true)); // ----------------------------------------------------------------- // Pre-Authorizations @@ -47,10 +90,10 @@ public Resources(final AnyWrapper modelObject) { public boolean evaluate() { if (SyncopeWebApplication.get().getSecuritySettings(). getAuthorizationStrategy().isActionAuthorized(this, RENDER)) { - available.setObject(SyncopeWebApplication.get().getResourceProvider().get()); + + available.setObject(SyncopeWebApplication.get().getResourceProvider().get(anyTO.getType())); return !available.getObject().isEmpty(); - } else { - return false; } + return false; } } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java index 1918fa55d6f..b93fd2ab03b 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java @@ -21,7 +21,6 @@ import java.util.Iterator; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.client.console.SyncopeWebApplication; import org.apache.syncope.client.console.commons.RealmsUtils; @@ -69,7 +68,7 @@ protected Iterator getChoices(final String input) { return (RealmsUtils.checkInput(input) ? (realmRestClient.search(fullRealmsTree ? RealmsUtils.buildBaseQuery() - : RealmsUtils.buildKeywordQuery(input)).getResult()) + : RealmsUtils.buildNameQuery(input)).getResult()) : List.of()).stream(). map(RealmTO::getFullPath).iterator(); } @@ -91,8 +90,7 @@ protected Iterator getChoices(final String input) { "bundleName", new PropertyModel<>(connInstanceTO, "bundleName"), false); bundleName.setEnabled(connInstanceTO.getKey() == null || connInstanceTO.isErrored()); - bundleName.setChoices(bundles.stream().map(ConnIdBundle::getBundleName). - distinct().sorted().collect(Collectors.toList())); + bundleName.setChoices(bundles.stream().map(ConnIdBundle::getBundleName).distinct().sorted().toList()); bundleName.getField().setOutputMarkupId(true); add(bundleName.addRequiredLabel().setOutputMarkupId(true)); @@ -123,7 +121,7 @@ protected void onUpdate(final AjaxRequestTarget target) { List> connectors = bundles.stream(). filter(bundle -> bundle.getBundleName().equals(connInstanceTO.getBundleName())). map(bundle -> Pair.of(bundle.getConnectorName(), bundle.getVersion())). - toList(); + toList(); if (connectors.size() == 1) { Pair entry = connectors.getFirst(); @@ -135,11 +133,9 @@ protected void onUpdate(final AjaxRequestTarget target) { version.getField().setModelObject(entry.getRight()); version.setChoices(List.of(entry.getRight())); } else { - connectorName.setChoices(connectors.stream(). - map(Pair::getLeft).distinct().sorted().collect(Collectors.toList())); + connectorName.setChoices(connectors.stream().map(Pair::getLeft).distinct().sorted().toList()); - List versions = connectors.stream(). - map(Pair::getRight).distinct().sorted().collect(Collectors.toList()); + List versions = connectors.stream().map(Pair::getRight).distinct().sorted().toList(); version.setChoices(versions); if (versions.size() == 1) { @@ -165,7 +161,7 @@ protected void onUpdate(final AjaxRequestTarget target) { List versions = bundles.stream(). filter(bundle -> bundle.getBundleName().equals(connInstanceTO.getBundleName()) && bundle.getConnectorName().equals(connInstanceTO.getConnectorName())). - map(ConnIdBundle::getVersion).collect(Collectors.toList()); + map(ConnIdBundle::getVersion).toList(); if (versions.size() == 1) { connInstanceTO.setVersion(versions.getFirst()); version.getField().setModelObject(versions.getFirst()); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorWizardBuilder.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorWizardBuilder.java index 7ecef671068..ab627674a4c 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorWizardBuilder.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorWizardBuilder.java @@ -21,7 +21,6 @@ import java.io.Serializable; import java.net.URI; import java.util.List; -import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.client.console.rest.ConnectorRestClient; import org.apache.syncope.client.console.topology.TopologyNode; @@ -53,7 +52,7 @@ public ConnectorWizardBuilder( super(defaultItem, pageRef); this.bundles = connectorRestClient.getAllBundles().stream(). - filter(object -> object.getLocation().equals(defaultItem.getLocation())).collect(Collectors.toList()); + filter(object -> object.getLocation().equals(defaultItem.getLocation())).toList(); this.connectorRestClient = connectorRestClient; } @@ -68,7 +67,7 @@ protected WizardModel buildModelSteps(final Serializable modelObject, final Wiza @Override protected Pair check(final AjaxRequestTarget target) { ConnInstanceTO connInstanceTO = ConnInstanceTO.class.cast(modelObject); - ConnIdBundle bundleTO = ConnectorWizardBuilder.getBundle(connInstanceTO, bundles); + ConnIdBundle bundleTO = getBundle(connInstanceTO, bundles); connInstanceTO.setConnectorName(bundleTO.getConnectorName()); connInstanceTO.setBundleName(bundleTO.getBundleName()); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceMappingPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceMappingPanel.java index 1441795726d..b25c7b84f4f 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceMappingPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceMappingPanel.java @@ -19,7 +19,7 @@ package org.apache.syncope.client.console.wizards.resources; import java.util.ArrayList; -import java.util.Collections; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -165,27 +165,24 @@ protected void setAttrNames(final AjaxTextFieldPanel toBeUpdated) { }); switch (provision.getAnyType()) { - case "USER": + case "USER" -> choices.addAll(ClassPathScanImplementationLookup.USER_FIELD_NAMES); - break; - case "GROUP": + case "GROUP" -> choices.addAll(ClassPathScanImplementationLookup.GROUP_FIELD_NAMES); - break; - default: + default -> choices.addAll(ClassPathScanImplementationLookup.ANY_OBJECT_FIELD_NAMES); } anyTypeClassTOs.forEach(anyTypeClassTO -> { choices.addAll(anyTypeClassTO.getPlainSchemas()); choices.addAll(anyTypeClassTO.getDerSchemas()); - choices.addAll(anyTypeClassTO.getVirSchemas()); }); } List names = new ArrayList<>(choices); - Collections.sort(names); + names.sort(Comparator.naturalOrder()); toBeUpdated.setChoices(names); } } diff --git a/client/idm/console/src/main/resources/META-INF/resources/css/_topology-theme.scss b/client/idm/console/src/main/resources/META-INF/resources/css/_topology-theme.scss new file mode 100644 index 00000000000..3424abcabd6 --- /dev/null +++ b/client/idm/console/src/main/resources/META-INF/resources/css/_topology-theme.scss @@ -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. + */ + +:root { + /* + * Topology + */ + --topology-node-border: rgba(15, 23, 42, 0.14); + --topology-node-shadow: 0 10px 28px rgba(2, 6, 23, 0.10); + --topology-node-shadow-hover: 0 14px 34px rgba(2, 6, 23, 0.14); + + --topology-root-bg: rgba(22, 163, 74, 0.18); + --topology-cs-bg: rgba(14, 165, 233, 0.12); + --topology-conn-bg: rgba(139, 92, 246, 0.10); + --topology-conn-errored-bg: rgba(244, 63, 94, 0.18); + --topology-res-bg: rgba(245, 158, 11, 0.14); + + /* + * Topology toolbar + */ + --topology-toolbar-bg: rgba(255, 255, 255, 0.92); + --topology-toolbar-bg-hover: rgba(255, 255, 255, 0.98); + --topology-toolbar-border: rgba(15, 23, 42, 0.10); + --topology-toolbar-fg: rgba(15, 23, 42, 0.78); + --topology-toolbar-fg-hover: rgba(15, 23, 42, 0.92); + --topology-toolbar-shadow: 0 8px 18px rgba(2, 6, 23, 0.10); + --topology-toolbar-shadow-hover: 0 10px 22px rgba(2, 6, 23, 0.14); + --topology-focus-ring: rgba(14, 165, 233, 0.25); + + /* + * Topology labels + */ + --topology-label-bg: #fff; + --topology-label-color: #444; + --topology-label-border: #808080; + --topology-label-hover-bg: #5c96bc; + --topology-label-hover-color: #fff; + --topology-label-hover-border: #fff; + + /* + * Topology tooltip + */ + --topology-tooltip-bg: #eee; + --topology-tooltip-color: #000; + --topology-tooltip-border: #bbb; +} + +[data-bs-theme="dark"] { + /* + * Application background: #2B3035 + * + * Surfaces are deliberately lighter to maintain contrast. + */ + --topology-node-border: rgba(255, 255, 255, 0.22); + --topology-node-shadow: 0 10px 28px rgba(0, 0, 0, 0.38); + --topology-node-shadow-hover: 0 14px 34px rgba(0, 0, 0, 0.52); + + --topology-root-bg: rgba(55, 210, 115, 0.30); + --topology-cs-bg: rgba(70, 185, 235, 0.27); + --topology-conn-bg: rgba(160, 120, 245, 0.25); + --topology-conn-errored-bg: rgba(245, 90, 105, 0.36); + --topology-res-bg: rgba(255, 195, 65, 0.30); + + --topology-toolbar-bg: rgba(65, 71, 77, 0.96); + --topology-toolbar-bg-hover: rgba(78, 85, 92, 0.98); + --topology-toolbar-border: rgba(255, 255, 255, 0.18); + --topology-toolbar-fg: rgba(245, 247, 249, 0.90); + --topology-toolbar-fg-hover: #fff; + --topology-toolbar-shadow: 0 8px 18px rgba(0, 0, 0, 0.40); + --topology-toolbar-shadow-hover: 0 10px 22px rgba(0, 0, 0, 0.52); + --topology-focus-ring: rgba(80, 190, 240, 0.45); + + --topology-label-bg: #414950; + --topology-label-color: #f5f5f5; + --topology-label-border: #9aa3ab; + --topology-label-hover-bg: #5c9bc3; + --topology-label-hover-color: #fff; + --topology-label-hover-border: #d9e2e8; + + --topology-tooltip-bg: #4a525a; + --topology-tooltip-color: #fff; + --topology-tooltip-border: #aeb7bf; +} diff --git a/client/idm/console/src/main/resources/META-INF/resources/css/topology.scss b/client/idm/console/src/main/resources/META-INF/resources/css/topology.scss new file mode 100644 index 00000000000..52674d114bc --- /dev/null +++ b/client/idm/console/src/main/resources/META-INF/resources/css/topology.scss @@ -0,0 +1,349 @@ +/* + * 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. + */ + +@import 'theme'; +@import 'topology-theme'; +@import 'utils'; + +.content-wrapper.accent-success { + overflow: hidden; +} + +/* + * Topology + */ + +#topology { + position: relative; + border: 0; + height: 780px; + overflow: hidden; + cursor: grab; +} + +#topology.topology-panning { + cursor: grabbing; +} + +#drawing { + height: 5000px; + width: 5000px; + display: block; + position: relative; + top: -2490px; + left: -2270px; +} + +/* + * Topology nodes + * + * IMPORTANT: + * position: absolute is required by jsPlumb. + */ +#drawing .window { + position: absolute; + + opacity: 1; + z-index: 20; + + width: 200px; + min-height: 72px; + height: auto; + + padding: 10px 12px; + + border: 1px solid var(--topology-node-border); + border-radius: 14px; + box-shadow: var(--topology-node-shadow); + + background-color: var(--topology-window-bg); + color: var(--topology-window-color); + + line-height: normal; + text-align: center; + + display: flex; + align-items: center; + justify-content: center; + + box-sizing: border-box; + + cursor: pointer; +} + +#drawing .window:hover { + box-shadow: var(--topology-node-shadow-hover); +} + +#drawing .window.topology_root { + background-color: var(--topology-root-bg); +} + +#drawing .window.topology_cs { + background-color: var(--topology-cs-bg); +} + +#drawing .window.topology_conn { + background-color: var(--topology-conn-bg); +} + +#drawing .window.topology_conn_errored { + background-color: var(--topology-conn-errored-bg); +} + +#drawing .window.topology_res { + background-color: var(--topology-res-bg); +} + +#drawing .window p { + margin: 0 !important; + + font-weight: 700; + letter-spacing: 0.2px; + font-size: 1.25em; + line-height: 1.25; +} + +/* + * Disable the old CSS tooltip because the topology + * already provides its own interaction. + */ +#drawing .window[data-original-title]:hover::after { + content: none !important; + display: none !important; +} + +/* + * jsPlumb + */ + +._jsPlumb_dragging { + z-index: 4000; +} + +#drawing ._jsPlumb_overlay.component.label, +.aLabel { + background-color: var(--topology-label-bg); + color: var(--topology-label-color); + border: 1px solid var(--topology-label-border); +} + +#drawing ._jsPlumb_overlay.component.label { + border-radius: 0.5em; + font-size: 0.8em; + opacity: 0.8; + padding: 0.5em; +} + +.aLabel { + padding: 0.4em; + + font: 12px sans-serif; + + z-index: 21; + opacity: 0.8; + filter: alpha(opacity = 80); + + cursor: pointer; + + &._jsPlumb_hover { + background-color: var(--topology-label-hover-bg); + color: var(--topology-label-hover-color); + border: 1px solid var(--topology-label-hover-border); + } +} + +/* + * Zoom toolbar + */ + +#zoom { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 6px; + + ul.menu { + display: flex; + gap: 6px; + + margin: 0; + padding: 0; + + list-style: none; + } + + ul.menu > li { + margin: 0; + padding: 0; + } + + ul.menu > li > a.btn, + .topology-toolbar-btn { + appearance: none; + + border: 1px solid var(--topology-toolbar-border); + border-radius: 999px; + + background: var(--topology-toolbar-bg); + color: var(--topology-toolbar-fg); + + padding: 6px 10px; + + line-height: 1; + + box-shadow: var(--topology-toolbar-shadow); + + transition: transform 120ms ease, + box-shadow 120ms ease, + background 120ms ease, + color 120ms ease; + } + + ul.menu > li > a.btn:hover, + .topology-toolbar-btn:hover { + color: var(--topology-toolbar-fg-hover); + background: var(--topology-toolbar-bg-hover); + + box-shadow: var(--topology-toolbar-shadow-hover); + + transform: translateY(-1px); + + text-decoration: none; + } + + ul.menu > li > a.btn:focus, + .topology-toolbar-btn:focus { + outline: none; + + box-shadow: 0 0 0 3px var(--topology-focus-ring), + var(--topology-toolbar-shadow-hover); + } + + ul.menu > li > a.btn i, + .topology-toolbar-btn i { + font-size: 1.05rem; + padding-right: 0; + } + + ul.menu > li > a { + display: inline-block; + margin-left: 5px; + } +} + +/* + * Node actions + */ + +.node-action-link { + clear: both; + + display: block; + + height: 0; + + padding: 3px 8px 0 0; + + position: absolute; + right: 0; + top: 0; + + .fa { + font-size: 14px; + display: block !important; + } + + .dropdown-menu { + border-radius: 12px; + min-width: 50px; + width: 50px; + } +} + +/* + * Legacy topology tooltip + */ + +.window[data-original-title] { + &:hover::after { + border: 1px solid var(--topology-tooltip-border); + border-radius: 0.5em; + + padding: 5px 10px; + + background: var(--topology-tooltip-bg); + color: var(--topology-tooltip-color); + + content: attr(data-original-title); + + position: relative; + right: -5px; + top: -70px; + + white-space: nowrap; + text-align: center; + + height: 20px; + margin: 0; + } +} + +/* + * Miscellaneous + */ + +.checkbox { + margin-top: 15px; + margin-bottom: 15px; +} + +div.alert-widget > a > .badge { + font-size: 9px; + line-height: 0.9; + + padding: 2px 3px; + + position: absolute; + + text-align: center; + + margin-right: -13px; + top: 0; +} + +.card.card-outline { + margin-top: 20px; + margin-bottom: 0; +} + +.panel-group.box-group { + margin-bottom: 20px; +} + +.scrollable-tab-content { + padding-top: 20px; +} + +.conn-check-wrapper { + position: absolute; + top: 0; + right: 25px; + z-index: 1; +} diff --git a/client/idm/console/src/main/resources/META-INF/resources/js/idmTopology.js b/client/idm/console/src/main/resources/META-INF/resources/js/idmTopology.js index 8592edb3725..b604f532c23 100644 --- a/client/idm/console/src/main/resources/META-INF/resources/js/idmTopology.js +++ b/client/idm/console/src/main/resources/META-INF/resources/js/idmTopology.js @@ -16,296 +16,889 @@ * specific language governing permissions and limitations * under the License. */ -var def = { - paintStyle: { - lineWidth: 2, - strokeStyle: "rgba(204,204,204, 1)", - outlineColor: "#666", - outlineWidth: 1 - }, - connectorPaintStyle: { - lineWidth: 2 - }, - anchor: "AutoDefault", - detachable: false, - endpointStyle: { - gradient: { - stops: [ - [0, "rgba(204,204,204, 1)"], [1, "rgba(180, 180, 200, 1)"] - ], - offset: 5.5, - innerRadius: 3.5 +const def = { + paintStyle: { + lineWidth: 2, + strokeStyle: "#94a3b8", + outlineColor: "rgba(15, 23, 42, 0.18)", + outlineWidth: 1.5, + lineCap: "round", + lineJoin: "round" + }, + connectorPaintStyle: { + lineWidth: 2 }, - radius: 3.5 - } + anchor: "AutoDefault", + detachable: false, + endpointStyle: { + fillStyle: "#cbd5e1", + outlineColor: "rgba(15, 23, 42, 0.18)", + outlineWidth: 1, + radius: 3 + } }; -var failedConnectorStyle = { - lineWidth: 2, - strokeStyle: "rgba(220, 220, 220, 1)", - outlineColor: "#666", - outlineWidth: 1 +const failedConnectorStyle = { + lineWidth: 2, + strokeStyle: "rgba(148, 163, 184, 0.55)", + outlineColor: "rgba(15, 23, 42, 0.18)", + outlineWidth: 1.5 }; -var failedConnectorHoverStyle = { - strokeStyle: "#FFFFFF" +const failedConnectorHoverStyle = { + strokeStyle: "#FFFFFF" }; -var failedEndpointStyle = { - gradient: { - stops: [ - [0, "rgba(220, 220, 220, 1)"], [1, "rgba(180, 180, 200, 1)"] - ], - offset: 5.5, - innerRadius: 0 - }, - radius: 0 +const failedEndpointStyle = { + fillStyle: "rgba(148, 163, 184, 0.45)", + outlineColor: "rgba(15, 23, 42, 0.18)", + outlineWidth: 1, + radius: 3 }; -var disabledConnectorStyle = { - lineWidth: 2, - strokeStyle: "rgba(255, 69, 0, 1)", - outlineColor: "#666", - outlineWidth: 1 +const disabledConnectorStyle = { + lineWidth: 2, + strokeStyle: "#fb7185", + outlineColor: "rgba(15, 23, 42, 0.18)", + outlineWidth: 1.5 }; -var disabledConnectorHoverStyle = { - strokeStyle: "#FF8C00" +const disabledConnectorHoverStyle = { + strokeStyle: "#FF8C00" }; -var disabledEndpointStyle = { - gradient: { - stops: [ - [0, "rgba(255, 69, 0, 1)"], [1, "rgba(180, 180, 200, 1)"] - ], - offset: 5.5, - innerRadius: 1 - }, - radius: 1 +const disabledEndpointStyle = { + fillStyle: "rgba(251, 113, 133, 0.8)", + outlineColor: "rgba(15, 23, 42, 0.18)", + outlineWidth: 1, + radius: 3 }; -var enabledConnectorStyle = { - lineWidth: 2, - strokeStyle: "rgba(65, 155, 30, 1)", - outlineColor: "#666", - outlineWidth: 1 +const enabledConnectorStyle = { + lineWidth: 2, + strokeStyle: "#22c55e", + outlineColor: "rgba(15, 23, 42, 0.18)", + outlineWidth: 1.5 }; -var enabledConnectorHoverStyle = { - strokeStyle: "#00FF00" +const enabledConnectorHoverStyle = { + strokeStyle: "#16a34a" }; -var enabledEndpointStyle = { - gradient: { - stops: [ - [0, "rgba(65, 155, 30, 0.1)"], [1, "rgba(180, 180, 200, 0.1)"] - ], - offset: 5.5, - innerRadius: 2 - }, - radius: 2 +const enabledEndpointStyle = { + fillStyle: "rgba(34, 197, 94, 0.35)", + outlineColor: "rgba(15, 23, 42, 0.18)", + outlineWidth: 1, + radius: 3 }; window.disable = function (targetName) { - jsPlumb.ready(function () { - jsPlumb.select({target: targetName}).setPaintStyle(disabledConnectorStyle).setHoverPaintStyle(disabledConnectorHoverStyle); - jsPlumb.selectEndpoints({element: [targetName]}).setPaintStyle(disabledEndpointStyle); - }); + jsPlumb.ready(function () { + jsPlumb.select({target: targetName}).setPaintStyle(disabledConnectorStyle).setHoverPaintStyle(disabledConnectorHoverStyle); + jsPlumb.selectEndpoints({element: [targetName]}).setPaintStyle(disabledEndpointStyle); + }); } window.enable = function (targetName) { - jsPlumb.ready(function () { - jsPlumb.select({target: targetName}).setPaintStyle(enabledConnectorStyle).setHoverPaintStyle(enabledConnectorHoverStyle); - jsPlumb.selectEndpoints({element: [targetName]}).setPaintStyle(enabledEndpointStyle); - }); + jsPlumb.ready(function () { + jsPlumb.select({target: targetName}).setPaintStyle(enabledConnectorStyle).setHoverPaintStyle(enabledConnectorHoverStyle); + jsPlumb.selectEndpoints({element: [targetName]}).setPaintStyle(enabledEndpointStyle); + }); } window.failure = function (targetName) { - jsPlumb.ready(function () { - jsPlumb.select({target: targetName}).setPaintStyle(failedConnectorStyle).setHoverPaintStyle(failedConnectorHoverStyle); - jsPlumb.selectEndpoints({element: [targetName]}).setPaintStyle(failedEndpointStyle); - }); + jsPlumb.ready(function () { + jsPlumb.select({target: targetName}).setPaintStyle(failedConnectorStyle).setHoverPaintStyle(failedConnectorHoverStyle); + jsPlumb.selectEndpoints({element: [targetName]}).setPaintStyle(failedEndpointStyle); + }); } window.unknown = function (targetName) { } function getTopology() { - var topology = $.cookie("topology"); - - if (topology == null) { - var val = {}; - } else { - var val = JSON.parse(decodeURIComponent(topology)); - } + const topology = $.cookie("topology"); + let val; + if (topology == null) { + val = {}; + } else { + val = JSON.parse(decodeURIComponent(topology)); + } - return val; + return val; } window.refreshPosition = function (element) { - var val = getTopology(); + const val = getTopology(); - var id = $(element).attr('id'); - var left = $(element).css('left'); - var top = $(element).css('top'); + const id = $(element).attr('id'); + const left = $(element).css('left'); + const top = $(element).css('top'); - if (val[id] == null) { - val[id] = {'top': top, 'left': left}; - } else { - val[id].top = top; - val[id].left = left; - } + if (val[id] == null) { + val[id] = {'top': top, 'left': left}; + } else { + val[id].top = top; + val[id].left = left; + } - $.cookie("topology", JSON.stringify(val), {expires: 9999}); + $.cookie("topology", JSON.stringify(val), {expires: 9999}); } window.setPosition = function (id, x, y) { - var val = getTopology(); - - try { - // We cannot use jQuery selector for id since the syntax of connector server id - var element = $(document.getElementById(id)); - - if (val[id] == null) { - element.css("left", x + "px"); - element.css("top", y + "px"); - } else { - element.css("left", val[id].left); - element.css("top", val[id].top); + const val = getTopology(); + + try { + // We cannot use jQuery selector for id since the syntax of connector server id + const element = $(document.getElementById(id)); + + if (val[id] == null) { + element.css("left", x + "px"); + element.css("top", y + "px"); + } else { + element.css("left", val[id].left); + element.css("top", val[id].top); + } + } catch (err) { + console.log("Failure setting position for ", id); } - } catch (err) { - console.log("Failure setting position for ", id); - } } window.setZoom = function (el, zoom, instance, transformOrigin) { - transformOrigin = transformOrigin || [0.5, 0.5]; - instance = instance || jsPlumb; - el = el || instance.getContainer(); + transformOrigin = transformOrigin || [0.5, 0.5]; + instance = instance || jsPlumb; + el = el || instance.getContainer(); - var p = ["webkit", "moz", "ms", "o"], - s = "scale(" + zoom + ")", - oString = (transformOrigin[0] * 100) + "% " + (transformOrigin[1] * 100) + "%"; + const p = ["webkit", "moz", "ms", "o"], + s = "scale(" + zoom + ")", + oString = (transformOrigin[0] * 100) + "% " + (transformOrigin[1] * 100) + "%"; - for (var i = 0; i < p.length; i++) { - el.style[p[i] + "Transform"] = s; - el.style[p[i] + "TransformOrigin"] = oString; - } + for (let i = 0; i < p.length; i++) { + el.style[p[i] + "Transform"] = s; + el.style[p[i] + "TransformOrigin"] = oString; + } - el.style["transform"] = s; - el.style["transformOrigin"] = oString; + el.style["transform"] = s; + el.style["transformOrigin"] = oString; - instance.setZoom(zoom); + instance.setZoom(zoom); }; window.zoomIn = function (el, instance, transformOrigin) { - var val = getTopology(); - if (val.__zoom__ == null) { - var zoom = 0.69; - } else { - var zoom = val.__zoom__ + 0.01; - } + const val = getTopology(); + let zoom; + if (val.__zoom__ == null) { + zoom = 0.69; + } else { + zoom = val.__zoom__ + 0.01; + } - setZoom(el, zoom, instance, transformOrigin); + setZoom(el, zoom, instance, transformOrigin); - val['__zoom__'] = zoom; - $.cookie("topology", JSON.stringify(val), {expires: 9999}); + val['__zoom__'] = zoom; + $.cookie("topology", JSON.stringify(val), {expires: 9999}); }; window.zoomOut = function (el, instance, transformOrigin) { - var val = getTopology(); - if (val.__zoom__ == null) { - var zoom = 0.67; - } else { - var zoom = val.__zoom__ - 0.01; - } + const val = getTopology(); + let zoom; + if (val.__zoom__ == null) { + zoom = 0.67; + } else { + zoom = val.__zoom__ - 0.01; + } - setZoom(el, zoom, instance, transformOrigin); + setZoom(el, zoom, instance, transformOrigin); - val['__zoom__'] = zoom; - $.cookie("topology", JSON.stringify(val), {expires: 9999}); + val['__zoom__'] = zoom; + $.cookie("topology", JSON.stringify(val), {expires: 9999}); }; window.connect = function (source, target, scope) { - jsPlumb.ready(function () { - if (jsPlumb.select({source: source, target: target, scope: scope}) != null) { - jsPlumb.connect({source: source, target: target, scope: scope}, def); - } - }); + jsPlumb.ready(function () { + if (jsPlumb.select({source: source, target: target, scope: scope}) != null) { + jsPlumb.connect({source: source, target: target, scope: scope}, def); + } + }); } window.activate = function (zoom) { - jsPlumb.ready(function () { - jsPlumb.draggable(jsPlumb.getSelector(".window")); - jsPlumb.setContainer("drawing"); + jsPlumb.ready(function () { + jsPlumb.draggable(jsPlumb.getSelector(".window")); + jsPlumb.setContainer("drawing"); + + jsPlumb.Defaults.MaxConnections = 1000; + + (function initTopologyPanning() { + const $topology = $("#topology"); + const $drawing = $("#drawing"); + if ($topology.length === 0 || $drawing.length === 0) { + return; + } + + $topology.off(".topopanning"); + $(document).off(".topopanning"); + + let dragging = false; + let startX = 0; + let startY = 0; + let startLeft = 0; + let startTop = 0; + + function panBlocked(target) { + return $(target).closest(".window, ._jsPlumb_connector, ._jsPlumb_endpoint, ._jsPlumb_overlay").length > 0; + } + + $topology.on("mousedown.topopanning", function (e) { + if (e.button !== 0) { + return; + } + if (panBlocked(e.target)) { + return; + } + + dragging = true; + startX = e.clientX; + startY = e.clientY; + startLeft = topologyParsePx($drawing.css("left"), 0); + startTop = topologyParsePx($drawing.css("top"), 0); + $topology.addClass("topology-panning"); + e.preventDefault(); + }); + + $(document).on("mousemove.topopanning", function (e) { + if (!dragging) { + return; + } + const dx = e.clientX - startX; + const dy = e.clientY - startY; + $drawing.css("left", (startLeft + dx) + "px"); + $drawing.css("top", (startTop + dy) + "px"); + }); + + $(document).on("mouseup.topopanning", function () { + if (!dragging) { + return; + } + dragging = false; + $("#topology").removeClass("topology-panning"); + jsPlumb.repaintEverything(); + }); + })(); + + const val = getTopology(); + if (val.__zoom__ == null) { + setZoom($("#drawing")[0], zoom); + } else { + setZoom($("#drawing")[0], val.__zoom__); + } + }); +} - jsPlumb.Defaults.MaxConnections = 1000; +window.checkConnection = function () { + jsPlumb.ready(function () { + const items = []; + + jsPlumb.select({scope: "CONNECTOR"}).each(function (connection) { + const id = connection && connection.target ? connection.target.id : null; + if (id) { + items.push({kind: "CHECK_CONNECTOR", id: id, conn: connection}); + } + }); + jsPlumb.select({scope: "RESOURCE"}).each(function (connection) { + const id = connection && connection.target ? connection.target.id : null; + if (id) { + items.push({kind: "CHECK_RESOURCE", id: id, conn: connection}); + } + }); + + window.__topologyCheckRunId = (window.__topologyCheckRunId || 0) + 1; + const runId = window.__topologyCheckRunId; + + const batchSize = 25; + let idx = 0; + + function step() { + if (window.__topologyCheckRunId !== runId) { + return; + } + const end = Math.min(idx + batchSize, items.length); + + if (jsPlumb.setSuspendDrawing) { + jsPlumb.setSuspendDrawing(true); + } + try { + for (let i = idx; i < end; i++) { + const it = items[i]; + if (it.conn) { + it.conn.setPaintStyle(def.paintStyle); + } + jsPlumb.selectEndpoints({element: [it.id]}).setPaintStyle(def.endpointStyle); + + Wicket.WebSocket.send("{ \"kind\":\"" + it.kind + "\", \"target\":\"" + it.id + "\" }"); + } + } finally { + if (jsPlumb.setSuspendDrawing) { + jsPlumb.setSuspendDrawing(false, true); + } else { + jsPlumb.repaintEverything(); + } + } + + idx = end; + if (idx < items.length) { + window.setTimeout(step, 0); + } + } + + step(); + }); +} + +window.addEndpoint = function (source, target, scope) { + const sourceElement = $(document.getElementById(source)); - $("#drawing").draggable({ - containment: 'topology', - cursor: 'move' + const top = parseFloat(sourceElement.css("top")) + 10; + const left = parseFloat(sourceElement.css("left")) - 150; + + setPosition(target, left, top); + jsPlumb.ready(function () { + jsPlumb.draggable(jsPlumb.getSelector(document.getElementById(target))); + jsPlumb.connect({source: source, target: target, scope: scope}, def); }); +} - var val = getTopology(); - if (val.__zoom__ == null) { - setZoom($("#drawing")[0], zoom); - } else { - setZoom($("#drawing")[0], val.__zoom__); +function topologyParsePx(val, fallback) { + if (val == null) { + return fallback; + } + if (typeof val === 'number') { + return val; } - }); + const n = parseFloat(String(val).replace("px", "")); + return isNaN(n) ? fallback : n; } -window.checkConnection = function () { - jsPlumb.ready(function () { - jsPlumb.select({scope: "CONNECTOR"}).each(function (connection) { - Wicket.WebSocket.send("{ \"kind\":\"CHECK_CONNECTOR\", \"target\":\"" + connection.target.id + "\" }"); - }); - jsPlumb.select({scope: "RESOURCE"}).each(function (connection) { - Wicket.WebSocket.send("{ \"kind\":\"CHECK_RESOURCE\", \"target\":\"" + connection.target.id + "\" }"); +function topologyUniqPush(arr, value) { + for (let i = 0; i < arr.length; i++) { + if (arr[i] === value) { + return; + } + } + arr.push(value); +} + +function topologyBuildChildren(connections) { + const childrenById = {}; + const indegree = {}; + for (let i = 0; i < connections.length; i++) { + const c = connections[i]; + if (!c || !c.source || !c.target) { + continue; + } + const sourceId = c.source.id; + const targetId = c.target.id; + if (!sourceId || !targetId || sourceId === targetId) { + continue; + } + + if (!childrenById[sourceId]) { + childrenById[sourceId] = []; + } + topologyUniqPush(childrenById[sourceId], targetId); + + if (indegree[targetId] == null) { + indegree[targetId] = 1; + } else { + indegree[targetId] = indegree[targetId] + 1; + } + if (indegree[sourceId] == null) { + indegree[sourceId] = 0; + } + } + return {childrenById: childrenById, indegree: indegree}; +} + +function topologyPickRoot(allNodeIds, childrenById, indegree) { + const rootEl = $("#drawing .window.topology_root")[0]; + if (rootEl && rootEl.id) { + return rootEl.id; + } + + for (let i = 0; i < allNodeIds.length; i++) { + const id = allNodeIds[i]; + if ((indegree[id] == null || indegree[id] === 0) && childrenById[id] && childrenById[id].length > 0) { + return id; + } + } + + return allNodeIds.length > 0 ? allNodeIds[0] : null; +} + +function topologyComputeSubtreeWidth(nodeId, childrenById, nodeSizeById, hGap, visiting, memo) { + if (memo[nodeId] != null) { + return memo[nodeId]; + } + if (visiting[nodeId]) { + memo[nodeId] = nodeSizeById[nodeId] ? nodeSizeById[nodeId].w : 140; + return memo[nodeId]; + } + + visiting[nodeId] = true; + const children = childrenById[nodeId] || []; + const ownW = nodeSizeById[nodeId] ? nodeSizeById[nodeId].w : 140; + if (children.length === 0) { + memo[nodeId] = ownW; + visiting[nodeId] = false; + return memo[nodeId]; + } + + let total = 0; + for (let i = 0; i < children.length; i++) { + total += topologyComputeSubtreeWidth(children[i], childrenById, nodeSizeById, hGap, visiting, memo); + } + total += hGap * (children.length - 1); + memo[nodeId] = Math.max(ownW, total); + visiting[nodeId] = false; + return memo[nodeId]; +} + +function topologyAssignPositions(nodeId, childrenById, nodeSizeById, subtreeWById, levelY, xLeft, hGap, vGap, outPos, visited) { + if (visited[nodeId]) { + return; + } + visited[nodeId] = true; + + const nodeSize = nodeSizeById[nodeId] || {w: 140, h: 55}; + const subtreeW = subtreeWById[nodeId] != null ? subtreeWById[nodeId] : nodeSize.w; + const xCenter = xLeft + subtreeW / 2; + outPos[nodeId] = {x: xCenter - nodeSize.w / 2, y: levelY}; + + const children = childrenById[nodeId] || []; + if (children.length === 0) { + return; + } + + const nextY = levelY + nodeSize.h + vGap; + let childX = xLeft; + for (let i = 0; i < children.length; i++) { + const childId = children[i]; + const childSubtreeW = subtreeWById[childId] != null + ? subtreeWById[childId] + : (nodeSizeById[childId] ? nodeSizeById[childId].w : 140); + + topologyAssignPositions(childId, childrenById, nodeSizeById, subtreeWById, nextY, childX, hGap, vGap, outPos, visited); + childX += childSubtreeW + hGap; + } +} + +function topologyShowVeil() { + const veil = document.getElementById("veil"); + if (veil) { + veil.style.display = "block"; + } +} + +function topologyHideVeil() { + const veil = document.getElementById("veil"); + if (veil) { + veil.style.display = "none"; + } +} + +function topologyRunWithOptionalVeil(fn, options) { + options = options || {}; + const delayMs = options.delayMs != null ? options.delayMs : 120; + let shown = false; + + const t = window.setTimeout(function () { + topologyShowVeil(); + shown = true; + }, delayMs); + + window.setTimeout(function () { + try { + fn(); + } finally { + window.clearTimeout(t); + if (shown) { + topologyHideVeil(); + } + } + }, 0); +} + +window.autoLayoutTree = function (options) { + options = options || {}; + const hGap = options.hGap != null ? options.hGap : 70; + const vGap = options.vGap != null ? options.vGap : 80; + const padding = options.padding != null ? options.padding : 10; + const centerInView = options.centerInView != null ? options.centerInView : false; + + topologyRunWithOptionalVeil(function () { + jsPlumb.ready(function () { + try { + const nodeEls = $("#drawing .window").toArray(); + if (!nodeEls || nodeEls.length === 0) { + return; + } + + // Build node sizes (unscaled), and stable list of ids. + const nodeSizeById = {}; + const nodeIds = []; + for (let i = 0; i < nodeEls.length; i++) { + const el = nodeEls[i]; + if (!el || !el.id) { + continue; + } + nodeIds.push(el.id); + nodeSizeById[el.id] = {w: el.offsetWidth || 140, h: el.offsetHeight || 55}; + } + + const connections = jsPlumb.getAllConnections ? jsPlumb.getAllConnections() : []; + const graph = topologyBuildChildren(connections); + const rootId = topologyPickRoot(nodeIds, graph.childrenById, graph.indegree); + if (!rootId) { + return; + } + + // Compute subtree widths. + const subtreeWById = {}; + for (let j = 0; j < nodeIds.length; j++) { + topologyComputeSubtreeWidth( + nodeIds[j], + graph.childrenById, + nodeSizeById, + hGap, + {}, + subtreeWById); + } + + // Assign relative positions starting from root. + const relPos = {}; + topologyAssignPositions( + rootId, + graph.childrenById, + nodeSizeById, + subtreeWById, + 0, + 0, + hGap, + vGap, + relPos, + {}); + + // Translate either to the current viewport center or keep root on its current position (default). + let dx = 0; + let dy = 0; + if (centerInView) { + let minX = null, minY = null, maxX = null, maxY = null; + for (let bb = 0; bb < nodeIds.length; bb++) { + const bid = nodeIds[bb]; + const rp = relPos[bid]; + if (!rp) { + continue; + } + const sz = nodeSizeById[bid] || {w: 140, h: 55}; + minX = minX == null ? rp.x : Math.min(minX, rp.x); + minY = minY == null ? rp.y : Math.min(minY, rp.y); + maxX = maxX == null ? (rp.x + sz.w) : Math.max(maxX, rp.x + sz.w); + maxY = maxY == null ? (rp.y + sz.h) : Math.max(maxY, rp.y + sz.h); + } + + if (minX != null && minY != null && maxX != null && maxY != null) { + const layoutCx = (minX + maxX) / 2; + const layoutCy = (minY + maxY) / 2; + + const $topology = $("#topology"); + const $drawing = $("#drawing"); + const topoW = $topology.width() || 0; + const topoH = $topology.height() || 0; + const drawingLeft = topologyParsePx($drawing.css("left"), 0); + const drawingTop = topologyParsePx($drawing.css("top"), 0); + + const topoCookie = getTopology(); + const z = (jsPlumb.getZoom && jsPlumb.getZoom()) || topoCookie.__zoom__ || 1 || 1; + + const viewCx = (topoW / 2 - drawingLeft) / z; + const viewCy = (topoH / 2 - drawingTop) / z; + + dx = viewCx - layoutCx; + dy = viewCy - layoutCy; + } + } else { + const rootEl = document.getElementById(rootId); + let rootLeft = topologyParsePx(rootEl && rootEl.style ? rootEl.style.left : null, 0); + let rootTop = topologyParsePx(rootEl && rootEl.style ? rootEl.style.top : null, 0); + if (isNaN(rootLeft) || isNaN(rootTop) || (rootLeft === 0 && rootTop === 0)) { + rootLeft = topologyParsePx($(rootEl).css("left"), 0); + rootTop = topologyParsePx($(rootEl).css("top"), 0); + } + const rootRel = relPos[rootId]; + dx = rootLeft - (rootRel ? rootRel.x : 0); + dy = rootTop - (rootRel ? rootRel.y : 0); + } + + const topo = getTopology(); + let placedMinX = null; + let placedMaxX = null; + let placedMaxY = null; + + if (jsPlumb.setSuspendDrawing) { + jsPlumb.setSuspendDrawing(true); + } + + for (let k = 0; k < nodeIds.length; k++) { + const id = nodeIds[k]; + const p = relPos[id]; + if (!p) { + continue; + } + const x = Math.round(p.x + dx + padding); + const y = Math.round(p.y + dy + padding); + + const nEl = document.getElementById(id); + if (!nEl) { + continue; + } + nEl.style.left = x + "px"; + nEl.style.top = y + "px"; + + placedMinX = placedMinX == null ? x : Math.min(placedMinX, x); + placedMaxX = placedMaxX == null ? x : Math.max(placedMaxX, x + (nodeSizeById[id] ? nodeSizeById[id].w : 140)); + placedMaxY = placedMaxY == null ? y : Math.max(placedMaxY, y + (nodeSizeById[id] ? nodeSizeById[id].h : 55)); + + if (topo[id] == null) { + topo[id] = {top: y + "px", left: x + "px"}; + } else { + topo[id].top = y + "px"; + topo[id].left = x + "px"; + } + } + + const unplaced = []; + for (let u = 0; u < nodeIds.length; u++) { + if (!relPos[nodeIds[u]]) { + unplaced.push(nodeIds[u]); + } + } + if (unplaced.length > 0) { + let startX = (placedMaxX == null ? 0 : placedMaxX) + hGap * 2; + let startY = (placedMinX == null ? 0 : (placedMinX - 20)); + let colX = startX; + let rowY = startY; + let colW = 0; + const maxColH = Math.max(420, (placedMaxY == null ? 420 : (placedMaxY - startY))); + for (let ui = 0; ui < unplaced.length; ui++) { + const uid = unplaced[ui]; + const us = nodeSizeById[uid] || {w: 140, h: 55}; + if ((rowY - startY) + us.h > maxColH) { + colX = colX + colW + hGap; + rowY = startY; + colW = 0; + } + + const ux = Math.round(colX + padding); + const uy = Math.round(rowY + padding); + const uEl = document.getElementById(uid); + if (uEl) { + uEl.style.left = ux + "px"; + uEl.style.top = uy + "px"; + } + topo[uid] = {top: uy + "px", left: ux + "px"}; + + rowY += us.h + 12; + colW = Math.max(colW, us.w); + } + } + + $.cookie("topology", JSON.stringify(topo), {expires: 9999}); + + let treeMinX = null, treeMinY = null, treeMaxX = null, treeMaxY = null; + for (let tb = 0; tb < nodeIds.length; tb++) { + const tid = nodeIds[tb]; + const tp = relPos[tid]; + if (!tp) { + continue; + } + const ts = nodeSizeById[tid] || {w: 140, h: 55}; + const ax = tp.x + dx + padding; + const ay = tp.y + dy + padding; + treeMinX = treeMinX == null ? ax : Math.min(treeMinX, ax); + treeMinY = treeMinY == null ? ay : Math.min(treeMinY, ay); + treeMaxX = treeMaxX == null ? (ax + ts.w) : Math.max(treeMaxX, ax + ts.w); + treeMaxY = treeMaxY == null ? (ay + ts.h) : Math.max(treeMaxY, ay + ts.h); + } + window.__topologyTreeBBox = (treeMinX == null) ? null : { + minX: treeMinX, + minY: treeMinY, + maxX: treeMaxX, + maxY: treeMaxY + }; + + if (jsPlumb.setSuspendDrawing) { + jsPlumb.setSuspendDrawing(false, true); + } else { + jsPlumb.repaintEverything(); + } + + if (window.__topologyTreeBBox) { + recenterToTree(); + } + } catch (err) { + console.debug("autoLayoutTree failed", err); + } + }); }); - }); +}; + +function topologyGetZoom() { + const topoCookie = getTopology(); + const z = (jsPlumb.getZoom && jsPlumb.getZoom()) || topoCookie.__zoom__ || 1; + return z || 1; } -window.addEndpoint = function (source, target, scope) { - var sourceElement = $(document.getElementById(source)); +function topologyRecenterOnBBox(bbox) { + if (!bbox) { + return; + } + const $topology = $("#topology"); + const $drawing = $("#drawing"); + if ($topology.length === 0 || $drawing.length === 0) { + return; + } + + const topoW = $topology.width() || 0; + const topoH = $topology.height() || 0; + if (topoW <= 0 || topoH <= 0) { + return; + } + + const z = topologyGetZoom(); + const drawingW = $drawing.width() || 0; + const drawingH = $drawing.height() || 0; + const originX = drawingW * 0.5; + const originY = drawingH * 0.5; - var top = parseFloat(sourceElement.css("top")) + 10; - var left = parseFloat(sourceElement.css("left")) - 150; + const cx = (bbox.minX + bbox.maxX) / 2; + const cy = (bbox.minY + bbox.maxY) / 2; - setPosition(target, left, top); - jsPlumb.ready(function () { - jsPlumb.draggable(jsPlumb.getSelector(document.getElementById(target))); - jsPlumb.connect({source: source, target: target, scope: scope}, def); - }); + const viewportCenterX = topoW / 2; + const viewportCenterY = topoH / 2; + + const newLeft = viewportCenterX - originX - (cx - originX) * z; + const newTop = viewportCenterY - originY - (cy - originY) * z; + + $drawing.css("left", Math.round(newLeft) + "px"); + $drawing.css("top", Math.round(newTop) + "px"); + jsPlumb.repaintEverything(); } +window.recenterToTree = function () { + topologyRunWithOptionalVeil(function () { + jsPlumb.ready(function () { + let bbox = window.__topologyTreeBBox; + if (!bbox) { + const nodeEls = $("#drawing .window").toArray(); + let minX = null, minY = null, maxX = null, maxY = null; + for (let i = 0; i < nodeEls.length; i++) { + const el = nodeEls[i]; + if (!el) { + continue; + } + const x = topologyParsePx($(el).css("left"), 0); + const y = topologyParsePx($(el).css("top"), 0); + const w = el.offsetWidth || 140; + const h = el.offsetHeight || 55; + minX = minX == null ? x : Math.min(minX, x); + minY = minY == null ? y : Math.min(minY, y); + maxX = maxX == null ? (x + w) : Math.max(maxX, x + w); + maxY = maxY == null ? (y + h) : Math.max(maxY, y + h); + } + bbox = (minX == null) ? null : {minX: minX, minY: minY, maxX: maxX, maxY: maxY}; + } + + topologyRecenterOnBBox(bbox); + }); + }); +}; + jsPlumb.importDefaults({ - Connector: ["Straight"], - DragOptions: { - cursor: "pointer", - zIndex: 2000 - }, - HoverClass: "connector-hover" + Connector: ["Bezier", { curviness: 10 }], + DragOptions: { + cursor: "pointer", + zIndex: 2000 + }, + HoverClass: "connector-hover" }); -jQuery(function ($) { - Wicket.Event.subscribe("/websocket/message", function (jqEvent, message) { - var val = JSON.parse(decodeURIComponent(message)); - switch (val.status) { - case 'UNKNOWN': - unknown(val.target); - break; - case 'REACHABLE': - enable(val.target); - break; - case 'UNREACHABLE': - disable(val.target); - break; - case 'FAILURE': - failure(val.target); - break; - default: - break; +jQuery(function () { + const pendingStatusByTarget = {}; + let flushScheduled = false; + + function applyStatus(targetId, status) { + if (!targetId) { + return; + } + switch (status) { + case 'UNKNOWN': + unknown(targetId); + break; + case 'REACHABLE': + enable(targetId); + break; + case 'UNREACHABLE': + disable(targetId); + break; + case 'FAILURE': + failure(targetId); + break; + default: + break; + } } - }); + + function flushStatuses() { + flushScheduled = false; + const entries = Object.entries(pendingStatusByTarget); + if (entries.length === 0) { + return; + } + + for (const [k] of entries) { + delete pendingStatusByTarget[k]; + } + + jsPlumb.ready(function () { + if (jsPlumb.setSuspendDrawing) { + jsPlumb.setSuspendDrawing(true); + } + try { + for (let i = 0; i < entries.length; i++) { + const [targetId, status] = entries[i]; + applyStatus(targetId, status); + } + } finally { + if (jsPlumb.setSuspendDrawing) { + jsPlumb.setSuspendDrawing(false, true); + } else { + jsPlumb.repaintEverything(); + } + } + }); + } + + function scheduleFlush() { + if (flushScheduled) { + return; + } + flushScheduled = true; + window.requestAnimationFrame(flushStatuses); + } + + Wicket.Event.subscribe("/websocket/message", function (_jqEvent, message) { + const val = JSON.parse(decodeURIComponent(message)); + pendingStatusByTarget[val.target] = val.status; + scheduleFlush(); + }); }); diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails.properties deleted file mode 100644 index a93c17a1f9a..00000000000 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails.properties +++ /dev/null @@ -1,24 +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. -required_alert=All form fields are required. -edit_attribute=Edit attribute -attributes=Virtual attributes -title=Virtual Schema -readonly=Read-only -resource=Resource -anyType=Type -extAttrName=External attribute diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails_it.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails_it.properties deleted file mode 100644 index 24de32579cf..00000000000 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails_it.properties +++ /dev/null @@ -1,24 +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. -required_alert=Tutti i campi sono richiesti. -edit_attribute=Modifica attributo -attributes=Attributi virtuali -title=Schema Virtuale -readonly=Read-only -resource=Risorsa -anyType=Tipo -extAttrName=Attributo esterno diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails_ja.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails_ja.properties deleted file mode 100644 index 8475c1a7047..00000000000 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails_ja.properties +++ /dev/null @@ -1,24 +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. -required_alert=\u3059\u3079\u3066\u306e\u30d5\u30a9\u30fc\u30e0\u30d5\u30a3\u30fc\u30eb\u30c9\u304c\u5fc5\u9808\u3067\u3059\u3002 -edit_attribute=\u5c5e\u6027\u3092\u7de8\u96c6 -attributes=\u4eee\u60f3\u5c5e\u6027 -title=\u4eee\u60f3\u30b9\u30ad\u30fc\u30de -readonly=\u8aad\u307f\u53d6\u308a\u5c02\u7528 -resource=\u30ea\u30bd\u30fc\u30b9 -anyType=\u30bf\u30a4\u30d7 -extAttrName=\u5916\u90e8\u5c5e\u6027 diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails_pt_BR.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails_pt_BR.properties deleted file mode 100644 index c757d89a73c..00000000000 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/panels/VirSchemaDetails_pt_BR.properties +++ /dev/null @@ -1,24 +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. -required_alert=Todos os campos do formul\u00e1rio s\u00e3o obrigat\u00f3rios -edit_attribute=Editar Atributo -attributes=Atributos Virtuais -title=Esquema Virtual -readonly=Apenas Leitura -resource=Recurso -anyType=Type -extAttrName=Atributo Externo diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/Topology.html b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/Topology.html index 4210527ae10..17ccca00226 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/Topology.html +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/Topology.html @@ -21,8 +21,7 @@ - - +