mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-08 14:51:49 +00:00
Compare commits
31 Commits
25.3.0.52
...
release/25
Author | SHA1 | Date | |
---|---|---|---|
|
7bb4ab869a | ||
|
3a16441521 | ||
|
2a13c1b496 | ||
|
8d802f8e3b | ||
|
5c74de837a | ||
|
992c3d9e90 | ||
|
e06a69e867 | ||
|
fe06ef712a | ||
|
b88ef90029 | ||
|
fd57312452 | ||
|
a5a4ae7e97 | ||
|
fbce7372ac | ||
|
e1e694227a | ||
|
5429dbbb1b | ||
|
4eb32a7423 | ||
|
712a5f5013 | ||
|
cb825f0b8b | ||
|
c43eaaef7b | ||
|
8dc1ed62ab | ||
|
b4e52bd2ab | ||
|
2b38242eba | ||
|
c755cb5d6f | ||
|
a9ea867a73 | ||
|
4094dab7e1 | ||
|
1c5833c0e2 | ||
|
2e1f943390 | ||
|
71c6f21687 | ||
|
a48d7c2cdd | ||
|
e186f9737a | ||
|
97e71f77f9 | ||
|
38d61e9cf6 |
188
.github/workflows/ci.yml
vendored
188
.github/workflows/ci.yml
vendored
@@ -44,14 +44,10 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- id: changed-files
|
- uses: Alfresco/alfresco-build-tools/.github/actions/pre-commit@v8.16.0
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/github-list-changes@v8.13.0
|
|
||||||
with:
|
|
||||||
write-list-to-env: true
|
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/pre-commit@v8.13.0
|
|
||||||
- name: "Init"
|
- name: "Init"
|
||||||
run: bash ./scripts/ci/init.sh
|
run: bash ./scripts/ci/init.sh
|
||||||
- name: "Prepare maven cache and check compilation"
|
- name: "Prepare maven cache and check compilation"
|
||||||
@@ -69,12 +65,12 @@ jobs:
|
|||||||
!contains(github.event.head_commit.message, '[force')
|
!contains(github.event.head_commit.message, '[force')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Init"
|
- name: "Init"
|
||||||
run: bash ./scripts/ci/init.sh
|
run: bash ./scripts/ci/init.sh
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/veracode@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/veracode@v8.16.0
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
srcclr-api-token: ${{ secrets.SRCCLR_API_TOKEN }}
|
srcclr-api-token: ${{ secrets.SRCCLR_API_TOKEN }}
|
||||||
@@ -92,10 +88,10 @@ jobs:
|
|||||||
!contains(github.event.head_commit.message, '[force')
|
!contains(github.event.head_commit.message, '[force')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/github-download-file@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/github-download-file@v8.16.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||||
repository: "Alfresco/veracode-baseline-archive"
|
repository: "Alfresco/veracode-baseline-archive"
|
||||||
@@ -148,10 +144,10 @@ jobs:
|
|||||||
!contains(github.event.head_commit.message, '[skip tests]') &&
|
!contains(github.event.head_commit.message, '[skip tests]') &&
|
||||||
!contains(github.event.head_commit.message, '[force]')
|
!contains(github.event.head_commit.message, '[force]')
|
||||||
steps:
|
steps:
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- uses: Alfresco/ya-pmd-scan@v4.1.0
|
- uses: Alfresco/ya-pmd-scan@v4.3.0
|
||||||
with:
|
with:
|
||||||
classpath-build-command: "mvn test-compile -ntp -Pags -pl \"-:alfresco-community-repo-docker\""
|
classpath-build-command: "mvn test-compile -ntp -Pags -pl \"-:alfresco-community-repo-docker\""
|
||||||
|
|
||||||
@@ -181,14 +177,14 @@ jobs:
|
|||||||
testAttributes: "-Dtest=AllMmtUnitTestSuite"
|
testAttributes: "-Dtest=AllMmtUnitTestSuite"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Init"
|
- name: "Init"
|
||||||
run: bash ./scripts/ci/init.sh
|
run: bash ./scripts/ci/init.sh
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.testModule }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.testModule }}
|
||||||
@@ -219,7 +215,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: "Summarize Report Portal"
|
- name: "Summarize Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
|
||||||
id: rp-summarize
|
id: rp-summarize
|
||||||
with:
|
with:
|
||||||
tests-outcome: ${{ steps.run-tests.outcome }}
|
tests-outcome: ${{ steps.run-tests.outcome }}
|
||||||
@@ -261,9 +257,9 @@ jobs:
|
|||||||
REQUIRES_INSTALLED_ARTIFACTS: true
|
REQUIRES_INSTALLED_ARTIFACTS: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||||
run: |
|
run: |
|
||||||
@@ -276,7 +272,7 @@ jobs:
|
|||||||
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d
|
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.testSuite }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.testSuite }}
|
||||||
@@ -307,7 +303,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: "Summarize Report Portal"
|
- name: "Summarize Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
|
||||||
id: rp-summarize
|
id: rp-summarize
|
||||||
with:
|
with:
|
||||||
tests-outcome: ${{ steps.run-tests.outcome }}
|
tests-outcome: ${{ steps.run-tests.outcome }}
|
||||||
@@ -340,9 +336,9 @@ jobs:
|
|||||||
version: ['10.5', '10.6']
|
version: ['10.5', '10.6']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Init"
|
- name: "Init"
|
||||||
run: bash ./scripts/ci/init.sh
|
run: bash ./scripts/ci/init.sh
|
||||||
- name: Run MariaDB ${{ matrix.version }} database
|
- name: Run MariaDB ${{ matrix.version }} database
|
||||||
@@ -351,7 +347,7 @@ jobs:
|
|||||||
MARIADB_VERSION: ${{ matrix.version }}
|
MARIADB_VERSION: ${{ matrix.version }}
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.version }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.version }}
|
||||||
@@ -382,7 +378,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: "Summarize Report Portal"
|
- name: "Summarize Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
|
||||||
id: rp-summarize
|
id: rp-summarize
|
||||||
with:
|
with:
|
||||||
tests-outcome: ${{ steps.run-tests.outcome }}
|
tests-outcome: ${{ steps.run-tests.outcome }}
|
||||||
@@ -411,9 +407,9 @@ jobs:
|
|||||||
!contains(github.event.head_commit.message, '[force')
|
!contains(github.event.head_commit.message, '[force')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Init"
|
- name: "Init"
|
||||||
run: bash ./scripts/ci/init.sh
|
run: bash ./scripts/ci/init.sh
|
||||||
- name: "Run MariaDB 10.11 database"
|
- name: "Run MariaDB 10.11 database"
|
||||||
@@ -422,7 +418,7 @@ jobs:
|
|||||||
MARIADB_VERSION: 10.11
|
MARIADB_VERSION: 10.11
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
||||||
@@ -453,7 +449,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: "Summarize Report Portal"
|
- name: "Summarize Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
|
||||||
id: rp-summarize
|
id: rp-summarize
|
||||||
with:
|
with:
|
||||||
tests-outcome: ${{ steps.run-tests.outcome }}
|
tests-outcome: ${{ steps.run-tests.outcome }}
|
||||||
@@ -482,9 +478,9 @@ jobs:
|
|||||||
!contains(github.event.head_commit.message, '[force')
|
!contains(github.event.head_commit.message, '[force')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Init"
|
- name: "Init"
|
||||||
run: bash ./scripts/ci/init.sh
|
run: bash ./scripts/ci/init.sh
|
||||||
- name: "Run MySQL 8 database"
|
- name: "Run MySQL 8 database"
|
||||||
@@ -493,7 +489,7 @@ jobs:
|
|||||||
MYSQL_VERSION: 8
|
MYSQL_VERSION: 8
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
||||||
@@ -524,7 +520,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: "Summarize Report Portal"
|
- name: "Summarize Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
|
||||||
id: rp-summarize
|
id: rp-summarize
|
||||||
with:
|
with:
|
||||||
tests-outcome: ${{ steps.run-tests.outcome }}
|
tests-outcome: ${{ steps.run-tests.outcome }}
|
||||||
@@ -552,9 +548,9 @@ jobs:
|
|||||||
!contains(github.event.head_commit.message, '[force')
|
!contains(github.event.head_commit.message, '[force')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Init"
|
- name: "Init"
|
||||||
run: bash ./scripts/ci/init.sh
|
run: bash ./scripts/ci/init.sh
|
||||||
- name: "Run PostgreSQL 14.15 database"
|
- name: "Run PostgreSQL 14.15 database"
|
||||||
@@ -563,7 +559,7 @@ jobs:
|
|||||||
POSTGRES_VERSION: 14.15
|
POSTGRES_VERSION: 14.15
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
||||||
@@ -594,7 +590,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: "Summarize Report Portal"
|
- name: "Summarize Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
|
||||||
id: rp-summarize
|
id: rp-summarize
|
||||||
with:
|
with:
|
||||||
tests-outcome: ${{ steps.run-tests.outcome }}
|
tests-outcome: ${{ steps.run-tests.outcome }}
|
||||||
@@ -622,9 +618,9 @@ jobs:
|
|||||||
!contains(github.event.head_commit.message, '[force')
|
!contains(github.event.head_commit.message, '[force')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Init"
|
- name: "Init"
|
||||||
run: bash ./scripts/ci/init.sh
|
run: bash ./scripts/ci/init.sh
|
||||||
- name: "Run PostgreSQL 15.10 database"
|
- name: "Run PostgreSQL 15.10 database"
|
||||||
@@ -633,7 +629,7 @@ jobs:
|
|||||||
POSTGRES_VERSION: 15.10
|
POSTGRES_VERSION: 15.10
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
||||||
@@ -664,7 +660,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: "Summarize Report Portal"
|
- name: "Summarize Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
|
||||||
id: rp-summarize
|
id: rp-summarize
|
||||||
with:
|
with:
|
||||||
tests-outcome: ${{ steps.run-tests.outcome }}
|
tests-outcome: ${{ steps.run-tests.outcome }}
|
||||||
@@ -692,9 +688,9 @@ jobs:
|
|||||||
!contains(github.event.head_commit.message, '[force')
|
!contains(github.event.head_commit.message, '[force')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Init"
|
- name: "Init"
|
||||||
run: bash ./scripts/ci/init.sh
|
run: bash ./scripts/ci/init.sh
|
||||||
- name: "Run PostgreSQL 16.6 database"
|
- name: "Run PostgreSQL 16.6 database"
|
||||||
@@ -703,7 +699,7 @@ jobs:
|
|||||||
POSTGRES_VERSION: 16.6
|
POSTGRES_VERSION: 16.6
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
||||||
@@ -734,7 +730,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: "Summarize Report Portal"
|
- name: "Summarize Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
|
||||||
id: rp-summarize
|
id: rp-summarize
|
||||||
with:
|
with:
|
||||||
tests-outcome: ${{ steps.run-tests.outcome }}
|
tests-outcome: ${{ steps.run-tests.outcome }}
|
||||||
@@ -760,16 +756,16 @@ jobs:
|
|||||||
!contains(github.event.head_commit.message, '[force')
|
!contains(github.event.head_commit.message, '[force')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Init"
|
- name: "Init"
|
||||||
run: bash ./scripts/ci/init.sh
|
run: bash ./scripts/ci/init.sh
|
||||||
- name: "Run ActiveMQ"
|
- name: "Run ActiveMQ"
|
||||||
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile activemq up -d
|
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile activemq up -d
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
||||||
@@ -800,7 +796,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: "Summarize Report Portal"
|
- name: "Summarize Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
|
||||||
id: rp-summarize
|
id: rp-summarize
|
||||||
with:
|
with:
|
||||||
tests-outcome: ${{ steps.run-tests.outcome }}
|
tests-outcome: ${{ steps.run-tests.outcome }}
|
||||||
@@ -860,9 +856,9 @@ jobs:
|
|||||||
mvn-options: '-Dencryption.ssl.keystore.location=${CI_WORKSPACE}/keystores/alfresco/alfresco.keystore -Dencryption.ssl.truststore.location=${CI_WORKSPACE}/keystores/alfresco/alfresco.truststore'
|
mvn-options: '-Dencryption.ssl.keystore.location=${CI_WORKSPACE}/keystores/alfresco/alfresco.keystore -Dencryption.ssl.truststore.location=${CI_WORKSPACE}/keystores/alfresco/alfresco.truststore'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Init"
|
- name: "Init"
|
||||||
run: bash ./scripts/ci/init.sh
|
run: bash ./scripts/ci/init.sh
|
||||||
- name: "Set transformers tag"
|
- name: "Set transformers tag"
|
||||||
@@ -885,7 +881,7 @@ jobs:
|
|||||||
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d
|
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.testSuite }} ${{ matrix.idp }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.testSuite }} ${{ matrix.idp }}
|
||||||
@@ -916,7 +912,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: "Summarize Report Portal"
|
- name: "Summarize Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
|
||||||
id: rp-summarize
|
id: rp-summarize
|
||||||
with:
|
with:
|
||||||
tests-outcome: ${{ steps.run-tests.outcome }}
|
tests-outcome: ${{ steps.run-tests.outcome }}
|
||||||
@@ -974,9 +970,9 @@ jobs:
|
|||||||
REQUIRES_LOCAL_IMAGES: true
|
REQUIRES_LOCAL_IMAGES: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||||
run: |
|
run: |
|
||||||
@@ -992,7 +988,7 @@ jobs:
|
|||||||
run: mvn install -pl :alfresco-community-repo-integration-test -am -DskipTests -Pall-tas-tests
|
run: mvn install -pl :alfresco-community-repo-integration-test -am -DskipTests -Pall-tas-tests
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.test-name }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.test-name }}
|
||||||
@@ -1030,7 +1026,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: "Summarize Report Portal"
|
- name: "Summarize Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
|
||||||
id: rp-summarize
|
id: rp-summarize
|
||||||
with:
|
with:
|
||||||
tests-outcome: ${{ steps.tests.outcome }}
|
tests-outcome: ${{ steps.tests.outcome }}
|
||||||
@@ -1056,16 +1052,16 @@ jobs:
|
|||||||
!contains(github.event.head_commit.message, '[force')
|
!contains(github.event.head_commit.message, '[force')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Init"
|
- name: "Init"
|
||||||
run: bash ./scripts/ci/init.sh
|
run: bash ./scripts/ci/init.sh
|
||||||
- name: "Run Postgres 16.6 database"
|
- name: "Run Postgres 16.6 database"
|
||||||
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile postgres up -d
|
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile postgres up -d
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
||||||
@@ -1096,7 +1092,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: "Summarize Report Portal"
|
- name: "Summarize Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
|
||||||
id: rp-summarize
|
id: rp-summarize
|
||||||
with:
|
with:
|
||||||
tests-outcome: ${{ steps.run-tests.outcome }}
|
tests-outcome: ${{ steps.run-tests.outcome }}
|
||||||
@@ -1130,9 +1126,9 @@ jobs:
|
|||||||
REQUIRES_INSTALLED_ARTIFACTS: true
|
REQUIRES_INSTALLED_ARTIFACTS: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||||
run: |
|
run: |
|
||||||
@@ -1140,7 +1136,7 @@ jobs:
|
|||||||
bash ./scripts/ci/build.sh
|
bash ./scripts/ci/build.sh
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} 0${{ matrix.part }} - (PostgreSQL) ${{ matrix.test-name }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} 0${{ matrix.part }} - (PostgreSQL) ${{ matrix.test-name }}
|
||||||
@@ -1176,9 +1172,9 @@ jobs:
|
|||||||
REQUIRES_INSTALLED_ARTIFACTS: true
|
REQUIRES_INSTALLED_ARTIFACTS: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||||
run: |
|
run: |
|
||||||
@@ -1186,7 +1182,7 @@ jobs:
|
|||||||
bash ./scripts/ci/build.sh
|
bash ./scripts/ci/build.sh
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} 0${{ matrix.part }} - (MySQL) ${{ matrix.test-name }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} 0${{ matrix.part }} - (MySQL) ${{ matrix.test-name }}
|
||||||
@@ -1218,9 +1214,9 @@ jobs:
|
|||||||
REQUIRES_LOCAL_IMAGES: true
|
REQUIRES_LOCAL_IMAGES: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||||
run: |
|
run: |
|
||||||
@@ -1234,7 +1230,7 @@ jobs:
|
|||||||
mvn -B install -pl :alfresco-governance-services-automation-community-rest-api -am -Pags -Pall-tas-tests -DskipTests
|
mvn -B install -pl :alfresco-governance-services-automation-community-rest-api -am -Pags -Pall-tas-tests -DskipTests
|
||||||
- name: "Prepare Report Portal"
|
- name: "Prepare Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
|
||||||
id: rp-prepare
|
id: rp-prepare
|
||||||
with:
|
with:
|
||||||
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
|
||||||
@@ -1266,7 +1262,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: "Summarize Report Portal"
|
- name: "Summarize Report Portal"
|
||||||
if: github.ref_name == 'master'
|
if: github.ref_name == 'master'
|
||||||
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.13.0
|
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
|
||||||
id: rp-summarize
|
id: rp-summarize
|
||||||
with:
|
with:
|
||||||
tests-outcome: ${{ steps.run-tests.outcome }}
|
tests-outcome: ${{ steps.run-tests.outcome }}
|
||||||
@@ -1308,9 +1304,9 @@ jobs:
|
|||||||
!contains(github.event.head_commit.message, '[force]')
|
!contains(github.event.head_commit.message, '[force]')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.13.0
|
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||||
run: |
|
run: |
|
||||||
|
@@ -133,21 +133,21 @@
|
|||||||
"filename": ".github/workflows/ci.yml",
|
"filename": ".github/workflows/ci.yml",
|
||||||
"hashed_secret": "b86dc2f033a63f2b7b9e7d270ab806d2910d7572",
|
"hashed_secret": "b86dc2f033a63f2b7b9e7d270ab806d2910d7572",
|
||||||
"is_verified": false,
|
"is_verified": false,
|
||||||
"line_number": 299
|
"line_number": 295
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Secret Keyword",
|
"type": "Secret Keyword",
|
||||||
"filename": ".github/workflows/ci.yml",
|
"filename": ".github/workflows/ci.yml",
|
||||||
"hashed_secret": "1bfb0e20f886150ba59b853bcd49dea893e00966",
|
"hashed_secret": "1bfb0e20f886150ba59b853bcd49dea893e00966",
|
||||||
"is_verified": false,
|
"is_verified": false,
|
||||||
"line_number": 374
|
"line_number": 370
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Secret Keyword",
|
"type": "Secret Keyword",
|
||||||
"filename": ".github/workflows/ci.yml",
|
"filename": ".github/workflows/ci.yml",
|
||||||
"hashed_secret": "128f14373ccfaff49e3664045d3a11b50cbb7b39",
|
"hashed_secret": "128f14373ccfaff49e3664045d3a11b50cbb7b39",
|
||||||
"is_verified": false,
|
"is_verified": false,
|
||||||
"line_number": 908
|
"line_number": 904
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
".github/workflows/master_release.yml": [
|
".github/workflows/master_release.yml": [
|
||||||
@@ -1607,7 +1607,7 @@
|
|||||||
"filename": "repository/src/test/java/org/alfresco/repo/security/authentication/identityservice/SpringBasedIdentityServiceFacadeUnitTest.java",
|
"filename": "repository/src/test/java/org/alfresco/repo/security/authentication/identityservice/SpringBasedIdentityServiceFacadeUnitTest.java",
|
||||||
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
|
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
|
||||||
"is_verified": false,
|
"is_verified": false,
|
||||||
"line_number": 46,
|
"line_number": 48,
|
||||||
"is_secret": false
|
"is_secret": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -1868,5 +1868,5 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"generated_at": "2025-02-26T15:13:52Z"
|
"generated_at": "2025-05-13T13:17:41Z"
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo-amps</artifactId>
|
<artifactId>alfresco-community-repo-amps</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-governance-services-community-parent</artifactId>
|
<artifactId>alfresco-governance-services-community-parent</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-governance-services-automation-community-repo</artifactId>
|
<artifactId>alfresco-governance-services-automation-community-repo</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@@ -131,6 +131,16 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
|
|||||||
.until(() -> getRestAPIFactory().getSearchAPI(null).search(searchRequest).getPagination()
|
.until(() -> getRestAPIFactory().getSearchAPI(null).search(searchRequest).getPagination()
|
||||||
.getTotalItems() == NUMBER_OF_FILES);
|
.getTotalItems() == NUMBER_OF_FILES);
|
||||||
|
|
||||||
|
RestRequestQueryModel ancestorReq = getContentFromFolderAndAllSubfoldersQuery(rootFolder.getNodeRefWithoutVersion());
|
||||||
|
SearchRequest ancestorSearchRequest = new SearchRequest();
|
||||||
|
ancestorSearchRequest.setQuery(ancestorReq);
|
||||||
|
|
||||||
|
STEP("Wait until paths are indexed.");
|
||||||
|
// to improve stability on CI - seems that sometimes during big load we need to wait longer for the condition
|
||||||
|
await().atMost(120, TimeUnit.SECONDS)
|
||||||
|
.until(() -> getRestAPIFactory().getSearchAPI(null).search(ancestorSearchRequest).getPagination()
|
||||||
|
.getTotalItems() == NUMBER_OF_FILES);
|
||||||
|
|
||||||
holdBulkOperation = HoldBulkOperation.builder()
|
holdBulkOperation = HoldBulkOperation.builder()
|
||||||
.query(queryReq)
|
.query(queryReq)
|
||||||
.op(HoldBulkOperationType.ADD).build();
|
.op(HoldBulkOperationType.ADD).build();
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-governance-services-community-parent</artifactId>
|
<artifactId>alfresco-governance-services-community-parent</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
|
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
# Version label
|
# Version label
|
||||||
version.major=25
|
version.major=25
|
||||||
version.minor=1
|
version.minor=1
|
||||||
version.revision=0
|
version.revision=1
|
||||||
version.label=
|
version.label=
|
||||||
|
|
||||||
# Edition label
|
# Edition label
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
|
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo</artifactId>
|
<artifactId>alfresco-community-repo</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo-amps</artifactId>
|
<artifactId>alfresco-community-repo-amps</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo</artifactId>
|
<artifactId>alfresco-community-repo</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo</artifactId>
|
<artifactId>alfresco-community-repo</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo</artifactId>
|
<artifactId>alfresco-community-repo</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@@ -9,6 +9,6 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
</project>
|
</project>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo</artifactId>
|
<artifactId>alfresco-community-repo</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<organization>
|
<organization>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<developers>
|
<developers>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<developers>
|
<developers>
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<developers>
|
<developers>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
8
pom.xml
8
pom.xml
@@ -2,7 +2,7 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>alfresco-community-repo</artifactId>
|
<artifactId>alfresco-community-repo</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>Alfresco Community Repo Parent</name>
|
<name>Alfresco Community Repo Parent</name>
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<acs.version.major>25</acs.version.major>
|
<acs.version.major>25</acs.version.major>
|
||||||
<acs.version.minor>1</acs.version.minor>
|
<acs.version.minor>1</acs.version.minor>
|
||||||
<acs.version.revision>0</acs.version.revision>
|
<acs.version.revision>2</acs.version.revision>
|
||||||
<acs.version.label />
|
<acs.version.label />
|
||||||
<amp.min.version>${acs.version.major}.0.0</amp.min.version>
|
<amp.min.version>${acs.version.major}.0.0</amp.min.version>
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@
|
|||||||
<connection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</connection>
|
<connection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</connection>
|
||||||
<developerConnection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</developerConnection>
|
<developerConnection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</developerConnection>
|
||||||
<url>https://github.com/Alfresco/alfresco-community-repo</url>
|
<url>https://github.com/Alfresco/alfresco-community-repo</url>
|
||||||
<tag>25.1.0.71</tag>
|
<tag>HEAD</tag>
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
<distributionManagement>
|
<distributionManagement>
|
||||||
@@ -410,7 +410,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-beanutils</groupId>
|
<groupId>commons-beanutils</groupId>
|
||||||
<artifactId>commons-beanutils</artifactId>
|
<artifactId>commons-beanutils</artifactId>
|
||||||
<version>1.9.4</version>
|
<version>1.11.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-codec</groupId>
|
<groupId>commons-codec</groupId>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo</artifactId>
|
<artifactId>alfresco-community-repo</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.alfresco</groupId>
|
<groupId>org.alfresco</groupId>
|
||||||
<artifactId>alfresco-community-repo</artifactId>
|
<artifactId>alfresco-community-repo</artifactId>
|
||||||
<version>25.1.0.71</version>
|
<version>25.1.2.6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
* #%L
|
* #%L
|
||||||
* Alfresco Repository
|
* Alfresco Repository
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
* %%
|
* %%
|
||||||
* This file is part of the Alfresco software.
|
* This file is part of the Alfresco software.
|
||||||
* If the software was purchased under a paid Alfresco license, the terms of
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
@@ -31,6 +31,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.alfresco.repo.action.ParameterDefinitionImpl;
|
import org.alfresco.repo.action.ParameterDefinitionImpl;
|
||||||
|
import org.alfresco.repo.action.access.ActionAccessRestriction;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
import org.alfresco.service.cmr.action.Action;
|
import org.alfresco.service.cmr.action.Action;
|
||||||
import org.alfresco.service.cmr.action.ParameterDefinition;
|
import org.alfresco.service.cmr.action.ParameterDefinition;
|
||||||
@@ -65,7 +66,8 @@ public class AddFeaturesActionExecuter extends ActionExecuterAbstractBase
|
|||||||
/**
|
/**
|
||||||
* Set the node service
|
* Set the node service
|
||||||
*
|
*
|
||||||
* @param nodeService the node service
|
* @param nodeService
|
||||||
|
* the node service
|
||||||
*/
|
*/
|
||||||
public void setNodeService(NodeService nodeService)
|
public void setNodeService(NodeService nodeService)
|
||||||
{
|
{
|
||||||
@@ -75,7 +77,8 @@ public class AddFeaturesActionExecuter extends ActionExecuterAbstractBase
|
|||||||
/**
|
/**
|
||||||
* Set the transaction service
|
* Set the transaction service
|
||||||
*
|
*
|
||||||
* @param transactionService the transaction service
|
* @param transactionService
|
||||||
|
* the transaction service
|
||||||
*/
|
*/
|
||||||
public void setTransactionService(TransactionService transactionService)
|
public void setTransactionService(TransactionService transactionService)
|
||||||
{
|
{
|
||||||
@@ -98,43 +101,42 @@ public class AddFeaturesActionExecuter extends ActionExecuterAbstractBase
|
|||||||
{
|
{
|
||||||
if (this.nodeService.exists(actionedUponNodeRef))
|
if (this.nodeService.exists(actionedUponNodeRef))
|
||||||
{
|
{
|
||||||
transactionService.getRetryingTransactionHelper().doInTransaction(
|
transactionService.getRetryingTransactionHelper().doInTransaction(
|
||||||
new RetryingTransactionCallback<Void>()
|
new RetryingTransactionCallback<Void>() {
|
||||||
{
|
public Void execute() throws Throwable
|
||||||
public Void execute() throws Throwable
|
{
|
||||||
{
|
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
|
||||||
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
|
QName aspectQName = null;
|
||||||
QName aspectQName = null;
|
|
||||||
|
|
||||||
if(! nodeService.exists(actionedUponNodeRef))
|
if (!nodeService.exists(actionedUponNodeRef))
|
||||||
{
|
{
|
||||||
// Node has gone away, skip
|
// Node has gone away, skip
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the aspect details
|
// Build the aspect details
|
||||||
Map<String, Serializable> paramValues = ruleAction.getParameterValues();
|
Map<String, Serializable> paramValues = ruleAction.getParameterValues();
|
||||||
for (Map.Entry<String, Serializable> entry : paramValues.entrySet())
|
removeActionContextParameter(paramValues);
|
||||||
{
|
for (Map.Entry<String, Serializable> entry : paramValues.entrySet())
|
||||||
if (entry.getKey().equals(PARAM_ASPECT_NAME) == true)
|
{
|
||||||
{
|
if (entry.getKey().equals(PARAM_ASPECT_NAME) == true)
|
||||||
aspectQName = (QName)entry.getValue();
|
{
|
||||||
}
|
aspectQName = (QName) entry.getValue();
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
// Must be an adhoc property
|
{
|
||||||
QName propertyQName = QName.createQName(entry.getKey());
|
// Must be an adhoc property
|
||||||
Serializable propertyValue = entry.getValue();
|
QName propertyQName = QName.createQName(entry.getKey());
|
||||||
properties.put(propertyQName, propertyValue);
|
Serializable propertyValue = entry.getValue();
|
||||||
}
|
properties.put(propertyQName, propertyValue);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add the aspect
|
// Add the aspect
|
||||||
nodeService.addAspect(actionedUponNodeRef, aspectQName, properties);
|
nodeService.addAspect(actionedUponNodeRef, aspectQName, properties);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,4 +149,11 @@ public class AddFeaturesActionExecuter extends ActionExecuterAbstractBase
|
|||||||
paramList.add(new ParameterDefinitionImpl(PARAM_ASPECT_NAME, DataTypeDefinition.QNAME, true, getParamDisplayLabel(PARAM_ASPECT_NAME), false, "ac-aspects"));
|
paramList.add(new ParameterDefinitionImpl(PARAM_ASPECT_NAME, DataTypeDefinition.QNAME, true, getParamDisplayLabel(PARAM_ASPECT_NAME), false, "ac-aspects"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove actionContext from the parameter values to declassify as an adhoc property
|
||||||
|
*/
|
||||||
|
private void removeActionContextParameter(Map<String, Serializable> paramValues)
|
||||||
|
{
|
||||||
|
paramValues.remove(ActionAccessRestriction.ACTION_CONTEXT_PARAM_NAME);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -542,10 +542,7 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
|
|||||||
@Override
|
@Override
|
||||||
protected void onShutdown(ApplicationEvent applicationEvent)
|
protected void onShutdown(ApplicationEvent applicationEvent)
|
||||||
{
|
{
|
||||||
if (eventSender != null)
|
// NOOP
|
||||||
{
|
|
||||||
eventSender.destroy();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class EventTransactionListener extends TransactionListenerAdapter
|
protected class EventTransactionListener extends TransactionListenerAdapter
|
||||||
|
@@ -52,7 +52,7 @@ public interface EventSender
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It's called when the application context is closing, allowing {@link org.alfresco.repo.event2.EventGenerator} to perform cleanup operations.
|
* It's called when the bean instance is destroyed, allowing to perform cleanup operations.
|
||||||
*/
|
*/
|
||||||
default void destroy()
|
default void destroy()
|
||||||
{
|
{
|
||||||
|
@@ -25,15 +25,16 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.event2;
|
package org.alfresco.repo.event2;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
import jakarta.annotation.Nonnull;
|
import jakarta.annotation.Nonnull;
|
||||||
import org.alfresco.util.PropertyCheck;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||||
import org.springframework.core.env.PropertyResolver;
|
import org.springframework.core.env.PropertyResolver;
|
||||||
|
|
||||||
import java.util.Optional;
|
import org.alfresco.util.PropertyCheck;
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
public class EventSenderFactoryBean extends AbstractFactoryBean<EventSender>
|
public class EventSenderFactoryBean extends AbstractFactoryBean<EventSender>
|
||||||
{
|
{
|
||||||
@@ -51,7 +52,7 @@ public class EventSenderFactoryBean extends AbstractFactoryBean<EventSender>
|
|||||||
private boolean legacySkipQueueConfig;
|
private boolean legacySkipQueueConfig;
|
||||||
|
|
||||||
public EventSenderFactoryBean(@Autowired PropertyResolver propertyResolver, Event2MessageProducer event2MessageProducer,
|
public EventSenderFactoryBean(@Autowired PropertyResolver propertyResolver, Event2MessageProducer event2MessageProducer,
|
||||||
Executor enqueueThreadPoolExecutor, Executor dequeueThreadPoolExecutor)
|
Executor enqueueThreadPoolExecutor, Executor dequeueThreadPoolExecutor)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
PropertyCheck.mandatory(this, "propertyResolver", propertyResolver);
|
PropertyCheck.mandatory(this, "propertyResolver", propertyResolver);
|
||||||
@@ -155,4 +156,13 @@ public class EventSenderFactoryBean extends AbstractFactoryBean<EventSender>
|
|||||||
{
|
{
|
||||||
return event2MessageProducer;
|
return event2MessageProducer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void destroyInstance(EventSender eventSender)
|
||||||
|
{
|
||||||
|
if (eventSender != null)
|
||||||
|
{
|
||||||
|
eventSender.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -37,6 +37,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
|
|||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.alfresco.transform.config.CoreFunction;
|
import org.alfresco.transform.config.CoreFunction;
|
||||||
import org.alfresco.util.PropertyCheck;
|
import org.alfresco.util.PropertyCheck;
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
@@ -46,6 +47,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
|
||||||
import static org.alfresco.model.ContentModel.PROP_CONTENT;
|
import static org.alfresco.model.ContentModel.PROP_CONTENT;
|
||||||
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL;
|
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL;
|
||||||
@@ -68,6 +70,7 @@ public class LocalTransformClient implements TransformClient, InitializingBean
|
|||||||
private ContentService contentService;
|
private ContentService contentService;
|
||||||
private RenditionService2Impl renditionService2;
|
private RenditionService2Impl renditionService2;
|
||||||
private boolean directAccessUrlEnabled;
|
private boolean directAccessUrlEnabled;
|
||||||
|
private int threadPoolSize;
|
||||||
|
|
||||||
private ExecutorService executorService;
|
private ExecutorService executorService;
|
||||||
private ThreadLocal<LocalTransform> transform = new ThreadLocal<>();
|
private ThreadLocal<LocalTransform> transform = new ThreadLocal<>();
|
||||||
@@ -97,6 +100,11 @@ public class LocalTransformClient implements TransformClient, InitializingBean
|
|||||||
this.directAccessUrlEnabled = directAccessUrlEnabled;
|
this.directAccessUrlEnabled = directAccessUrlEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setThreadPoolSize(int threadPoolSize)
|
||||||
|
{
|
||||||
|
this.threadPoolSize = threadPoolSize;
|
||||||
|
}
|
||||||
|
|
||||||
public void setExecutorService(ExecutorService executorService)
|
public void setExecutorService(ExecutorService executorService)
|
||||||
{
|
{
|
||||||
this.executorService = executorService;
|
this.executorService = executorService;
|
||||||
@@ -110,9 +118,11 @@ public class LocalTransformClient implements TransformClient, InitializingBean
|
|||||||
PropertyCheck.mandatory(this, "contentService", contentService);
|
PropertyCheck.mandatory(this, "contentService", contentService);
|
||||||
PropertyCheck.mandatory(this, "renditionService2", renditionService2);
|
PropertyCheck.mandatory(this, "renditionService2", renditionService2);
|
||||||
PropertyCheck.mandatory(this, "directAccessUrlEnabled", directAccessUrlEnabled);
|
PropertyCheck.mandatory(this, "directAccessUrlEnabled", directAccessUrlEnabled);
|
||||||
|
PropertyCheck.mandatory(this, "threadPoolSize", threadPoolSize);
|
||||||
if (executorService == null)
|
if (executorService == null)
|
||||||
{
|
{
|
||||||
executorService = Executors.newCachedThreadPool();
|
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("local-transform-%d").build();
|
||||||
|
executorService = Executors.newFixedThreadPool(threadPoolSize, threadFactory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
* #%L
|
* #%L
|
||||||
* Alfresco Repository
|
* Alfresco Repository
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2005 - 2022 Alfresco Software Limited
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
* %%
|
* %%
|
||||||
* This file is part of the Alfresco software.
|
* This file is part of the Alfresco software.
|
||||||
* If the software was purchased under a paid Alfresco license, the terms of
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
@@ -25,6 +25,24 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.rendition2;
|
package org.alfresco.repo.rendition2;
|
||||||
|
|
||||||
|
import static org.alfresco.model.ContentModel.PROP_CONTENT;
|
||||||
|
import static org.alfresco.model.RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE;
|
||||||
|
import static org.alfresco.service.namespace.QName.createQName;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.model.RenditionModel;
|
import org.alfresco.model.RenditionModel;
|
||||||
import org.alfresco.repo.content.ContentServicePolicies;
|
import org.alfresco.repo.content.ContentServicePolicies;
|
||||||
@@ -51,23 +69,6 @@ import org.alfresco.service.namespace.QName;
|
|||||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
import org.alfresco.service.namespace.RegexQNamePattern;
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.alfresco.util.PropertyCheck;
|
import org.alfresco.util.PropertyCheck;
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import static org.alfresco.model.ContentModel.PROP_CONTENT;
|
|
||||||
import static org.alfresco.model.RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE;
|
|
||||||
import static org.alfresco.service.namespace.QName.createQName;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Async Rendition service. Replaces the original deprecated RenditionService.
|
* The Async Rendition service. Replaces the original deprecated RenditionService.
|
||||||
@@ -80,11 +81,19 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
|
|
||||||
public static final QName DEFAULT_RENDITION_CONTENT_PROP = ContentModel.PROP_CONTENT;
|
public static final QName DEFAULT_RENDITION_CONTENT_PROP = ContentModel.PROP_CONTENT;
|
||||||
public static final String DEFAULT_MIMETYPE = MimetypeMap.MIMETYPE_TEXT_PLAIN;
|
public static final String DEFAULT_MIMETYPE = MimetypeMap.MIMETYPE_TEXT_PLAIN;
|
||||||
|
public static final String MIMETYPE_METADATA_EXTRACT = "alfresco-metadata-extract";
|
||||||
|
public static final String MIMETYPE_METADATA_EMBED = "alfresco-metadata-embed";
|
||||||
public static final String DEFAULT_ENCODING = "UTF-8";
|
public static final String DEFAULT_ENCODING = "UTF-8";
|
||||||
|
|
||||||
public static final int SOURCE_HAS_NO_CONTENT = -1;
|
public static final int SOURCE_HAS_NO_CONTENT = -1;
|
||||||
public static final int RENDITION2_DOES_NOT_EXIST = -2;
|
public static final int RENDITION2_DOES_NOT_EXIST = -2;
|
||||||
|
|
||||||
|
// Allowed mimetypes to support text or metadata extract transforms when thumbnails are disabled.
|
||||||
|
private static final Set<String> ALLOWED_MIMETYPES = Set.of(
|
||||||
|
MimetypeMap.MIMETYPE_TEXT_PLAIN,
|
||||||
|
MIMETYPE_METADATA_EXTRACT,
|
||||||
|
MIMETYPE_METADATA_EMBED);
|
||||||
|
|
||||||
private static Log logger = LogFactory.getLog(RenditionService2Impl.class);
|
private static Log logger = LogFactory.getLog(RenditionService2Impl.class);
|
||||||
|
|
||||||
// As Async transforms and renditions are so similar, this class provides a way to provide the code that is different.
|
// As Async transforms and renditions are so similar, this class provides a way to provide the code that is different.
|
||||||
@@ -95,12 +104,10 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
abstract RenditionDefinition2 getRenditionDefinition();
|
abstract RenditionDefinition2 getRenditionDefinition();
|
||||||
|
|
||||||
void handleUnsupported(UnsupportedOperationException e)
|
void handleUnsupported(UnsupportedOperationException e)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
void throwIllegalStateExceptionIfAlreadyDone(int sourceContentHashCode)
|
void throwIllegalStateExceptionIfAlreadyDone(int sourceContentHashCode)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransactionService transactionService;
|
private TransactionService transactionService;
|
||||||
@@ -217,8 +224,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
@Override
|
@Override
|
||||||
public void transform(NodeRef sourceNodeRef, TransformDefinition transformDefinition)
|
public void transform(NodeRef sourceNodeRef, TransformDefinition transformDefinition)
|
||||||
{
|
{
|
||||||
requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack()
|
requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack() {
|
||||||
{
|
|
||||||
@Override
|
@Override
|
||||||
public String getName()
|
public String getName()
|
||||||
{
|
{
|
||||||
@@ -237,8 +243,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
@Override
|
@Override
|
||||||
public void render(NodeRef sourceNodeRef, String renditionName)
|
public void render(NodeRef sourceNodeRef, String renditionName)
|
||||||
{
|
{
|
||||||
requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack()
|
requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack() {
|
||||||
{
|
|
||||||
@Override
|
@Override
|
||||||
public String getName()
|
public String getName()
|
||||||
{
|
{
|
||||||
@@ -261,7 +266,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
@Override
|
@Override
|
||||||
public void handleUnsupported(UnsupportedOperationException e)
|
public void handleUnsupported(UnsupportedOperationException e)
|
||||||
{
|
{
|
||||||
// On the initial request for a rendition throw the exception.
|
// On the initial request for a rendition throw the exception.
|
||||||
NodeRef renditionNode = getRenditionNode(sourceNodeRef, renditionName);
|
NodeRef renditionNode = getRenditionNode(sourceNodeRef, renditionName);
|
||||||
if (renditionNode == null)
|
if (renditionNode == null)
|
||||||
{
|
{
|
||||||
@@ -277,7 +282,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
|
int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
logger.debug(getName() + ": Source " + sourceContentHashCode + " rendition " + renditionContentHashCode+ " hashCodes");
|
logger.debug(getName() + ": Source " + sourceContentHashCode + " rendition " + renditionContentHashCode + " hashCodes");
|
||||||
}
|
}
|
||||||
if (renditionContentHashCode == sourceContentHashCode)
|
if (renditionContentHashCode == sourceContentHashCode)
|
||||||
{
|
{
|
||||||
@@ -291,7 +296,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!isEnabled())
|
if (!isAsyncAllowed(renderOrTransform))
|
||||||
{
|
{
|
||||||
throw new RenditionService2Exception("Async transforms and renditions are disabled " +
|
throw new RenditionService2Exception("Async transforms and renditions are disabled " +
|
||||||
"(system.thumbnail.generate=false or renditionService2.enabled=false).");
|
"(system.thumbnail.generate=false or renditionService2.enabled=false).");
|
||||||
@@ -299,14 +304,14 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
|
|
||||||
if (!nodeService.exists(sourceNodeRef))
|
if (!nodeService.exists(sourceNodeRef))
|
||||||
{
|
{
|
||||||
throw new IllegalArgumentException(renderOrTransform.getName()+ ": The supplied sourceNodeRef "+sourceNodeRef+" does not exist.");
|
throw new IllegalArgumentException(renderOrTransform.getName() + ": The supplied sourceNodeRef " + sourceNodeRef + " does not exist.");
|
||||||
}
|
}
|
||||||
|
|
||||||
RenditionDefinition2 renditionDefinition = renderOrTransform.getRenditionDefinition();
|
RenditionDefinition2 renditionDefinition = renderOrTransform.getRenditionDefinition();
|
||||||
|
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
logger.debug(renderOrTransform.getName()+ ": transform " +sourceNodeRef);
|
logger.debug(renderOrTransform.getName() + ": transform " + sourceNodeRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
AtomicBoolean supported = new AtomicBoolean(true);
|
AtomicBoolean supported = new AtomicBoolean(true);
|
||||||
@@ -328,14 +333,13 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
}
|
}
|
||||||
|
|
||||||
String user = AuthenticationUtil.getRunAsUser();
|
String user = AuthenticationUtil.getRunAsUser();
|
||||||
RetryingTransactionHelper.RetryingTransactionCallback callback = () ->
|
RetryingTransactionHelper.RetryingTransactionCallback callback = () -> {
|
||||||
{
|
|
||||||
int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
|
int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
|
||||||
if (!supported.get())
|
if (!supported.get())
|
||||||
{
|
{
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
logger.debug(renderOrTransform.getName() +" is not supported. " +
|
logger.debug(renderOrTransform.getName() + " is not supported. " +
|
||||||
"The content might be too big or the source mimetype cannot be converted.");
|
"The content might be too big or the source mimetype cannot be converted.");
|
||||||
}
|
}
|
||||||
failure(sourceNodeRef, renditionDefinition, sourceContentHashCode);
|
failure(sourceNodeRef, renditionDefinition, sourceContentHashCode);
|
||||||
@@ -372,26 +376,24 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
public void failure(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, int transformContentHashCode)
|
public void failure(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, int transformContentHashCode)
|
||||||
{
|
{
|
||||||
// The original transaction may have already have failed
|
// The original transaction may have already have failed
|
||||||
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () ->
|
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
|
||||||
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
|
consume(sourceNodeRef, null, renditionDefinition, transformContentHashCode);
|
||||||
{
|
return null;
|
||||||
consume(sourceNodeRef, null, renditionDefinition, transformContentHashCode);
|
}, false, true));
|
||||||
return null;
|
|
||||||
}, false, true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void consume(NodeRef sourceNodeRef, InputStream transformInputStream, RenditionDefinition2 renditionDefinition,
|
public void consume(NodeRef sourceNodeRef, InputStream transformInputStream, RenditionDefinition2 renditionDefinition,
|
||||||
int transformContentHashCode)
|
int transformContentHashCode)
|
||||||
{
|
{
|
||||||
int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
|
int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
logger.debug("Consume: Source " + sourceContentHashCode + " and transform's source " + transformContentHashCode+" hashcodes");
|
logger.debug("Consume: Source " + sourceContentHashCode + " and transform's source " + transformContentHashCode + " hashcodes");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (renditionDefinition instanceof TransformDefinition)
|
if (renditionDefinition instanceof TransformDefinition)
|
||||||
{
|
{
|
||||||
TransformDefinition transformDefinition = (TransformDefinition)renditionDefinition;
|
TransformDefinition transformDefinition = (TransformDefinition) renditionDefinition;
|
||||||
String targetMimetype = transformDefinition.getTargetMimetype();
|
String targetMimetype = transformDefinition.getTargetMimetype();
|
||||||
if (AsynchronousExtractor.isMetadataExtractMimetype(targetMimetype))
|
if (AsynchronousExtractor.isMetadataExtractMimetype(targetMimetype))
|
||||||
{
|
{
|
||||||
@@ -413,7 +415,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void consumeExtractedMetadata(NodeRef nodeRef, int sourceContentHashCode, InputStream transformInputStream,
|
private void consumeExtractedMetadata(NodeRef nodeRef, int sourceContentHashCode, InputStream transformInputStream,
|
||||||
TransformDefinition transformDefinition, int transformContentHashCode)
|
TransformDefinition transformDefinition, int transformContentHashCode)
|
||||||
{
|
{
|
||||||
if (transformInputStream == null)
|
if (transformInputStream == null)
|
||||||
{
|
{
|
||||||
@@ -440,7 +442,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void consumeEmbeddedMetadata(NodeRef nodeRef, int sourceContentHashCode, InputStream transformInputStream,
|
private void consumeEmbeddedMetadata(NodeRef nodeRef, int sourceContentHashCode, InputStream transformInputStream,
|
||||||
TransformDefinition transformDefinition, int transformContentHashCode)
|
TransformDefinition transformDefinition, int transformContentHashCode)
|
||||||
{
|
{
|
||||||
if (transformInputStream == null)
|
if (transformInputStream == null)
|
||||||
{
|
{
|
||||||
@@ -468,7 +470,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void consumeTransformReply(NodeRef sourceNodeRef, InputStream transformInputStream,
|
private void consumeTransformReply(NodeRef sourceNodeRef, InputStream transformInputStream,
|
||||||
TransformDefinition transformDefinition, int transformContentHashCode)
|
TransformDefinition transformDefinition, int transformContentHashCode)
|
||||||
{
|
{
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
@@ -484,12 +486,10 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a transformation (InputStream) and attaches it as a rendition to the source node.
|
* Takes a transformation (InputStream) and attaches it as a rendition to the source node. Does nothing if there is already a newer rendition. If the transformInputStream is null, this is taken to be a transform failure.
|
||||||
* Does nothing if there is already a newer rendition.
|
|
||||||
* If the transformInputStream is null, this is taken to be a transform failure.
|
|
||||||
*/
|
*/
|
||||||
private void consumeRendition(NodeRef sourceNodeRef, int sourceContentHashCode, InputStream transformInputStream,
|
private void consumeRendition(NodeRef sourceNodeRef, int sourceContentHashCode, InputStream transformInputStream,
|
||||||
RenditionDefinition2 renditionDefinition, int transformContentHashCode)
|
RenditionDefinition2 renditionDefinition, int transformContentHashCode)
|
||||||
{
|
{
|
||||||
String renditionName = renditionDefinition.getRenditionName();
|
String renditionName = renditionDefinition.getRenditionName();
|
||||||
if (transformContentHashCode != sourceContentHashCode)
|
if (transformContentHashCode != sourceContentHashCode)
|
||||||
@@ -507,93 +507,92 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
(transformInputStream == null ? " to null as the transform failed" : " to the transform result"));
|
(transformInputStream == null ? " to null as the transform failed" : " to the transform result"));
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () ->
|
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
|
||||||
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
|
// Ensure that the creation of a rendition does not cause updates to the modified, modifier properties on the source node
|
||||||
|
NodeRef renditionNode = getRenditionNode(sourceNodeRef, renditionName);
|
||||||
|
boolean createRenditionNode = renditionNode == null;
|
||||||
|
boolean sourceHasAspectRenditioned = nodeService.hasAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ruleService.disableRuleType(RuleType.UPDATE);
|
||||||
|
behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE);
|
||||||
|
behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE);
|
||||||
|
|
||||||
|
// If they do not exist create the rendition association and the rendition node.
|
||||||
|
if (createRenditionNode)
|
||||||
|
{
|
||||||
|
renditionNode = createRenditionNode(sourceNodeRef, renditionDefinition);
|
||||||
|
}
|
||||||
|
else if (!nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
|
||||||
|
{
|
||||||
|
nodeService.addAspect(renditionNode, RenditionModel.ASPECT_RENDITION2, null);
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Added rendition2 aspect to rendition " + renditionName + " on " + sourceNodeRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Set ThumbnailLastModified for " + renditionName);
|
||||||
|
}
|
||||||
|
setThumbnailLastModified(sourceNodeRef, renditionName);
|
||||||
|
|
||||||
|
if (transformInputStream != null)
|
||||||
{
|
{
|
||||||
// Ensure that the creation of a rendition does not cause updates to the modified, modifier properties on the source node
|
|
||||||
NodeRef renditionNode = getRenditionNode(sourceNodeRef, renditionName);
|
|
||||||
boolean createRenditionNode = renditionNode == null;
|
|
||||||
boolean sourceHasAspectRenditioned = nodeService.hasAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ruleService.disableRuleType(RuleType.UPDATE);
|
// Set or replace rendition content
|
||||||
behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE);
|
ContentWriter contentWriter = contentService.getWriter(renditionNode, DEFAULT_RENDITION_CONTENT_PROP, true);
|
||||||
behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE);
|
String targetMimetype = renditionDefinition.getTargetMimetype();
|
||||||
|
contentWriter.setMimetype(targetMimetype);
|
||||||
|
contentWriter.setEncoding(DEFAULT_ENCODING);
|
||||||
|
ContentWriter renditionWriter = contentWriter;
|
||||||
|
renditionWriter.putContent(transformInputStream);
|
||||||
|
|
||||||
// If they do not exist create the rendition association and the rendition node.
|
ContentReader contentReader = renditionWriter.getReader();
|
||||||
if (createRenditionNode)
|
long sizeOfRendition = contentReader.getSize();
|
||||||
|
if (sizeOfRendition > 0L)
|
||||||
{
|
{
|
||||||
renditionNode = createRenditionNode(sourceNodeRef, renditionDefinition);
|
|
||||||
}
|
|
||||||
else if (!nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
|
|
||||||
{
|
|
||||||
nodeService.addAspect(renditionNode, RenditionModel.ASPECT_RENDITION2, null);
|
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
logger.debug("Added rendition2 aspect to rendition " + renditionName + " on " + sourceNodeRef);
|
logger.debug("Set rendition hashcode for " + renditionName);
|
||||||
}
|
|
||||||
}
|
|
||||||
if (logger.isDebugEnabled())
|
|
||||||
{
|
|
||||||
logger.debug("Set ThumbnailLastModified for " + renditionName);
|
|
||||||
}
|
|
||||||
setThumbnailLastModified(sourceNodeRef, renditionName);
|
|
||||||
|
|
||||||
if (transformInputStream != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Set or replace rendition content
|
|
||||||
ContentWriter contentWriter = contentService.getWriter(renditionNode, DEFAULT_RENDITION_CONTENT_PROP, true);
|
|
||||||
String targetMimetype = renditionDefinition.getTargetMimetype();
|
|
||||||
contentWriter.setMimetype(targetMimetype);
|
|
||||||
contentWriter.setEncoding(DEFAULT_ENCODING);
|
|
||||||
ContentWriter renditionWriter = contentWriter;
|
|
||||||
renditionWriter.putContent(transformInputStream);
|
|
||||||
|
|
||||||
ContentReader contentReader = renditionWriter.getReader();
|
|
||||||
long sizeOfRendition = contentReader.getSize();
|
|
||||||
if (sizeOfRendition > 0L)
|
|
||||||
{
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Set rendition hashcode for " + renditionName);
|
|
||||||
}
|
|
||||||
nodeService.setProperty(renditionNode, RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE, transformContentHashCode);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.error("Transform was zero bytes for " + renditionName + " on " + sourceNodeRef);
|
|
||||||
clearRenditionContentData(renditionNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
logger.error("Failed to copy transform InputStream into rendition " + renditionName + " on " + sourceNodeRef);
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
nodeService.setProperty(renditionNode, RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE, transformContentHashCode);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
logger.error("Transform was zero bytes for " + renditionName + " on " + sourceNodeRef);
|
||||||
clearRenditionContentData(renditionNode);
|
clearRenditionContentData(renditionNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sourceHasAspectRenditioned)
|
|
||||||
{
|
|
||||||
nodeService.addAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
throw new RenditionService2Exception(TRANSFORMING_ERROR_MESSAGE + e.getMessage(), e);
|
logger.error("Failed to copy transform InputStream into rendition " + renditionName + " on " + sourceNodeRef);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
finally
|
}
|
||||||
{
|
else
|
||||||
behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE);
|
{
|
||||||
behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE);
|
clearRenditionContentData(renditionNode);
|
||||||
ruleService.enableRuleType(RuleType.UPDATE);
|
}
|
||||||
}
|
|
||||||
return null;
|
if (!sourceHasAspectRenditioned)
|
||||||
}, false, true));
|
{
|
||||||
|
nodeService.addAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RenditionService2Exception(TRANSFORMING_ERROR_MESSAGE + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE);
|
||||||
|
behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE);
|
||||||
|
ruleService.enableRuleType(RuleType.UPDATE);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, false, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,14 +633,14 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
|
|
||||||
if (logger.isTraceEnabled())
|
if (logger.isTraceEnabled())
|
||||||
{
|
{
|
||||||
logger.trace("Setting thumbnail last modified date to " + lastModifiedValue +" on source node: " + sourceNodeRef);
|
logger.trace("Setting thumbnail last modified date to " + lastModifiedValue + " on source node: " + sourceNodeRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeService.hasAspect(sourceNodeRef, ContentModel.ASPECT_THUMBNAIL_MODIFICATION))
|
if (nodeService.hasAspect(sourceNodeRef, ContentModel.ASPECT_THUMBNAIL_MODIFICATION))
|
||||||
{
|
{
|
||||||
List<String> thumbnailMods = (List<String>) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA);
|
List<String> thumbnailMods = (List<String>) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA);
|
||||||
String target = null;
|
String target = null;
|
||||||
for (String currThumbnailMod: thumbnailMods)
|
for (String currThumbnailMod : thumbnailMods)
|
||||||
{
|
{
|
||||||
if (currThumbnailMod.startsWith(prefix))
|
if (currThumbnailMod.startsWith(prefix))
|
||||||
{
|
{
|
||||||
@@ -665,8 +664,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the hash code of the source node's content url. As transformations may be returned in a different
|
* Returns the hash code of the source node's content url. As transformations may be returned in a different sequences to which they were requested, this is used work out if a rendition should be replaced.
|
||||||
* sequences to which they were requested, this is used work out if a rendition should be replaced.
|
|
||||||
*/
|
*/
|
||||||
private int getSourceContentHashCode(NodeRef sourceNodeRef)
|
private int getSourceContentHashCode(NodeRef sourceNodeRef)
|
||||||
{
|
{
|
||||||
@@ -675,7 +673,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
if (contentData != null)
|
if (contentData != null)
|
||||||
{
|
{
|
||||||
// Originally we used the contentData URL, but that is not enough if the mimetype changes.
|
// Originally we used the contentData URL, but that is not enough if the mimetype changes.
|
||||||
String contentString = contentData.getContentUrl()+contentData.getMimetype();
|
String contentString = contentData.getContentUrl() + contentData.getMimetype();
|
||||||
if (contentString != null)
|
if (contentString != null)
|
||||||
{
|
{
|
||||||
hashCode = contentString.hashCode();
|
hashCode = contentString.hashCode();
|
||||||
@@ -685,13 +683,11 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the hash code of source node's content url on the rendition node (node may be null) if it does not exist.
|
* Returns the hash code of source node's content url on the rendition node (node may be null) if it does not exist. Used work out if a rendition should be replaced. {@code -2} is returned if the rendition does not exist or was not created by RenditionService2. {@code -1} is returned if there was no source content or the rendition failed.
|
||||||
* Used work out if a rendition should be replaced. {@code -2} is returned if the rendition does not exist or was
|
|
||||||
* not created by RenditionService2. {@code -1} is returned if there was no source content or the rendition failed.
|
|
||||||
*/
|
*/
|
||||||
private int getRenditionContentHashCode(NodeRef renditionNode)
|
private int getRenditionContentHashCode(NodeRef renditionNode)
|
||||||
{
|
{
|
||||||
if ( renditionNode == null || !nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
|
if (renditionNode == null || !nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
|
||||||
{
|
{
|
||||||
return RENDITION2_DOES_NOT_EXIST;
|
return RENDITION2_DOES_NOT_EXIST;
|
||||||
}
|
}
|
||||||
@@ -699,7 +695,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
Serializable hashCode = nodeService.getProperty(renditionNode, PROP_RENDITION_CONTENT_HASH_CODE);
|
Serializable hashCode = nodeService.getProperty(renditionNode, PROP_RENDITION_CONTENT_HASH_CODE);
|
||||||
return hashCode == null
|
return hashCode == null
|
||||||
? SOURCE_HAS_NO_CONTENT
|
? SOURCE_HAS_NO_CONTENT
|
||||||
: (int)hashCode;
|
: (int) hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private NodeRef getRenditionNode(NodeRef sourceNodeRef, String renditionName)
|
private NodeRef getRenditionNode(NodeRef sourceNodeRef, String renditionName)
|
||||||
@@ -773,11 +769,12 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method checks whether the specified source node is of a content class which has been registered for
|
* This method checks whether the specified source node is of a content class which has been registered for rendition prevention.
|
||||||
* rendition prevention.
|
|
||||||
*
|
*
|
||||||
* @param sourceNode the node to check.
|
* @param sourceNode
|
||||||
* @throws RenditionService2PreventedException if the source node is configured for rendition prevention.
|
* the node to check.
|
||||||
|
* @throws RenditionService2PreventedException
|
||||||
|
* if the source node is configured for rendition prevention.
|
||||||
*/
|
*/
|
||||||
// This code is based on the old RenditionServiceImpl.checkSourceNodeForPreventionClass(...)
|
// This code is based on the old RenditionServiceImpl.checkSourceNodeForPreventionClass(...)
|
||||||
private void checkSourceNodeForPreventionClass(NodeRef sourceNode)
|
private void checkSourceNodeForPreventionClass(NodeRef sourceNode)
|
||||||
@@ -823,7 +820,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
|
|
||||||
for (ChildAssociationRef childAssoc : childAsocs)
|
for (ChildAssociationRef childAssoc : childAsocs)
|
||||||
{
|
{
|
||||||
NodeRef renditionNode = childAssoc.getChildRef();
|
NodeRef renditionNode = childAssoc.getChildRef();
|
||||||
if (isRenditionAvailable(sourceNodeRef, renditionNode))
|
if (isRenditionAvailable(sourceNodeRef, renditionNode))
|
||||||
{
|
{
|
||||||
result.add(childAssoc);
|
result.add(childAssoc);
|
||||||
@@ -833,8 +830,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if the rendition is available. Failed renditions (there was an error) don't have a contentUrl
|
* Indicates if the rendition is available. Failed renditions (there was an error) don't have a contentUrl and out of date renditions or those still being created don't have a matching contentHashCode.
|
||||||
* and out of date renditions or those still being created don't have a matching contentHashCode.
|
|
||||||
*/
|
*/
|
||||||
public boolean isRenditionAvailable(NodeRef sourceNodeRef, NodeRef renditionNode)
|
public boolean isRenditionAvailable(NodeRef sourceNodeRef, NodeRef renditionNode)
|
||||||
{
|
{
|
||||||
@@ -852,7 +848,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
|
int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
logger.debug("isRenditionAvailable source " + sourceContentHashCode + " and rendition " + renditionContentHashCode+" hashcodes");
|
logger.debug("isRenditionAvailable source " + sourceContentHashCode + " and rendition " + renditionContentHashCode + " hashcodes");
|
||||||
}
|
}
|
||||||
if (sourceContentHashCode != renditionContentHashCode)
|
if (sourceContentHashCode != renditionContentHashCode)
|
||||||
{
|
{
|
||||||
@@ -892,19 +888,17 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
}
|
}
|
||||||
ChildAssociationRef childAssoc = renditions.get(0);
|
ChildAssociationRef childAssoc = renditions.get(0);
|
||||||
NodeRef renditionNode = childAssoc.getChildRef();
|
NodeRef renditionNode = childAssoc.getChildRef();
|
||||||
return !isRenditionAvailable(sourceNodeRef, renditionNode) ? null: childAssoc;
|
return !isRenditionAvailable(sourceNodeRef, renditionNode) ? null : childAssoc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearRenditionContentDataInTransaction(NodeRef renditionNode)
|
public void clearRenditionContentDataInTransaction(NodeRef renditionNode)
|
||||||
{
|
{
|
||||||
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () ->
|
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
|
||||||
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
|
clearRenditionContentData(renditionNode);
|
||||||
{
|
return null;
|
||||||
clearRenditionContentData(renditionNode);
|
}, false, true));
|
||||||
return null;
|
|
||||||
}, false, true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -950,4 +944,23 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if the given transform callback is a text extract transform for content indexing or metadata extract/embed.
|
||||||
|
private boolean isTextOrMetadataExtractTransform(RenderOrTransformCallBack renderOrTransform)
|
||||||
|
{
|
||||||
|
RenditionDefinition2 renditionDefinition = renderOrTransform.getRenditionDefinition();
|
||||||
|
return renditionDefinition != null && ALLOWED_MIMETYPES.contains(renditionDefinition.getTargetMimetype());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAsyncAllowed(RenderOrTransformCallBack renderOrTransform)
|
||||||
|
{
|
||||||
|
// If enabled is false, all async transforms/renditions must be blocked
|
||||||
|
if (!enabled)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If thumbnails are disabled, allow only text extract or metadata extract/embed transforms
|
||||||
|
return thumbnailsEnabled || isTextOrMetadataExtractTransform(renderOrTransform);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -25,22 +25,20 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.security.authentication.identityservice;
|
package org.alfresco.repo.security.authentication.identityservice;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.alfresco.repo.management.subsystems.ActivateableBean;
|
import org.alfresco.repo.management.subsystems.ActivateableBean;
|
||||||
import org.alfresco.repo.security.authentication.AbstractAuthenticationComponent;
|
import org.alfresco.repo.security.authentication.AbstractAuthenticationComponent;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationException;
|
import org.alfresco.repo.security.authentication.AuthenticationException;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationGrant;
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationGrant;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.IdentityServiceFacadeException;
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.IdentityServiceFacadeException;
|
||||||
import org.apache.commons.logging.Log;
|
import org.alfresco.repo.security.authentication.identityservice.user.OIDCUserInfo;
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Authenticates a user against Identity Service (Keycloak/Authorization Server).
|
* Authenticates a user against Identity Service (Keycloak/Authorization Server). {@link IdentityServiceFacade} is used to verify provided user credentials. User is set as the current user if the user credentials are valid. <br>
|
||||||
* {@link IdentityServiceFacade} is used to verify provided user credentials. User is set as the current user if the
|
* The {@link IdentityServiceAuthenticationComponent#identityServiceFacade} can be null in which case this authenticator will just fall through to the next one in the chain.
|
||||||
* user credentials are valid.
|
|
||||||
* <br>
|
|
||||||
* The {@link IdentityServiceAuthenticationComponent#identityServiceFacade} can be null in which case this authenticator
|
|
||||||
* will just fall through to the next one in the chain.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class IdentityServiceAuthenticationComponent extends AbstractAuthenticationComponent implements ActivateableBean
|
public class IdentityServiceAuthenticationComponent extends AbstractAuthenticationComponent implements ActivateableBean
|
||||||
@@ -48,7 +46,7 @@ public class IdentityServiceAuthenticationComponent extends AbstractAuthenticati
|
|||||||
private final Log LOGGER = LogFactory.getLog(IdentityServiceAuthenticationComponent.class);
|
private final Log LOGGER = LogFactory.getLog(IdentityServiceAuthenticationComponent.class);
|
||||||
/** client used to authenticate user credentials against Authorization Server **/
|
/** client used to authenticate user credentials against Authorization Server **/
|
||||||
private IdentityServiceFacade identityServiceFacade;
|
private IdentityServiceFacade identityServiceFacade;
|
||||||
/** enabled flag for the identity service subsystem**/
|
/** enabled flag for the identity service subsystem **/
|
||||||
private boolean active;
|
private boolean active;
|
||||||
|
|
||||||
private IdentityServiceJITProvisioningHandler jitProvisioningHandler;
|
private IdentityServiceJITProvisioningHandler jitProvisioningHandler;
|
||||||
@@ -89,8 +87,8 @@ public class IdentityServiceAuthenticationComponent extends AbstractAuthenticati
|
|||||||
IdentityServiceFacade.AccessTokenAuthorization accessTokenAuthorization = identityServiceFacade.authorize(AuthorizationGrant.password(userName, String.valueOf(password)));
|
IdentityServiceFacade.AccessTokenAuthorization accessTokenAuthorization = identityServiceFacade.authorize(AuthorizationGrant.password(userName, String.valueOf(password)));
|
||||||
|
|
||||||
String normalizedUsername = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(accessTokenAuthorization.getAccessToken().getTokenValue())
|
String normalizedUsername = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(accessTokenAuthorization.getAccessToken().getTokenValue())
|
||||||
.map(OIDCUserInfo::username)
|
.map(OIDCUserInfo::username)
|
||||||
.orElseThrow(() -> new AuthenticationException("Failed to extract username from token and user info endpoint."));
|
.orElseThrow(() -> new AuthenticationException("Failed to extract username from token and user info endpoint."));
|
||||||
// Verification was successful so treat as authenticated user
|
// Verification was successful so treat as authenticated user
|
||||||
setCurrentUser(normalizedUsername);
|
setCurrentUser(normalizedUsername);
|
||||||
}
|
}
|
||||||
|
@@ -69,6 +69,13 @@ public class IdentityServiceConfig
|
|||||||
private boolean clientIdValidationDisabled;
|
private boolean clientIdValidationDisabled;
|
||||||
private String adminConsoleRedirectPath;
|
private String adminConsoleRedirectPath;
|
||||||
private String signatureAlgorithms;
|
private String signatureAlgorithms;
|
||||||
|
private String adminConsoleScopes;
|
||||||
|
private String passwordGrantScopes;
|
||||||
|
private String issuerAttribute;
|
||||||
|
private String firstNameAttribute;
|
||||||
|
private String lastNameAttribute;
|
||||||
|
private String emailAttribute;
|
||||||
|
private long jwtClockSkewMs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -81,7 +88,8 @@ public class IdentityServiceConfig
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param clientConnectionTimeout Client connection timeout in milliseconds.
|
* @param clientConnectionTimeout
|
||||||
|
* Client connection timeout in milliseconds.
|
||||||
*/
|
*/
|
||||||
public void setClientConnectionTimeout(int clientConnectionTimeout)
|
public void setClientConnectionTimeout(int clientConnectionTimeout)
|
||||||
{
|
{
|
||||||
@@ -99,7 +107,8 @@ public class IdentityServiceConfig
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param clientSocketTimeout Client socket timeout in milliseconds.
|
* @param clientSocketTimeout
|
||||||
|
* Client socket timeout in milliseconds.
|
||||||
*/
|
*/
|
||||||
public void setClientSocketTimeout(int clientSocketTimeout)
|
public void setClientSocketTimeout(int clientSocketTimeout)
|
||||||
{
|
{
|
||||||
@@ -139,13 +148,13 @@ public class IdentityServiceConfig
|
|||||||
public String getAuthServerUrl()
|
public String getAuthServerUrl()
|
||||||
{
|
{
|
||||||
return Optional.ofNullable(realm)
|
return Optional.ofNullable(realm)
|
||||||
.filter(StringUtils::isNotBlank)
|
.filter(StringUtils::isNotBlank)
|
||||||
.filter(realm -> StringUtils.isNotBlank(authServerUrl))
|
.filter(realm -> StringUtils.isNotBlank(authServerUrl))
|
||||||
.map(realm -> UriComponentsBuilder.fromUriString(authServerUrl)
|
.map(realm -> UriComponentsBuilder.fromUriString(authServerUrl)
|
||||||
.pathSegment(REALMS, realm)
|
.pathSegment(REALMS, realm)
|
||||||
.build()
|
.build()
|
||||||
.toString())
|
.toString())
|
||||||
.orElse(authServerUrl);
|
.orElse(authServerUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAuthServerUrl(String authServerUrl)
|
public void setAuthServerUrl(String authServerUrl)
|
||||||
@@ -181,7 +190,7 @@ public class IdentityServiceConfig
|
|||||||
public String getClientSecret()
|
public String getClientSecret()
|
||||||
{
|
{
|
||||||
return Optional.ofNullable(clientSecret)
|
return Optional.ofNullable(clientSecret)
|
||||||
.orElse("");
|
.orElse("");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAllowAnyHostname(boolean allowAnyHostname)
|
public void setAllowAnyHostname(boolean allowAnyHostname)
|
||||||
@@ -317,14 +326,88 @@ public class IdentityServiceConfig
|
|||||||
public Set<SignatureAlgorithm> getSignatureAlgorithms()
|
public Set<SignatureAlgorithm> getSignatureAlgorithms()
|
||||||
{
|
{
|
||||||
return Stream.of(signatureAlgorithms.split(","))
|
return Stream.of(signatureAlgorithms.split(","))
|
||||||
.map(String::trim)
|
.map(String::trim)
|
||||||
.map(SignatureAlgorithm::from)
|
.map(SignatureAlgorithm::from)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.collect(Collectors.toUnmodifiableSet());
|
.collect(Collectors.toUnmodifiableSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSignatureAlgorithms(String signatureAlgorithms)
|
public void setSignatureAlgorithms(String signatureAlgorithms)
|
||||||
{
|
{
|
||||||
this.signatureAlgorithms = signatureAlgorithms;
|
this.signatureAlgorithms = signatureAlgorithms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getIssuerAttribute()
|
||||||
|
{
|
||||||
|
return issuerAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIssuerAttribute(String issuerAttribute)
|
||||||
|
{
|
||||||
|
this.issuerAttribute = issuerAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getAdminConsoleScopes()
|
||||||
|
{
|
||||||
|
return Stream.of(adminConsoleScopes.split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.collect(Collectors.toUnmodifiableSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAdminConsoleScopes(String adminConsoleScopes)
|
||||||
|
{
|
||||||
|
this.adminConsoleScopes = adminConsoleScopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getPasswordGrantScopes()
|
||||||
|
{
|
||||||
|
return Stream.of(passwordGrantScopes.split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.collect(Collectors.toUnmodifiableSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPasswordGrantScopes(String passwordGrantScopes)
|
||||||
|
{
|
||||||
|
this.passwordGrantScopes = passwordGrantScopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstNameAttribute(String firstNameAttribute)
|
||||||
|
{
|
||||||
|
this.firstNameAttribute = firstNameAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastNameAttribute(String lastNameAttribute)
|
||||||
|
{
|
||||||
|
this.lastNameAttribute = lastNameAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmailAttribute(String emailAttribute)
|
||||||
|
{
|
||||||
|
this.emailAttribute = emailAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJwtClockSkewMs(long jwtClockSkewMs)
|
||||||
|
{
|
||||||
|
this.jwtClockSkewMs = jwtClockSkewMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstNameAttribute()
|
||||||
|
{
|
||||||
|
return firstNameAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastNameAttribute()
|
||||||
|
{
|
||||||
|
return lastNameAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmailAttribute()
|
||||||
|
{
|
||||||
|
return emailAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getJwtClockSkewMs()
|
||||||
|
{
|
||||||
|
return jwtClockSkewMs;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -34,6 +34,9 @@ import java.util.Optional;
|
|||||||
|
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
|
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.DecodedTokenUser;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.UserInfoAttrMapping;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows to interact with the Identity Service
|
* Allows to interact with the Identity Service
|
||||||
*/
|
*/
|
||||||
@@ -41,28 +44,36 @@ public interface IdentityServiceFacade
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Returns {@link AccessToken} based authorization for provided {@link AuthorizationGrant}.
|
* Returns {@link AccessToken} based authorization for provided {@link AuthorizationGrant}.
|
||||||
* @param grant the OAuth2 grant provided by the Resource Owner.
|
*
|
||||||
|
* @param grant
|
||||||
|
* the OAuth2 grant provided by the Resource Owner.
|
||||||
* @return {@link AccessTokenAuthorization} containing access token and optional refresh token.
|
* @return {@link AccessTokenAuthorization} containing access token and optional refresh token.
|
||||||
* @throws {@link AuthorizationException} when provided grant cannot be exchanged for the access token.
|
* @throws {@link
|
||||||
|
* AuthorizationException} when provided grant cannot be exchanged for the access token.
|
||||||
*/
|
*/
|
||||||
AccessTokenAuthorization authorize(AuthorizationGrant grant) throws AuthorizationException;
|
AccessTokenAuthorization authorize(AuthorizationGrant grant) throws AuthorizationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes the access token into the {@link DecodedAccessToken} which contains claims connected with a given token.
|
* Decodes the access token into the {@link DecodedAccessToken} which contains claims connected with a given token.
|
||||||
* @param token {@link String} with encoded access token value.
|
*
|
||||||
|
* @param token
|
||||||
|
* {@link String} with encoded access token value.
|
||||||
* @return {@link DecodedAccessToken} containing decoded claims.
|
* @return {@link DecodedAccessToken} containing decoded claims.
|
||||||
* @throws {@link TokenDecodingException} when token decoding failed.
|
* @throws {@link
|
||||||
|
* TokenDecodingException} when token decoding failed.
|
||||||
*/
|
*/
|
||||||
DecodedAccessToken decodeToken(String token) throws TokenDecodingException;
|
DecodedAccessToken decodeToken(String token) throws TokenDecodingException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets claims about the authenticated user,
|
* Gets claims about the authenticated user, such as name and email address, via the UserInfo endpoint of the OpenID provider.
|
||||||
* such as name and email address, via the UserInfo endpoint of the OpenID provider.
|
*
|
||||||
* @param token {@link String} with encoded access token value.
|
* @param token
|
||||||
* @param principalAttribute {@link String} the attribute name used to access the user's name from the user info response.
|
* {@link String} with encoded access token value.
|
||||||
* @return {@link OIDCUserInfo} containing user claims.
|
* @param userInfoAttrMapping
|
||||||
|
* {@link UserInfoAttrMapping} containing the mapping of claims.
|
||||||
|
* @return {@link DecodedTokenUser} containing user claims or {@link Optional#empty()} if the token does not contain a username claim.
|
||||||
*/
|
*/
|
||||||
Optional<OIDCUserInfo> getUserInfo(String token, String principalAttribute);
|
Optional<DecodedTokenUser> getUserInfo(String token, UserInfoAttrMapping userInfoAttrMapping);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a client registration
|
* Gets a client registration
|
||||||
@@ -129,19 +140,23 @@ public interface IdentityServiceFacade
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Required {@link AccessToken}
|
* Required {@link AccessToken}
|
||||||
|
*
|
||||||
* @return {@link AccessToken}
|
* @return {@link AccessToken}
|
||||||
*/
|
*/
|
||||||
AccessToken getAccessToken();
|
AccessToken getAccessToken();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional refresh token.
|
* Optional refresh token.
|
||||||
|
*
|
||||||
* @return Refresh token or {@code null}
|
* @return Refresh token or {@code null}
|
||||||
*/
|
*/
|
||||||
String getRefreshTokenValue();
|
String getRefreshTokenValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AccessToken {
|
interface AccessToken
|
||||||
|
{
|
||||||
String getTokenValue();
|
String getTokenValue();
|
||||||
|
|
||||||
Instant getExpiresAt();
|
Instant getExpiresAt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +165,8 @@ public interface IdentityServiceFacade
|
|||||||
Object getClaim(String claim);
|
Object getClaim(String claim);
|
||||||
}
|
}
|
||||||
|
|
||||||
class AuthorizationGrant {
|
class AuthorizationGrant
|
||||||
|
{
|
||||||
private final String username;
|
private final String username;
|
||||||
private final String password;
|
private final String password;
|
||||||
private final String refreshToken;
|
private final String refreshToken;
|
||||||
|
@@ -72,9 +72,8 @@ import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
|
|||||||
import com.nimbusds.oauth2.sdk.Scope;
|
import com.nimbusds.oauth2.sdk.Scope;
|
||||||
import com.nimbusds.oauth2.sdk.id.Identifier;
|
import com.nimbusds.oauth2.sdk.id.Identifier;
|
||||||
import com.nimbusds.oauth2.sdk.id.Issuer;
|
import com.nimbusds.oauth2.sdk.id.Issuer;
|
||||||
|
import com.nimbusds.openid.connect.sdk.claims.PersonClaims;
|
||||||
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
|
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
|
||||||
|
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.IdentityServiceFacadeException;
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
@@ -98,6 +97,7 @@ import org.springframework.http.client.ClientHttpRequest;
|
|||||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||||
import org.springframework.http.converter.FormHttpMessageConverter;
|
import org.springframework.http.converter.FormHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||||
import org.springframework.security.converter.RsaKeyConverters;
|
import org.springframework.security.converter.RsaKeyConverters;
|
||||||
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
|
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
|
||||||
import org.springframework.security.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory;
|
import org.springframework.security.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory;
|
||||||
@@ -125,6 +125,10 @@ import org.springframework.web.client.RestOperations;
|
|||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.IdentityServiceFacadeException;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.DecodedTokenUser;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.UserInfoAttrMapping;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance of {@link IdentityServiceFacade}. <br>
|
* Creates an instance of {@link IdentityServiceFacade}. <br>
|
||||||
* This factory can return a null if it is disabled.
|
* This factory can return a null if it is disabled.
|
||||||
@@ -134,6 +138,7 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
private static final Log LOGGER = LogFactory.getLog(IdentityServiceFacadeFactoryBean.class);
|
private static final Log LOGGER = LogFactory.getLog(IdentityServiceFacadeFactoryBean.class);
|
||||||
|
|
||||||
private static final JOSEObjectType AT_JWT = new JOSEObjectType("at+jwt");
|
private static final JOSEObjectType AT_JWT = new JOSEObjectType("at+jwt");
|
||||||
|
private static final String DEFAULT_ISSUER_ATTR = "issuer";
|
||||||
|
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
private SpringBasedIdentityServiceFacadeFactory factory;
|
private SpringBasedIdentityServiceFacadeFactory factory;
|
||||||
@@ -146,10 +151,9 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
public void setIdentityServiceConfig(IdentityServiceConfig identityServiceConfig)
|
public void setIdentityServiceConfig(IdentityServiceConfig identityServiceConfig)
|
||||||
{
|
{
|
||||||
factory = new SpringBasedIdentityServiceFacadeFactory(
|
factory = new SpringBasedIdentityServiceFacadeFactory(
|
||||||
new HttpClientProvider(identityServiceConfig)::createHttpClient,
|
new HttpClientProvider(identityServiceConfig)::createHttpClient,
|
||||||
new ClientRegistrationProvider(identityServiceConfig)::createClientRegistration,
|
new ClientRegistrationProvider(identityServiceConfig)::createClientRegistration,
|
||||||
new JwtDecoderProvider(identityServiceConfig)::createJwtDecoder
|
new JwtDecoderProvider(identityServiceConfig)::createJwtDecoder);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -207,9 +211,9 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<OIDCUserInfo> getUserInfo(String token, String principalAttribute)
|
public Optional<DecodedTokenUser> getUserInfo(String token, UserInfoAttrMapping userInfoAttrMapping)
|
||||||
{
|
{
|
||||||
return getTargetFacade().getUserInfo(token, principalAttribute);
|
return getTargetFacade().getUserInfo(token, userInfoAttrMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -221,8 +225,7 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
private IdentityServiceFacade getTargetFacade()
|
private IdentityServiceFacade getTargetFacade()
|
||||||
{
|
{
|
||||||
return ofNullable(targetFacade.get())
|
return ofNullable(targetFacade.get())
|
||||||
.orElseGet(() -> targetFacade.updateAndGet(prev ->
|
.orElseGet(() -> targetFacade.updateAndGet(prev -> ofNullable(prev).orElseGet(this::createTargetFacade)));
|
||||||
ofNullable(prev).orElseGet(this::createTargetFacade)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IdentityServiceFacade createTargetFacade()
|
private IdentityServiceFacade createTargetFacade()
|
||||||
@@ -250,9 +253,9 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
private final BiFunction<RestOperations, ProviderDetails, JwtDecoder> jwtDecoderProvider;
|
private final BiFunction<RestOperations, ProviderDetails, JwtDecoder> jwtDecoderProvider;
|
||||||
|
|
||||||
SpringBasedIdentityServiceFacadeFactory(
|
SpringBasedIdentityServiceFacadeFactory(
|
||||||
Supplier<HttpClient> httpClientProvider,
|
Supplier<HttpClient> httpClientProvider,
|
||||||
Function<RestOperations, ClientRegistration> clientRegistrationProvider,
|
Function<RestOperations, ClientRegistration> clientRegistrationProvider,
|
||||||
BiFunction<RestOperations, ProviderDetails, JwtDecoder> jwtDecoderProvider)
|
BiFunction<RestOperations, ProviderDetails, JwtDecoder> jwtDecoderProvider)
|
||||||
{
|
{
|
||||||
this.httpClientProvider = requireNonNull(httpClientProvider);
|
this.httpClientProvider = requireNonNull(httpClientProvider);
|
||||||
this.clientRegistrationProvider = requireNonNull(clientRegistrationProvider);
|
this.clientRegistrationProvider = requireNonNull(clientRegistrationProvider);
|
||||||
@@ -261,25 +264,25 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
|
|
||||||
private IdentityServiceFacade createIdentityServiceFacade()
|
private IdentityServiceFacade createIdentityServiceFacade()
|
||||||
{
|
{
|
||||||
//Here we preserve the behaviour of previously used Keycloak Adapter
|
// Here we preserve the behaviour of previously used Keycloak Adapter
|
||||||
// * Client is authenticating itself using basic auth
|
// * Client is authenticating itself using basic auth
|
||||||
// * Resource Owner Password Credentials Flow is used to authenticate Resource Owner
|
// * Resource Owner Password Credentials Flow is used to authenticate Resource Owner
|
||||||
|
|
||||||
final ClientHttpRequestFactory httpRequestFactory = new CustomClientHttpRequestFactory(
|
final ClientHttpRequestFactory httpRequestFactory = new CustomClientHttpRequestFactory(
|
||||||
httpClientProvider.get());
|
httpClientProvider.get());
|
||||||
final RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
|
final RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
|
||||||
final ClientRegistration clientRegistration = clientRegistrationProvider.apply(restTemplate);
|
final ClientRegistration clientRegistration = clientRegistrationProvider.apply(restTemplate);
|
||||||
final JwtDecoder jwtDecoder = jwtDecoderProvider.apply(restTemplate,
|
final JwtDecoder jwtDecoder = jwtDecoderProvider.apply(restTemplate,
|
||||||
clientRegistration.getProviderDetails());
|
clientRegistration.getProviderDetails());
|
||||||
|
|
||||||
return new SpringBasedIdentityServiceFacade(createOAuth2RestTemplate(httpRequestFactory),
|
return new SpringBasedIdentityServiceFacade(createOAuth2RestTemplate(httpRequestFactory),
|
||||||
clientRegistration, jwtDecoder);
|
clientRegistration, jwtDecoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RestTemplate createOAuth2RestTemplate(ClientHttpRequestFactory requestFactory)
|
private RestTemplate createOAuth2RestTemplate(ClientHttpRequestFactory requestFactory)
|
||||||
{
|
{
|
||||||
final RestTemplate restTemplate = new RestTemplate(
|
final RestTemplate restTemplate = new RestTemplate(
|
||||||
Arrays.asList(new FormHttpMessageConverter(), new OAuth2AccessTokenResponseHttpMessageConverter()));
|
Arrays.asList(new FormHttpMessageConverter(), new OAuth2AccessTokenResponseHttpMessageConverter(), new MappingJackson2HttpMessageConverter()));
|
||||||
restTemplate.setRequestFactory(requestFactory);
|
restTemplate.setRequestFactory(requestFactory);
|
||||||
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
|
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
|
||||||
|
|
||||||
@@ -323,29 +326,29 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
private void applyConnectionConfiguration(PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder)
|
private void applyConnectionConfiguration(PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder)
|
||||||
{
|
{
|
||||||
final ConnectionConfig connectionConfig = ConnectionConfig.custom()
|
final ConnectionConfig connectionConfig = ConnectionConfig.custom()
|
||||||
.setConnectTimeout(config.getClientConnectionTimeout(), TimeUnit.MILLISECONDS)
|
.setConnectTimeout(config.getClientConnectionTimeout(), TimeUnit.MILLISECONDS)
|
||||||
.setSocketTimeout(config.getClientSocketTimeout(), TimeUnit.MILLISECONDS)
|
.setSocketTimeout(config.getClientSocketTimeout(), TimeUnit.MILLISECONDS)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
connectionManagerBuilder.setMaxConnTotal(config.getConnectionPoolSize());
|
connectionManagerBuilder.setMaxConnTotal(config.getConnectionPoolSize());
|
||||||
connectionManagerBuilder.setDefaultConnectionConfig(connectionConfig);
|
connectionManagerBuilder.setDefaultConnectionConfig(connectionConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applySSLConfiguration(PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder)
|
private void applySSLConfiguration(PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
SSLContextBuilder sslContextBuilder = null;
|
SSLContextBuilder sslContextBuilder = null;
|
||||||
if (config.isDisableTrustManager())
|
if (config.isDisableTrustManager())
|
||||||
{
|
{
|
||||||
sslContextBuilder = SSLContexts.custom()
|
sslContextBuilder = SSLContexts.custom()
|
||||||
.loadTrustMaterial(TrustAllStrategy.INSTANCE);
|
.loadTrustMaterial(TrustAllStrategy.INSTANCE);
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (isDefined(config.getTruststore()))
|
else if (isDefined(config.getTruststore()))
|
||||||
{
|
{
|
||||||
final char[] truststorePassword = asCharArray(config.getTruststorePassword(), null);
|
final char[] truststorePassword = asCharArray(config.getTruststorePassword(), null);
|
||||||
sslContextBuilder = SSLContexts.custom()
|
sslContextBuilder = SSLContexts.custom()
|
||||||
.loadTrustMaterial(new File(config.getTruststore()), truststorePassword);
|
.loadTrustMaterial(new File(config.getTruststore()), truststorePassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDefined(config.getClientKeystore()))
|
if (isDefined(config.getClientKeystore()))
|
||||||
@@ -377,9 +380,9 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
private char[] asCharArray(String value, char... nullValue)
|
private char[] asCharArray(String value, char... nullValue)
|
||||||
{
|
{
|
||||||
return ofNullable(value)
|
return ofNullable(value)
|
||||||
.filter(not(String::isBlank))
|
.filter(not(String::isBlank))
|
||||||
.map(String::toCharArray)
|
.map(String::toCharArray)
|
||||||
.orElse(nullValue);
|
.orElse(nullValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,8 +390,6 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
{
|
{
|
||||||
private final IdentityServiceConfig config;
|
private final IdentityServiceConfig config;
|
||||||
|
|
||||||
private static final Set<String> SCOPES = Set.of("openid", "profile", "email");
|
|
||||||
|
|
||||||
ClientRegistrationProvider(IdentityServiceConfig config)
|
ClientRegistrationProvider(IdentityServiceConfig config)
|
||||||
{
|
{
|
||||||
this.config = requireNonNull(config);
|
this.config = requireNonNull(config);
|
||||||
@@ -397,16 +398,16 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
public ClientRegistration createClientRegistration(final RestOperations rest)
|
public ClientRegistration createClientRegistration(final RestOperations rest)
|
||||||
{
|
{
|
||||||
return possibleMetadataURIs()
|
return possibleMetadataURIs()
|
||||||
.stream()
|
.stream()
|
||||||
.map(u -> extractMetadata(rest, u))
|
.map(u -> extractMetadata(rest, u))
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.map(this::validateDiscoveryDocument)
|
.map(this::validateDiscoveryDocument)
|
||||||
.map(this::createBuilder)
|
.map(this::createBuilder)
|
||||||
.map(this::configureClientAuthentication)
|
.map(this::configureClientAuthentication)
|
||||||
.map(Builder::build)
|
.map(Builder::build)
|
||||||
.orElseThrow(() -> new IllegalStateException("Failed to create ClientRegistration."));
|
.orElseThrow(() -> new IllegalStateException("Failed to create ClientRegistration."));
|
||||||
}
|
}
|
||||||
|
|
||||||
private OIDCProviderMetadata validateDiscoveryDocument(OIDCProviderMetadata metadata)
|
private OIDCProviderMetadata validateDiscoveryDocument(OIDCProviderMetadata metadata)
|
||||||
@@ -423,11 +424,11 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
URI metadataIssuerURI = new URI(metadata.getIssuer().getValue());
|
URI metadataIssuerURI = new URI(metadata.getIssuer().getValue());
|
||||||
validateOIDCEndpoint(metadataIssuerURI, "Issuer");
|
validateOIDCEndpoint(metadataIssuerURI, "Issuer");
|
||||||
if (StringUtils.isNotBlank(config.getIssuerUrl()) &&
|
if (StringUtils.isNotBlank(config.getIssuerUrl()) &&
|
||||||
!metadataIssuerURI.equals(URI.create(config.getIssuerUrl())))
|
!metadataIssuerURI.equals(URI.create(config.getIssuerUrl())))
|
||||||
{
|
{
|
||||||
throw new IdentityServiceException("Failed to create ClientRegistration. "
|
throw new IdentityServiceException("Failed to create ClientRegistration. "
|
||||||
+ "The Issuer value from the OIDC Discovery Endpoint does not align with the provided Issuer. Expected `%s` but found `%s`"
|
+ "The Issuer value from the OIDC Discovery Endpoint does not align with the provided Issuer. Expected `%s` but found `%s`"
|
||||||
.formatted(config.getIssuerUrl(), metadata.getIssuer().getValue()));
|
.formatted(config.getIssuerUrl(), metadata.getIssuer().getValue()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (URISyntaxException e)
|
catch (URISyntaxException e)
|
||||||
@@ -454,37 +455,37 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
private ClientRegistration.Builder createBuilder(OIDCProviderMetadata metadata)
|
private ClientRegistration.Builder createBuilder(OIDCProviderMetadata metadata)
|
||||||
{
|
{
|
||||||
final String authUri = Optional.of(metadata)
|
final String authUri = Optional.of(metadata)
|
||||||
.map(OIDCProviderMetadata::getAuthorizationEndpointURI)
|
.map(OIDCProviderMetadata::getAuthorizationEndpointURI)
|
||||||
.map(URI::toASCIIString)
|
.map(URI::toASCIIString)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
|
|
||||||
final String issuerUri = Optional.of(metadata)
|
var metadataIssuer = getMetadataIssuer(metadata, config);
|
||||||
.map(OIDCProviderMetadata::getIssuer)
|
final String issuerUri = metadataIssuer
|
||||||
.map(Issuer::getValue)
|
.orElseGet(() -> (StringUtils.isNotBlank(config.getRealm()) && StringUtils.isBlank(config.getIssuerUrl())) ? config.getAuthServerUrl() : config.getIssuerUrl());
|
||||||
.orElseGet(() -> (StringUtils.isNotBlank(config.getRealm()) && StringUtils.isBlank(config.getIssuerUrl())) ?
|
|
||||||
config.getAuthServerUrl() :
|
final var usernameAttribute = StringUtils.isNotBlank(config.getPrincipalAttribute()) ? config.getPrincipalAttribute() : PersonClaims.PREFERRED_USERNAME_CLAIM_NAME;
|
||||||
config.getIssuerUrl());
|
|
||||||
|
|
||||||
return ClientRegistration
|
return ClientRegistration
|
||||||
.withRegistrationId("ids")
|
.withRegistrationId("ids")
|
||||||
.authorizationUri(authUri)
|
.authorizationUri(authUri)
|
||||||
.tokenUri(metadata.getTokenEndpointURI().toASCIIString())
|
.tokenUri(metadata.getTokenEndpointURI().toASCIIString())
|
||||||
.jwkSetUri(metadata.getJWKSetURI().toASCIIString())
|
.jwkSetUri(metadata.getJWKSetURI().toASCIIString())
|
||||||
.issuerUri(issuerUri)
|
.issuerUri(issuerUri)
|
||||||
.userInfoUri(metadata.getUserInfoEndpointURI().toASCIIString())
|
.userInfoUri(metadata.getUserInfoEndpointURI().toASCIIString())
|
||||||
.scope(getSupportedScopes(metadata.getScopes()))
|
.userNameAttributeName(usernameAttribute)
|
||||||
.providerConfigurationMetadata(createMetadata(metadata))
|
.scope(getSupportedScopes(metadata.getScopes()))
|
||||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD);
|
.providerConfigurationMetadata(createMetadata(metadata))
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> createMetadata(OIDCProviderMetadata metadata)
|
private Map<String, Object> createMetadata(OIDCProviderMetadata metadata)
|
||||||
{
|
{
|
||||||
Map<String, Object> configurationMetadata = new LinkedHashMap<>();
|
Map<String, Object> configurationMetadata = new LinkedHashMap<>();
|
||||||
if(metadata.getScopes() != null)
|
if (metadata.getScopes() != null)
|
||||||
{
|
{
|
||||||
configurationMetadata.put(SCOPES_SUPPORTED.getValue(), metadata.getScopes());
|
configurationMetadata.put(SCOPES_SUPPORTED.getValue(), metadata.getScopes());
|
||||||
}
|
}
|
||||||
if(StringUtils.isNotBlank(config.getAudience()))
|
if (StringUtils.isNotBlank(config.getAudience()))
|
||||||
{
|
{
|
||||||
configurationMetadata.put(AUDIENCE.getValue(), config.getAudience());
|
configurationMetadata.put(AUDIENCE.getValue(), config.getAudience());
|
||||||
}
|
}
|
||||||
@@ -497,17 +498,23 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
if (config.isPublicClient())
|
if (config.isPublicClient())
|
||||||
{
|
{
|
||||||
return builder.clientSecret(null)
|
return builder.clientSecret(null)
|
||||||
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST);
|
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST);
|
||||||
}
|
}
|
||||||
return builder.clientSecret(config.getClientSecret())
|
return builder.clientSecret(config.getClientSecret())
|
||||||
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
|
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> getSupportedScopes(Scope scopes)
|
private Set<String> getSupportedScopes(Scope scopes)
|
||||||
{
|
{
|
||||||
return scopes.stream().filter(scope -> SCOPES.contains(scope.getValue()))
|
return scopes.stream()
|
||||||
.map(Identifier::getValue)
|
.filter(this::hasPasswordGrantScope)
|
||||||
.collect(Collectors.toSet());
|
.map(Identifier::getValue)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasPasswordGrantScope(Scope.Value scope)
|
||||||
|
{
|
||||||
|
return config.getPasswordGrantScopes().contains(scope.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<OIDCProviderMetadata> extractMetadata(RestOperations rest, URI metadataUri)
|
private Optional<OIDCProviderMetadata> extractMetadata(RestOperations rest, URI metadataUri)
|
||||||
@@ -519,7 +526,7 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
if (r.getStatusCode() != HttpStatus.OK || !r.hasBody())
|
if (r.getStatusCode() != HttpStatus.OK || !r.hasBody())
|
||||||
{
|
{
|
||||||
LOGGER.warn("Unexpected response from " + metadataUri + ". Status code: " + r.getStatusCode()
|
LOGGER.warn("Unexpected response from " + metadataUri + ". Status code: " + r.getStatusCode()
|
||||||
+ ", has body: " + r.hasBody() + ".");
|
+ ", has body: " + r.hasBody() + ".");
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
response = r.getBody();
|
response = r.getBody();
|
||||||
@@ -545,19 +552,29 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
if (StringUtils.isBlank(config.getAuthServerUrl()) && StringUtils.isBlank(config.getIssuerUrl()))
|
if (StringUtils.isBlank(config.getAuthServerUrl()) && StringUtils.isBlank(config.getIssuerUrl()))
|
||||||
{
|
{
|
||||||
throw new IdentityServiceException(
|
throw new IdentityServiceException(
|
||||||
"Failed to create ClientRegistration. The values of issuer url and auth server url cannot both be empty.");
|
"Failed to create ClientRegistration. The values of issuer url and auth server url cannot both be empty.");
|
||||||
}
|
}
|
||||||
|
|
||||||
String baseUrl = StringUtils.isNotBlank(config.getAuthServerUrl()) ?
|
String baseUrl = StringUtils.isNotBlank(config.getAuthServerUrl()) ? config.getAuthServerUrl() : config.getIssuerUrl();
|
||||||
config.getAuthServerUrl() :
|
|
||||||
config.getIssuerUrl();
|
|
||||||
|
|
||||||
return List.of(UriComponentsBuilder.fromUriString(baseUrl)
|
return List.of(UriComponentsBuilder.fromUriString(baseUrl)
|
||||||
.pathSegment(".well-known", "openid-configuration")
|
.pathSegment(".well-known", "openid-configuration")
|
||||||
.build().toUri());
|
.build().toUri());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Optional<String> getMetadataIssuer(OIDCProviderMetadata metadata, IdentityServiceConfig config)
|
||||||
|
{
|
||||||
|
return DEFAULT_ISSUER_ATTR.equals(config.getIssuerAttribute()) ? Optional.of(metadata)
|
||||||
|
.map(OIDCProviderMetadata::getIssuer)
|
||||||
|
.map(Issuer::getValue)
|
||||||
|
: Optional.of(metadata)
|
||||||
|
.map(OIDCProviderMetadata::getCustomParameters)
|
||||||
|
.map(map -> map.get(config.getIssuerAttribute()))
|
||||||
|
.filter(String.class::isInstance)
|
||||||
|
.map(String.class::cast);
|
||||||
|
}
|
||||||
|
|
||||||
static class JwtDecoderProvider
|
static class JwtDecoderProvider
|
||||||
{
|
{
|
||||||
private static final SignatureAlgorithm DEFAULT_SIGNATURE_ALGORITHM = SignatureAlgorithm.RS256;
|
private static final SignatureAlgorithm DEFAULT_SIGNATURE_ALGORITHM = SignatureAlgorithm.RS256;
|
||||||
@@ -568,12 +585,12 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
{
|
{
|
||||||
this.config = requireNonNull(config);
|
this.config = requireNonNull(config);
|
||||||
this.signatureAlgorithms = ofNullable(config.getSignatureAlgorithms())
|
this.signatureAlgorithms = ofNullable(config.getSignatureAlgorithms())
|
||||||
.filter(not(Set::isEmpty))
|
.filter(not(Set::isEmpty))
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
LOGGER.warn("Unable to find any valid signature algorithms in the configuration. "
|
LOGGER.warn("Unable to find any valid signature algorithms in the configuration. "
|
||||||
+ "Using the default signature algorithm: " + DEFAULT_SIGNATURE_ALGORITHM.getName() + ".");
|
+ "Using the default signature algorithm: " + DEFAULT_SIGNATURE_ALGORITHM.getName() + ".");
|
||||||
return Set.of(DEFAULT_SIGNATURE_ALGORITHM);
|
return Set.of(DEFAULT_SIGNATURE_ALGORITHM);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public JwtDecoder createJwtDecoder(RestOperations rest, ProviderDetails providerDetails)
|
public JwtDecoder createJwtDecoder(RestOperations rest, ProviderDetails providerDetails)
|
||||||
@@ -584,7 +601,7 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
|
|
||||||
decoder.setJwtValidator(createJwtTokenValidator(providerDetails));
|
decoder.setJwtValidator(createJwtTokenValidator(providerDetails));
|
||||||
decoder.setClaimSetConverter(
|
decoder.setClaimSetConverter(
|
||||||
new ClaimTypeConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverters()));
|
new ClaimTypeConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverters()));
|
||||||
|
|
||||||
return decoder;
|
return decoder;
|
||||||
}
|
}
|
||||||
@@ -601,26 +618,26 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
{
|
{
|
||||||
final RSAPublicKey publicKey = parsePublicKey(config.getRealmKey());
|
final RSAPublicKey publicKey = parsePublicKey(config.getRealmKey());
|
||||||
return NimbusJwtDecoder.withPublicKey(publicKey)
|
return NimbusJwtDecoder.withPublicKey(publicKey)
|
||||||
.signatureAlgorithm(DEFAULT_SIGNATURE_ALGORITHM)
|
.signatureAlgorithm(DEFAULT_SIGNATURE_ALGORITHM)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
final String jwkSetUri = requireValidJwkSetUri(providerDetails);
|
final String jwkSetUri = requireValidJwkSetUri(providerDetails);
|
||||||
final NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder decoderBuilder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri);
|
final NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder decoderBuilder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri);
|
||||||
signatureAlgorithms.forEach(decoderBuilder::jwsAlgorithm);
|
signatureAlgorithms.forEach(decoderBuilder::jwsAlgorithm);
|
||||||
return decoderBuilder
|
return decoderBuilder
|
||||||
.restOperations(rest)
|
.restOperations(rest)
|
||||||
.jwtProcessorCustomizer(this::reconfigureJWKSCache)
|
.jwtProcessorCustomizer(this::reconfigureJWKSCache)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reconfigureJWKSCache(ConfigurableJWTProcessor<SecurityContext> jwtProcessor)
|
private void reconfigureJWKSCache(ConfigurableJWTProcessor<SecurityContext> jwtProcessor)
|
||||||
{
|
{
|
||||||
final Optional<RemoteJWKSet<SecurityContext>> jwkSource = ofNullable(jwtProcessor)
|
final Optional<RemoteJWKSet<SecurityContext>> jwkSource = ofNullable(jwtProcessor)
|
||||||
.map(ConfigurableJWTProcessor::getJWSKeySelector)
|
.map(ConfigurableJWTProcessor::getJWSKeySelector)
|
||||||
.filter(JWSVerificationKeySelector.class::isInstance)
|
.filter(JWSVerificationKeySelector.class::isInstance)
|
||||||
.map(o -> (JWSVerificationKeySelector<SecurityContext>) o)
|
.map(o -> (JWSVerificationKeySelector<SecurityContext>) o)
|
||||||
.map(JWSVerificationKeySelector::getJWKSource)
|
.map(JWSVerificationKeySelector::getJWKSource)
|
||||||
.filter(RemoteJWKSet.class::isInstance).map(o -> (RemoteJWKSet<SecurityContext>) o);
|
.filter(RemoteJWKSet.class::isInstance).map(o -> (RemoteJWKSet<SecurityContext>) o);
|
||||||
if (jwkSource.isEmpty())
|
if (jwkSource.isEmpty())
|
||||||
{
|
{
|
||||||
LOGGER.warn("Not able to reconfigure the JWK Cache. Unexpected JWKSource.");
|
LOGGER.warn("Not able to reconfigure the JWK Cache. Unexpected JWKSource.");
|
||||||
@@ -642,22 +659,22 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
}
|
}
|
||||||
|
|
||||||
final DefaultJWKSetCache cache = new DefaultJWKSetCache(config.getPublicKeyCacheTtl(), -1,
|
final DefaultJWKSetCache cache = new DefaultJWKSetCache(config.getPublicKeyCacheTtl(), -1,
|
||||||
TimeUnit.SECONDS);
|
TimeUnit.SECONDS);
|
||||||
final JWKSource<SecurityContext> cachingJWKSource = new RemoteJWKSet<>(jwkSetUrl.get(),
|
final JWKSource<SecurityContext> cachingJWKSource = new RemoteJWKSet<>(jwkSetUrl.get(),
|
||||||
resourceRetriever.get(), cache);
|
resourceRetriever.get(), cache);
|
||||||
|
|
||||||
jwtProcessor.setJWSKeySelector(new JWSVerificationKeySelector<>(
|
jwtProcessor.setJWSKeySelector(new JWSVerificationKeySelector<>(
|
||||||
signatureAlgorithms.stream()
|
signatureAlgorithms.stream()
|
||||||
.map(signatureAlgorithm -> JWSAlgorithm.parse(signatureAlgorithm.getName()))
|
.map(signatureAlgorithm -> JWSAlgorithm.parse(signatureAlgorithm.getName()))
|
||||||
.collect(Collectors.toSet()),
|
.collect(Collectors.toSet()),
|
||||||
cachingJWKSource));
|
cachingJWKSource));
|
||||||
jwtProcessor.setJWSTypeVerifier(new CustomJOSEObjectTypeVerifier(JOSEObjectType.JWT, AT_JWT));
|
jwtProcessor.setJWSTypeVerifier(new CustomJOSEObjectTypeVerifier(JOSEObjectType.JWT, AT_JWT));
|
||||||
}
|
}
|
||||||
|
|
||||||
private OAuth2TokenValidator<Jwt> createJwtTokenValidator(ProviderDetails providerDetails)
|
private OAuth2TokenValidator<Jwt> createJwtTokenValidator(ProviderDetails providerDetails)
|
||||||
{
|
{
|
||||||
List<OAuth2TokenValidator<Jwt>> validators = new ArrayList<>();
|
List<OAuth2TokenValidator<Jwt>> validators = new ArrayList<>();
|
||||||
validators.add(new JwtTimestampValidator(Duration.of(0, ChronoUnit.MILLIS)));
|
validators.add(new JwtTimestampValidator(Duration.of(config.getJwtClockSkewMs(), ChronoUnit.MILLIS)));
|
||||||
validators.add(new JwtIssuerValidator(providerDetails.getIssuerUri()));
|
validators.add(new JwtIssuerValidator(providerDetails.getIssuerUri()));
|
||||||
if (!config.isClientIdValidationDisabled())
|
if (!config.isClientIdValidationDisabled())
|
||||||
{
|
{
|
||||||
@@ -680,7 +697,7 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
{
|
{
|
||||||
if (isPemFormatException(e))
|
if (isPemFormatException(e))
|
||||||
{
|
{
|
||||||
//For backward compatibility with Keycloak adapter
|
// For backward compatibility with Keycloak adapter
|
||||||
return tryToParsePublicKey("-----BEGIN PUBLIC KEY-----\n" + pem + "\n-----END PUBLIC KEY-----");
|
return tryToParsePublicKey("-----BEGIN PUBLIC KEY-----\n" + pem + "\n-----END PUBLIC KEY-----");
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
@@ -704,10 +721,10 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
if (!isDefined(uri))
|
if (!isDefined(uri))
|
||||||
{
|
{
|
||||||
OAuth2Error oauth2Error = new OAuth2Error("missing_signature_verifier",
|
OAuth2Error oauth2Error = new OAuth2Error("missing_signature_verifier",
|
||||||
"Failed to find a Signature Verifier for: '"
|
"Failed to find a Signature Verifier for: '"
|
||||||
+ providerDetails.getIssuerUri()
|
+ providerDetails.getIssuerUri()
|
||||||
+ "'. Check to ensure you have configured the JwkSet URI.",
|
+ "'. Check to ensure you have configured the JwkSet URI.",
|
||||||
null);
|
null);
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
}
|
}
|
||||||
return uri;
|
return uri;
|
||||||
@@ -734,9 +751,9 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
}
|
}
|
||||||
|
|
||||||
final OAuth2Error error = new OAuth2Error(
|
final OAuth2Error error = new OAuth2Error(
|
||||||
OAuth2ErrorCodes.INVALID_TOKEN,
|
OAuth2ErrorCodes.INVALID_TOKEN,
|
||||||
"The iss claim is not valid. Expected `%s` but got `%s`.".formatted(requiredIssuer, issuer),
|
"The iss claim is not valid. Expected `%s` but got `%s`.".formatted(requiredIssuer, issuer),
|
||||||
"https://tools.ietf.org/html/rfc6750#section-3.1");
|
"https://tools.ietf.org/html/rfc6750#section-3.1");
|
||||||
return OAuth2TokenValidatorResult.failure(error);
|
return OAuth2TokenValidatorResult.failure(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -758,20 +775,20 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
final Object audience = token.getClaim(JwtClaimNames.AUD);
|
final Object audience = token.getClaim(JwtClaimNames.AUD);
|
||||||
if (audience != null)
|
if (audience != null)
|
||||||
{
|
{
|
||||||
if(audience instanceof List && ((List<String>) audience).contains(configuredAudience))
|
if (audience instanceof List && ((List<String>) audience).contains(configuredAudience))
|
||||||
{
|
{
|
||||||
return OAuth2TokenValidatorResult.success();
|
return OAuth2TokenValidatorResult.success();
|
||||||
}
|
}
|
||||||
if(audience instanceof String && audience.equals(configuredAudience))
|
if (audience instanceof String && audience.equals(configuredAudience))
|
||||||
{
|
{
|
||||||
return OAuth2TokenValidatorResult.success();
|
return OAuth2TokenValidatorResult.success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final OAuth2Error error = new OAuth2Error(
|
final OAuth2Error error = new OAuth2Error(
|
||||||
OAuth2ErrorCodes.INVALID_TOKEN,
|
OAuth2ErrorCodes.INVALID_TOKEN,
|
||||||
"The aud claim is not valid. Expected configured audience `%s` not found.".formatted(configuredAudience),
|
"The aud claim is not valid. Expected configured audience `%s` not found.".formatted(configuredAudience),
|
||||||
"https://tools.ietf.org/html/rfc6750#section-3.1");
|
"https://tools.ietf.org/html/rfc6750#section-3.1");
|
||||||
return OAuth2TokenValidatorResult.failure(error);
|
return OAuth2TokenValidatorResult.failure(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -786,13 +803,10 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
|||||||
@Override
|
@Override
|
||||||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException
|
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException
|
||||||
{
|
{
|
||||||
/*
|
/* This is to avoid the Brotli content encoding that is not well-supported by the combination of the Apache Http Client and the Spring RestTemplate */
|
||||||
* This is to avoid the Brotli content encoding that is not well-supported by the combination of
|
|
||||||
* the Apache Http Client and the Spring RestTemplate
|
|
||||||
*/
|
|
||||||
ClientHttpRequest request = super.createRequest(uri, httpMethod);
|
ClientHttpRequest request = super.createRequest(uri, httpMethod);
|
||||||
request.getHeaders()
|
request.getHeaders()
|
||||||
.add("Accept-Encoding", "gzip, deflate");
|
.add("Accept-Encoding", "gzip, deflate");
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,59 +30,38 @@ import java.io.Serializable;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import com.nimbusds.openid.connect.sdk.claims.PersonClaims;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import com.nimbusds.openid.connect.sdk.claims.UserInfo;
|
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.DecodedAccessToken;
|
import org.alfresco.repo.security.authentication.identityservice.user.AccessTokenToDecodedTokenUserMapper;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.DecodedTokenUser;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.OIDCUserInfo;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.TokenUserToOIDCUserMapper;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.UserInfoAttrMapping;
|
||||||
import org.alfresco.service.cmr.security.PersonService;
|
import org.alfresco.service.cmr.security.PersonService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class handles Just in Time user provisioning. It extracts {@link OIDCUserInfo}
|
* This class handles Just in Time user provisioning. It extracts {@link OIDCUserInfo} from the given bearer token and creates a new user if it does not exist in the repository.
|
||||||
* from {@link IdentityServiceFacade.DecodedAccessToken} or {@link UserInfo}
|
|
||||||
* and creates a new user if it does not exist in the repository.
|
|
||||||
*/
|
*/
|
||||||
public class IdentityServiceJITProvisioningHandler
|
public class IdentityServiceJITProvisioningHandler
|
||||||
{
|
{
|
||||||
private final IdentityServiceConfig identityServiceConfig;
|
|
||||||
private final IdentityServiceFacade identityServiceFacade;
|
private final IdentityServiceFacade identityServiceFacade;
|
||||||
private final PersonService personService;
|
private final PersonService personService;
|
||||||
private final TransactionService transactionService;
|
private final TransactionService transactionService;
|
||||||
|
private final IdentityServiceConfig identityServiceConfig;
|
||||||
private final BiFunction<DecodedAccessToken, String, Optional<? extends OIDCUserInfo>> mapTokenToUserInfoResponse = (token, usernameMappingClaim) -> {
|
private UserInfoAttrMapping userInfoAttrMapping;
|
||||||
Optional<String> firstName = Optional.ofNullable(token)
|
private TokenUserToOIDCUserMapper tokenUserToOIDCUserMapper;
|
||||||
.map(jwtToken -> jwtToken.getClaim(PersonClaims.GIVEN_NAME_CLAIM_NAME))
|
private AccessTokenToDecodedTokenUserMapper tokenToDecodedTokenUserMapper;
|
||||||
.filter(String.class::isInstance)
|
|
||||||
.map(String.class::cast);
|
|
||||||
Optional<String> lastName = Optional.ofNullable(token)
|
|
||||||
.map(jwtToken -> jwtToken.getClaim(PersonClaims.FAMILY_NAME_CLAIM_NAME))
|
|
||||||
.filter(String.class::isInstance)
|
|
||||||
.map(String.class::cast);
|
|
||||||
Optional<String> email = Optional.ofNullable(token)
|
|
||||||
.map(jwtToken -> jwtToken.getClaim(PersonClaims.EMAIL_CLAIM_NAME))
|
|
||||||
.filter(String.class::isInstance)
|
|
||||||
.map(String.class::cast);
|
|
||||||
|
|
||||||
return Optional.ofNullable(token.getClaim(Optional.ofNullable(usernameMappingClaim)
|
|
||||||
.filter(StringUtils::isNotBlank)
|
|
||||||
.orElse(PersonClaims.PREFERRED_USERNAME_CLAIM_NAME)))
|
|
||||||
.filter(String.class::isInstance)
|
|
||||||
.map(String.class::cast)
|
|
||||||
.map(this::normalizeUserId)
|
|
||||||
.map(username -> new OIDCUserInfo(username, firstName.orElse(""), lastName.orElse(""), email.orElse("")));
|
|
||||||
};
|
|
||||||
|
|
||||||
public IdentityServiceJITProvisioningHandler(IdentityServiceFacade identityServiceFacade,
|
public IdentityServiceJITProvisioningHandler(IdentityServiceFacade identityServiceFacade,
|
||||||
PersonService personService,
|
PersonService personService,
|
||||||
TransactionService transactionService,
|
TransactionService transactionService,
|
||||||
IdentityServiceConfig identityServiceConfig)
|
IdentityServiceConfig identityServiceConfig)
|
||||||
{
|
{
|
||||||
this.identityServiceFacade = identityServiceFacade;
|
this.identityServiceFacade = identityServiceFacade;
|
||||||
this.personService = personService;
|
this.personService = personService;
|
||||||
@@ -90,94 +69,95 @@ public class IdentityServiceJITProvisioningHandler
|
|||||||
this.identityServiceConfig = identityServiceConfig;
|
this.identityServiceConfig = identityServiceConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts {@link OIDCUserInfo} from the given bearer token and creates a new user if it does not exist in the repository. Call to the UserInfo endpoint is made only if the token does not contain a username claim or if user needs to be created and some of the {@link OIDCUserInfo} fields are empty.
|
||||||
|
*/
|
||||||
public Optional<OIDCUserInfo> extractUserInfoAndCreateUserIfNeeded(String bearerToken)
|
public Optional<OIDCUserInfo> extractUserInfoAndCreateUserIfNeeded(String bearerToken)
|
||||||
{
|
{
|
||||||
Optional<OIDCUserInfo> userInfoResponse = Optional.ofNullable(bearerToken)
|
if (userInfoAttrMapping == null)
|
||||||
.filter(Predicate.not(String::isEmpty))
|
|
||||||
.flatMap(token -> extractUserInfoResponseFromAccessToken(token)
|
|
||||||
.filter(userInfo -> StringUtils.isNotEmpty(userInfo.username()))
|
|
||||||
.or(() -> extractUserInfoResponseFromEndpoint(token)));
|
|
||||||
|
|
||||||
if (transactionService.isReadOnly() || userInfoResponse.isEmpty())
|
|
||||||
{
|
{
|
||||||
return userInfoResponse;
|
initMappers(identityServiceConfig);
|
||||||
}
|
}
|
||||||
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Optional<OIDCUserInfo>>()
|
|
||||||
|
Optional<OIDCUserInfo> oidcUserInfo = Optional.ofNullable(bearerToken)
|
||||||
|
.filter(Predicate.not(String::isEmpty))
|
||||||
|
.flatMap(token -> extractUserInfoResponseFromAccessToken(token).filter(decodedTokenUser -> StringUtils.isNotEmpty(decodedTokenUser.username()))
|
||||||
|
.or(() -> extractUserInfoResponseFromEndpoint(token, userInfoAttrMapping)))
|
||||||
|
.map(tokenUserToOIDCUserMapper::toOIDCUser);
|
||||||
|
|
||||||
|
if (transactionService.isReadOnly() || oidcUserInfo.isEmpty())
|
||||||
{
|
{
|
||||||
|
return oidcUserInfo;
|
||||||
|
}
|
||||||
|
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<>() {
|
||||||
@Override
|
@Override
|
||||||
public Optional<OIDCUserInfo> doWork() throws Exception
|
public Optional<OIDCUserInfo> doWork() throws Exception
|
||||||
{
|
{
|
||||||
return userInfoResponse.map(userInfo -> {
|
return oidcUserInfo.map(oidcUser -> {
|
||||||
if (userInfo.username() != null && personService.createMissingPeople()
|
if (userDoesNotExistsAndCanBeCreated(oidcUser))
|
||||||
&& !personService.personExists(userInfo.username()))
|
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!userInfo.allFieldsNotEmpty())
|
if (!oidcUser.allFieldsNotEmpty())
|
||||||
{
|
{
|
||||||
userInfo = extractUserInfoResponseFromEndpoint(bearerToken).orElse(userInfo);
|
oidcUser = extractUserInfoResponseFromEndpoint(bearerToken, userInfoAttrMapping)
|
||||||
|
.map(tokenUserToOIDCUserMapper::toOIDCUser)
|
||||||
|
.orElse(oidcUser);
|
||||||
}
|
}
|
||||||
Map<QName, Serializable> properties = new HashMap<>();
|
createPerson(oidcUser);
|
||||||
properties.put(ContentModel.PROP_USERNAME, userInfo.username());
|
|
||||||
properties.put(ContentModel.PROP_FIRSTNAME, userInfo.firstName());
|
|
||||||
properties.put(ContentModel.PROP_LASTNAME, userInfo.lastName());
|
|
||||||
properties.put(ContentModel.PROP_EMAIL, userInfo.email());
|
|
||||||
properties.put(ContentModel.PROP_ORGID, "");
|
|
||||||
properties.put(ContentModel.PROP_HOME_FOLDER_PROVIDER, null);
|
|
||||||
|
|
||||||
properties.put(ContentModel.PROP_SIZE_CURRENT, 0L);
|
|
||||||
properties.put(ContentModel.PROP_SIZE_QUOTA, -1L); // no quota
|
|
||||||
|
|
||||||
personService.createPerson(properties);
|
|
||||||
}
|
}
|
||||||
return userInfo;
|
return oidcUser;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}, AuthenticationUtil.getSystemUserName());
|
}, AuthenticationUtil.getSystemUserName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<OIDCUserInfo> extractUserInfoResponseFromAccessToken(String bearerToken)
|
private void initMappers(IdentityServiceConfig identityServiceConfig)
|
||||||
|
{
|
||||||
|
this.userInfoAttrMapping = initUserInfoAttrMapping(identityServiceConfig);
|
||||||
|
this.tokenUserToOIDCUserMapper = new TokenUserToOIDCUserMapper(personService);
|
||||||
|
this.tokenToDecodedTokenUserMapper = new AccessTokenToDecodedTokenUserMapper(userInfoAttrMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean userDoesNotExistsAndCanBeCreated(OIDCUserInfo userInfo)
|
||||||
|
{
|
||||||
|
return userInfo.username() != null && personService.createMissingPeople()
|
||||||
|
&& !personService.personExists(userInfo.username());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<DecodedTokenUser> extractUserInfoResponseFromAccessToken(String bearerToken)
|
||||||
{
|
{
|
||||||
return Optional.ofNullable(bearerToken)
|
return Optional.ofNullable(bearerToken)
|
||||||
.map(identityServiceFacade::decodeToken)
|
.map(identityServiceFacade::decodeToken)
|
||||||
.flatMap(decodedToken -> mapTokenToUserInfoResponse.apply(decodedToken,
|
.flatMap(tokenToDecodedTokenUserMapper::toDecodedTokenUser);
|
||||||
identityServiceConfig.getPrincipalAttribute()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<OIDCUserInfo> extractUserInfoResponseFromEndpoint(String bearerToken)
|
private Optional<DecodedTokenUser> extractUserInfoResponseFromEndpoint(String bearerToken, UserInfoAttrMapping userInfoAttrMapping)
|
||||||
{
|
{
|
||||||
return identityServiceFacade.getUserInfo(bearerToken,
|
return identityServiceFacade.getUserInfo(bearerToken, userInfoAttrMapping)
|
||||||
StringUtils.isNotBlank(identityServiceConfig.getPrincipalAttribute()) ?
|
.filter(userInfo -> userInfo.username() != null && !userInfo.username().isEmpty());
|
||||||
identityServiceConfig.getPrincipalAttribute() : PersonClaims.PREFERRED_USERNAME_CLAIM_NAME)
|
|
||||||
.filter(userInfo -> userInfo.username() != null && !userInfo.username().isEmpty())
|
|
||||||
.map(userInfo -> new OIDCUserInfo(normalizeUserId(userInfo.username()),
|
|
||||||
Optional.ofNullable(userInfo.firstName()).orElse(""),
|
|
||||||
Optional.ofNullable(userInfo.lastName()).orElse(""),
|
|
||||||
Optional.ofNullable(userInfo.email()).orElse("")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void createPerson(OIDCUserInfo userInfo)
|
||||||
* Normalizes a user id, taking into account existing user accounts and case sensitivity settings.
|
|
||||||
*
|
|
||||||
* @param userId the user id
|
|
||||||
* @return the string
|
|
||||||
*/
|
|
||||||
private String normalizeUserId(final String userId)
|
|
||||||
{
|
{
|
||||||
if (userId == null)
|
Map<QName, Serializable> properties = new HashMap<>();
|
||||||
{
|
properties.put(ContentModel.PROP_USERNAME, userInfo.username());
|
||||||
return null;
|
properties.put(ContentModel.PROP_FIRSTNAME, userInfo.firstName());
|
||||||
}
|
properties.put(ContentModel.PROP_LASTNAME, userInfo.lastName());
|
||||||
|
properties.put(ContentModel.PROP_EMAIL, userInfo.email());
|
||||||
|
properties.put(ContentModel.PROP_ORGID, "");
|
||||||
|
properties.put(ContentModel.PROP_HOME_FOLDER_PROVIDER, null);
|
||||||
|
properties.put(ContentModel.PROP_SIZE_CURRENT, 0L);
|
||||||
|
properties.put(ContentModel.PROP_SIZE_QUOTA, -1L); // no quota
|
||||||
|
|
||||||
String normalized = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<String>()
|
personService.createPerson(properties);
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public String doWork() throws Exception
|
|
||||||
{
|
|
||||||
return personService.getUserIdentifier(userId);
|
|
||||||
}
|
|
||||||
}, AuthenticationUtil.getSystemUserName());
|
|
||||||
|
|
||||||
return normalized == null ? userId : normalized;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UserInfoAttrMapping initUserInfoAttrMapping(IdentityServiceConfig identityServiceConfig)
|
||||||
|
{
|
||||||
|
return new UserInfoAttrMapping(identityServiceFacade.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName(),
|
||||||
|
identityServiceConfig.getFirstNameAttribute(),
|
||||||
|
identityServiceConfig.getLastNameAttribute(),
|
||||||
|
identityServiceConfig.getEmailAttribute());
|
||||||
|
}
|
||||||
}
|
}
|
@@ -2,7 +2,7 @@
|
|||||||
* #%L
|
* #%L
|
||||||
* Alfresco Repository
|
* Alfresco Repository
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2005 - 2023 Alfresco Software Limited
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
* %%
|
* %%
|
||||||
* This file is part of the Alfresco software.
|
* This file is part of the Alfresco software.
|
||||||
* If the software was purchased under a paid Alfresco license, the terms of
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
@@ -25,23 +25,23 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.security.authentication.identityservice;
|
package org.alfresco.repo.security.authentication.identityservice;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import java.util.Optional;
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||||
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||||
|
|
||||||
import org.alfresco.repo.management.subsystems.ActivateableBean;
|
import org.alfresco.repo.management.subsystems.ActivateableBean;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationException;
|
import org.alfresco.repo.security.authentication.AuthenticationException;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
import org.alfresco.repo.security.authentication.external.RemoteUserMapper;
|
import org.alfresco.repo.security.authentication.external.RemoteUserMapper;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.IdentityServiceFacadeException;
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.IdentityServiceFacadeException;
|
||||||
import org.apache.commons.logging.Log;
|
import org.alfresco.repo.security.authentication.identityservice.user.OIDCUserInfo;
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
|
||||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link RemoteUserMapper} implementation that detects and validates JWTs
|
* A {@link RemoteUserMapper} implementation that detects and validates JWTs issued by the Alfresco Identity Service.
|
||||||
* issued by the Alfresco Identity Service.
|
|
||||||
*
|
*
|
||||||
* @author Gavin Cornwell
|
* @author Gavin Cornwell
|
||||||
*/
|
*/
|
||||||
@@ -62,7 +62,8 @@ public class IdentityServiceRemoteUserMapper implements RemoteUserMapper, Activa
|
|||||||
/**
|
/**
|
||||||
* Sets the active flag
|
* Sets the active flag
|
||||||
*
|
*
|
||||||
* @param isEnabled true to enable the subsystem
|
* @param isEnabled
|
||||||
|
* true to enable the subsystem
|
||||||
*/
|
*/
|
||||||
public void setActive(boolean isEnabled)
|
public void setActive(boolean isEnabled)
|
||||||
{
|
{
|
||||||
@@ -72,7 +73,8 @@ public class IdentityServiceRemoteUserMapper implements RemoteUserMapper, Activa
|
|||||||
/**
|
/**
|
||||||
* Determines whether token validation failures are silent
|
* Determines whether token validation failures are silent
|
||||||
*
|
*
|
||||||
* @param silent true to silently fail, false to throw an exception
|
* @param silent
|
||||||
|
* true to silently fail, false to throw an exception
|
||||||
*/
|
*/
|
||||||
public void setValidationFailureSilent(boolean silent)
|
public void setValidationFailureSilent(boolean silent)
|
||||||
{
|
{
|
||||||
@@ -89,10 +91,9 @@ public class IdentityServiceRemoteUserMapper implements RemoteUserMapper, Activa
|
|||||||
this.jitProvisioningHandler = jitProvisioningHandler;
|
this.jitProvisioningHandler = jitProvisioningHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* (non-Javadoc)
|
||||||
* (non-Javadoc)
|
*
|
||||||
* @see org.alfresco.web.app.servlet.RemoteUserMapper#getRemoteUser(jakarta.servlet.http.HttpServletRequest)
|
* @see org.alfresco.web.app.servlet.RemoteUserMapper#getRemoteUser(jakarta.servlet.http.HttpServletRequest) */
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getRemoteUser(HttpServletRequest request)
|
public String getRemoteUser(HttpServletRequest request)
|
||||||
{
|
{
|
||||||
@@ -107,7 +108,6 @@ public class IdentityServiceRemoteUserMapper implements RemoteUserMapper, Activa
|
|||||||
{
|
{
|
||||||
String normalizedUserId = extractUserFromHeader(request);
|
String normalizedUserId = extractUserFromHeader(request);
|
||||||
|
|
||||||
|
|
||||||
if (normalizedUserId != null)
|
if (normalizedUserId != null)
|
||||||
{
|
{
|
||||||
// Normalize the user ID taking into account case sensitivity settings
|
// Normalize the user ID taking into account case sensitivity settings
|
||||||
@@ -131,10 +131,9 @@ public class IdentityServiceRemoteUserMapper implements RemoteUserMapper, Activa
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* (non-Javadoc)
|
||||||
* (non-Javadoc)
|
*
|
||||||
* @see org.alfresco.repo.management.subsystems.ActivateableBean#isActive()
|
* @see org.alfresco.repo.management.subsystems.ActivateableBean#isActive() */
|
||||||
*/
|
|
||||||
public boolean isActive()
|
public boolean isActive()
|
||||||
{
|
{
|
||||||
return this.isEnabled;
|
return this.isEnabled;
|
||||||
@@ -143,7 +142,8 @@ public class IdentityServiceRemoteUserMapper implements RemoteUserMapper, Activa
|
|||||||
/**
|
/**
|
||||||
* Extracts the user name from the JWT in the given request.
|
* Extracts the user name from the JWT in the given request.
|
||||||
*
|
*
|
||||||
* @param request The request containing the JWT
|
* @param request
|
||||||
|
* The request containing the JWT
|
||||||
* @return The username or null if it can not be determined
|
* @return The username or null if it can not be determined
|
||||||
*/
|
*/
|
||||||
private String extractUserFromHeader(HttpServletRequest request)
|
private String extractUserFromHeader(HttpServletRequest request)
|
||||||
@@ -163,8 +163,8 @@ public class IdentityServiceRemoteUserMapper implements RemoteUserMapper, Activa
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Optional<String> possibleUsername = jitProvisioningHandler
|
final Optional<String> possibleUsername = jitProvisioningHandler
|
||||||
.extractUserInfoAndCreateUserIfNeeded(bearerToken)
|
.extractUserInfoAndCreateUserIfNeeded(bearerToken)
|
||||||
.map(OIDCUserInfo::username);
|
.map(OIDCUserInfo::username);
|
||||||
|
|
||||||
if (possibleUsername.isEmpty())
|
if (possibleUsername.isEmpty())
|
||||||
{
|
{
|
||||||
|
@@ -30,21 +30,12 @@ import static java.util.Objects.requireNonNull;
|
|||||||
|
|
||||||
import static org.alfresco.repo.security.authentication.identityservice.IdentityServiceMetadataKey.AUDIENCE;
|
import static org.alfresco.repo.security.authentication.identityservice.IdentityServiceMetadataKey.AUDIENCE;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
import com.nimbusds.oauth2.sdk.ErrorObject;
|
import com.nimbusds.openid.connect.sdk.claims.PersonClaims;
|
||||||
import com.nimbusds.oauth2.sdk.ParseException;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
|
|
||||||
import com.nimbusds.openid.connect.sdk.UserInfoErrorResponse;
|
|
||||||
import com.nimbusds.openid.connect.sdk.UserInfoRequest;
|
|
||||||
import com.nimbusds.openid.connect.sdk.UserInfoResponse;
|
|
||||||
import com.nimbusds.openid.connect.sdk.UserInfoSuccessResponse;
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
@@ -59,27 +50,35 @@ import org.springframework.security.oauth2.client.endpoint.OAuth2PasswordGrantRe
|
|||||||
import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;
|
import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
|
||||||
|
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
|
||||||
|
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
|
||||||
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
|
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
|
||||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||||
import org.springframework.security.oauth2.core.OAuth2AccessToken.TokenType;
|
import org.springframework.security.oauth2.core.OAuth2AccessToken.TokenType;
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||||
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
|
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
|
||||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
|
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
|
||||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
|
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
|
||||||
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
import org.springframework.security.oauth2.jwt.Jwt;
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.client.RestOperations;
|
import org.springframework.web.client.RestOperations;
|
||||||
|
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.DecodedTokenUser;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.UserInfoAttrMapping;
|
||||||
|
|
||||||
class SpringBasedIdentityServiceFacade implements IdentityServiceFacade
|
class SpringBasedIdentityServiceFacade implements IdentityServiceFacade
|
||||||
{
|
{
|
||||||
private static final Log LOGGER = LogFactory.getLog(SpringBasedIdentityServiceFacade.class);
|
private static final Log LOGGER = LogFactory.getLog(SpringBasedIdentityServiceFacade.class);
|
||||||
private static final Instant SOME_INSIGNIFICANT_DATE_IN_THE_PAST = Instant.MIN.plusSeconds(12345);
|
private static final Instant SOME_INSIGNIFICANT_DATE_IN_THE_PAST = Instant.MIN.plusSeconds(12345);
|
||||||
private final Map<AuthorizationGrantType, OAuth2AccessTokenResponseClient> clients;
|
private final Map<AuthorizationGrantType, OAuth2AccessTokenResponseClient> clients;
|
||||||
|
private final DefaultOAuth2UserService defaultOAuth2UserService;
|
||||||
private final ClientRegistration clientRegistration;
|
private final ClientRegistration clientRegistration;
|
||||||
private final JwtDecoder jwtDecoder;
|
private final JwtDecoder jwtDecoder;
|
||||||
|
|
||||||
@@ -93,6 +92,7 @@ class SpringBasedIdentityServiceFacade implements IdentityServiceFacade
|
|||||||
AuthorizationGrantType.AUTHORIZATION_CODE, createAuthorizationCodeClient(restOperations),
|
AuthorizationGrantType.AUTHORIZATION_CODE, createAuthorizationCodeClient(restOperations),
|
||||||
AuthorizationGrantType.REFRESH_TOKEN, createRefreshTokenClient(restOperations),
|
AuthorizationGrantType.REFRESH_TOKEN, createRefreshTokenClient(restOperations),
|
||||||
AuthorizationGrantType.PASSWORD, createPasswordClient(restOperations, clientRegistration));
|
AuthorizationGrantType.PASSWORD, createPasswordClient(restOperations, clientRegistration));
|
||||||
|
this.defaultOAuth2UserService = createOAuth2UserService(restOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -121,51 +121,18 @@ class SpringBasedIdentityServiceFacade implements IdentityServiceFacade
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<OIDCUserInfo> getUserInfo(String tokenParameter, String principalAttribute)
|
public Optional<DecodedTokenUser> getUserInfo(String token, UserInfoAttrMapping userInfoAttrMapping)
|
||||||
{
|
{
|
||||||
return Optional.ofNullable(tokenParameter)
|
try
|
||||||
.filter(Predicate.not(String::isEmpty))
|
{
|
||||||
.flatMap(token -> Optional.ofNullable(clientRegistration)
|
return Optional.ofNullable(defaultOAuth2UserService.loadUser(new OAuth2UserRequest(clientRegistration, getSpringAccessToken(token))))
|
||||||
.map(ClientRegistration::getProviderDetails)
|
.flatMap(oAuth2User -> mapOAuth2UserToDecodedTokenUser(oAuth2User, userInfoAttrMapping));
|
||||||
.map(ClientRegistration.ProviderDetails::getUserInfoEndpoint)
|
}
|
||||||
.map(ClientRegistration.ProviderDetails.UserInfoEndpoint::getUri)
|
catch (OAuth2AuthenticationException exception)
|
||||||
.flatMap(uri -> {
|
{
|
||||||
try
|
LOGGER.warn("User Info Request failed: " + exception.getMessage());
|
||||||
{
|
return Optional.empty();
|
||||||
return Optional.of(
|
}
|
||||||
new UserInfoRequest(new URI(uri), new BearerAccessToken(token)).toHTTPRequest().send());
|
|
||||||
}
|
|
||||||
catch (IOException | URISyntaxException e)
|
|
||||||
{
|
|
||||||
LOGGER.warn("Failed to get user information. Reason: " + e.getMessage());
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flatMap(httpResponse -> {
|
|
||||||
try
|
|
||||||
{
|
|
||||||
UserInfoResponse userInfoResponse = UserInfoResponse.parse(httpResponse);
|
|
||||||
|
|
||||||
if (userInfoResponse instanceof UserInfoErrorResponse userInfoErrorResponse)
|
|
||||||
{
|
|
||||||
String errorMessage = Optional.ofNullable(userInfoErrorResponse.getErrorObject())
|
|
||||||
.map(ErrorObject::getDescription)
|
|
||||||
.orElse("No error description found");
|
|
||||||
LOGGER.warn("User Info Request failed: " + errorMessage);
|
|
||||||
throw new UserInfoException(errorMessage);
|
|
||||||
}
|
|
||||||
return Optional.of(userInfoResponse);
|
|
||||||
}
|
|
||||||
catch (ParseException e)
|
|
||||||
{
|
|
||||||
LOGGER.warn("Failed to parse user info response. Reason: " + e.getMessage());
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(UserInfoResponse::toSuccessResponse)
|
|
||||||
.map(UserInfoSuccessResponse::getUserInfo))
|
|
||||||
.map(userInfo -> new OIDCUserInfo(userInfo.getStringClaim(principalAttribute), userInfo.getGivenName(),
|
|
||||||
userInfo.getFamilyName(), userInfo.getEmailAddress()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -202,11 +169,7 @@ class SpringBasedIdentityServiceFacade implements IdentityServiceFacade
|
|||||||
|
|
||||||
if (grant.isRefreshToken())
|
if (grant.isRefreshToken())
|
||||||
{
|
{
|
||||||
final OAuth2AccessToken expiredAccessToken = new OAuth2AccessToken(
|
final OAuth2AccessToken expiredAccessToken = getSpringAccessToken("JUST_FOR_FULFILLING_THE_SPRING_API");
|
||||||
TokenType.BEARER,
|
|
||||||
"JUST_FOR_FULFILLING_THE_SPRING_API",
|
|
||||||
SOME_INSIGNIFICANT_DATE_IN_THE_PAST,
|
|
||||||
SOME_INSIGNIFICANT_DATE_IN_THE_PAST.plusSeconds(1));
|
|
||||||
final OAuth2RefreshToken refreshToken = new OAuth2RefreshToken(grant.getRefreshToken(), null);
|
final OAuth2RefreshToken refreshToken = new OAuth2RefreshToken(grant.getRefreshToken(), null);
|
||||||
|
|
||||||
return new OAuth2RefreshTokenGrantRequest(clientRegistration, expiredAccessToken, refreshToken,
|
return new OAuth2RefreshTokenGrantRequest(clientRegistration, expiredAccessToken, refreshToken,
|
||||||
@@ -258,6 +221,26 @@ class SpringBasedIdentityServiceFacade implements IdentityServiceFacade
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static DefaultOAuth2UserService createOAuth2UserService(RestOperations rest)
|
||||||
|
{
|
||||||
|
final DefaultOAuth2UserService userService = new DefaultOAuth2UserService();
|
||||||
|
userService.setRestOperations(rest);
|
||||||
|
return userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<DecodedTokenUser> mapOAuth2UserToDecodedTokenUser(OAuth2User oAuth2User, UserInfoAttrMapping userInfoAttrMapping)
|
||||||
|
{
|
||||||
|
var preferredUsername = Optional.ofNullable(oAuth2User.getAttribute(PersonClaims.PREFERRED_USERNAME_CLAIM_NAME))
|
||||||
|
.filter(String.class::isInstance)
|
||||||
|
.map(String.class::cast)
|
||||||
|
.filter(StringUtils::isNotEmpty);
|
||||||
|
var userName = Optional.ofNullable(oAuth2User.getName()).filter(username -> !username.isEmpty()).or(() -> preferredUsername);
|
||||||
|
return userName.map(name -> DecodedTokenUser.validateAndCreate(name,
|
||||||
|
oAuth2User.getAttribute(userInfoAttrMapping.firstNameClaim()),
|
||||||
|
oAuth2User.getAttribute(userInfoAttrMapping.lastNameClaim()),
|
||||||
|
oAuth2User.getAttribute(userInfoAttrMapping.emailClaim())));
|
||||||
|
}
|
||||||
|
|
||||||
private static OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> createPasswordClient(RestOperations rest,
|
private static OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> createPasswordClient(RestOperations rest,
|
||||||
ClientRegistration clientRegistration)
|
ClientRegistration clientRegistration)
|
||||||
{
|
{
|
||||||
@@ -288,6 +271,16 @@ class SpringBasedIdentityServiceFacade implements IdentityServiceFacade
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static OAuth2AccessToken getSpringAccessToken(String token)
|
||||||
|
{
|
||||||
|
// Just for fulfilling the Spring API
|
||||||
|
return new OAuth2AccessToken(
|
||||||
|
TokenType.BEARER,
|
||||||
|
token,
|
||||||
|
SOME_INSIGNIFICANT_DATE_IN_THE_PAST,
|
||||||
|
SOME_INSIGNIFICANT_DATE_IN_THE_PAST.plusSeconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
private static class SpringAccessTokenAuthorization implements AccessTokenAuthorization
|
private static class SpringAccessTokenAuthorization implements AccessTokenAuthorization
|
||||||
{
|
{
|
||||||
private final OAuth2AccessTokenResponse tokenResponse;
|
private final OAuth2AccessTokenResponse tokenResponse;
|
||||||
|
@@ -37,13 +37,19 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import com.nimbusds.oauth2.sdk.Scope;
|
import com.nimbusds.oauth2.sdk.Scope;
|
||||||
import com.nimbusds.oauth2.sdk.id.Identifier;
|
import com.nimbusds.oauth2.sdk.id.Identifier;
|
||||||
import com.nimbusds.oauth2.sdk.id.State;
|
import com.nimbusds.oauth2.sdk.id.State;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import org.alfresco.repo.management.subsystems.ActivateableBean;
|
import org.alfresco.repo.management.subsystems.ActivateableBean;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationException;
|
import org.alfresco.repo.security.authentication.AuthenticationException;
|
||||||
import org.alfresco.repo.security.authentication.external.AdminConsoleAuthenticator;
|
import org.alfresco.repo.security.authentication.external.AdminConsoleAuthenticator;
|
||||||
@@ -53,16 +59,9 @@ import org.alfresco.repo.security.authentication.identityservice.IdentityService
|
|||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AccessTokenAuthorization;
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AccessTokenAuthorization;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationException;
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationException;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationGrant;
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationGrant;
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
|
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link AdminConsoleAuthenticator} implementation to extract an externally authenticated user ID
|
* An {@link AdminConsoleAuthenticator} implementation to extract an externally authenticated user ID or to initiate the OIDC authorization code flow.
|
||||||
* or to initiate the OIDC authorization code flow.
|
|
||||||
*/
|
*/
|
||||||
public class IdentityServiceAdminConsoleAuthenticator implements AdminConsoleAuthenticator, ActivateableBean
|
public class IdentityServiceAdminConsoleAuthenticator implements AdminConsoleAuthenticator, ActivateableBean
|
||||||
{
|
{
|
||||||
@@ -71,7 +70,6 @@ public class IdentityServiceAdminConsoleAuthenticator implements AdminConsoleAut
|
|||||||
private static final String ALFRESCO_ACCESS_TOKEN = "ALFRESCO_ACCESS_TOKEN";
|
private static final String ALFRESCO_ACCESS_TOKEN = "ALFRESCO_ACCESS_TOKEN";
|
||||||
private static final String ALFRESCO_REFRESH_TOKEN = "ALFRESCO_REFRESH_TOKEN";
|
private static final String ALFRESCO_REFRESH_TOKEN = "ALFRESCO_REFRESH_TOKEN";
|
||||||
private static final String ALFRESCO_TOKEN_EXPIRATION = "ALFRESCO_TOKEN_EXPIRATION";
|
private static final String ALFRESCO_TOKEN_EXPIRATION = "ALFRESCO_TOKEN_EXPIRATION";
|
||||||
private static final Set<String> SCOPES = Set.of("openid", "profile", "email", "offline_access");
|
|
||||||
|
|
||||||
private IdentityServiceConfig identityServiceConfig;
|
private IdentityServiceConfig identityServiceConfig;
|
||||||
private IdentityServiceFacade identityServiceFacade;
|
private IdentityServiceFacade identityServiceFacade;
|
||||||
@@ -145,7 +143,7 @@ public class IdentityServiceAdminConsoleAuthenticator implements AdminConsoleAut
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
AccessTokenAuthorization accessTokenAuthorization = identityServiceFacade.authorize(
|
AccessTokenAuthorization accessTokenAuthorization = identityServiceFacade.authorize(
|
||||||
authorizationCode(code, request.getRequestURL().toString()));
|
authorizationCode(code, request.getRequestURL().toString()));
|
||||||
addCookies(response, accessTokenAuthorization);
|
addCookies(response, accessTokenAuthorization);
|
||||||
bearerToken = accessTokenAuthorization.getAccessToken().getTokenValue();
|
bearerToken = accessTokenAuthorization.getAccessToken().getTokenValue();
|
||||||
}
|
}
|
||||||
@@ -154,8 +152,8 @@ public class IdentityServiceAdminConsoleAuthenticator implements AdminConsoleAut
|
|||||||
if (LOGGER.isWarnEnabled())
|
if (LOGGER.isWarnEnabled())
|
||||||
{
|
{
|
||||||
LOGGER.warn(
|
LOGGER.warn(
|
||||||
"Error while trying to retrieve a response using the Authorization Code at the Token Endpoint: {}",
|
"Error while trying to retrieve a response using the Authorization Code at the Token Endpoint: {}",
|
||||||
exception.getMessage());
|
exception.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bearerToken;
|
return bearerToken;
|
||||||
@@ -188,7 +186,7 @@ public class IdentityServiceAdminConsoleAuthenticator implements AdminConsoleAut
|
|||||||
{
|
{
|
||||||
cookiesService.addCookie(ALFRESCO_ACCESS_TOKEN, accessTokenAuthorization.getAccessToken().getTokenValue(), response);
|
cookiesService.addCookie(ALFRESCO_ACCESS_TOKEN, accessTokenAuthorization.getAccessToken().getTokenValue(), response);
|
||||||
cookiesService.addCookie(ALFRESCO_TOKEN_EXPIRATION, String.valueOf(
|
cookiesService.addCookie(ALFRESCO_TOKEN_EXPIRATION, String.valueOf(
|
||||||
accessTokenAuthorization.getAccessToken().getExpiresAt().toEpochMilli()), response);
|
accessTokenAuthorization.getAccessToken().getExpiresAt().toEpochMilli()), response);
|
||||||
cookiesService.addCookie(ALFRESCO_REFRESH_TOKEN, accessTokenAuthorization.getRefreshTokenValue(), response);
|
cookiesService.addCookie(ALFRESCO_REFRESH_TOKEN, accessTokenAuthorization.getRefreshTokenValue(), response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,13 +196,13 @@ public class IdentityServiceAdminConsoleAuthenticator implements AdminConsoleAut
|
|||||||
State state = new State();
|
State state = new State();
|
||||||
|
|
||||||
UriComponentsBuilder authRequestBuilder = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getAuthorizationUri())
|
UriComponentsBuilder authRequestBuilder = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getAuthorizationUri())
|
||||||
.queryParam("client_id", clientRegistration.getClientId())
|
.queryParam("client_id", clientRegistration.getClientId())
|
||||||
.queryParam("redirect_uri", getRedirectUri(request.getRequestURL().toString()))
|
.queryParam("redirect_uri", getRedirectUri(request.getRequestURL().toString()))
|
||||||
.queryParam("response_type", "code")
|
.queryParam("response_type", "code")
|
||||||
.queryParam("scope", String.join("+", getScopes(clientRegistration)))
|
.queryParam("scope", String.join("+", getScopes(clientRegistration)))
|
||||||
.queryParam("state", state.toString());
|
.queryParam("state", state.toString());
|
||||||
|
|
||||||
if(StringUtils.isNotBlank(identityServiceConfig.getAudience()))
|
if (StringUtils.isNotBlank(identityServiceConfig.getAudience()))
|
||||||
{
|
{
|
||||||
authRequestBuilder.queryParam("audience", identityServiceConfig.getAudience());
|
authRequestBuilder.queryParam("audience", identityServiceConfig.getAudience());
|
||||||
}
|
}
|
||||||
@@ -215,20 +213,25 @@ public class IdentityServiceAdminConsoleAuthenticator implements AdminConsoleAut
|
|||||||
private Set<String> getScopes(ClientRegistration clientRegistration)
|
private Set<String> getScopes(ClientRegistration clientRegistration)
|
||||||
{
|
{
|
||||||
return Optional.ofNullable(clientRegistration.getProviderDetails())
|
return Optional.ofNullable(clientRegistration.getProviderDetails())
|
||||||
.map(ProviderDetails::getConfigurationMetadata)
|
.map(ProviderDetails::getConfigurationMetadata)
|
||||||
.map(metadata -> metadata.get(SCOPES_SUPPORTED.getValue()))
|
.map(metadata -> metadata.get(SCOPES_SUPPORTED.getValue()))
|
||||||
.filter(Scope.class::isInstance)
|
.filter(Scope.class::isInstance)
|
||||||
.map(Scope.class::cast)
|
.map(Scope.class::cast)
|
||||||
.map(this::getSupportedScopes)
|
.map(this::getSupportedScopes)
|
||||||
.orElse(clientRegistration.getScopes());
|
.orElse(clientRegistration.getScopes());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> getSupportedScopes(Scope scopes)
|
private Set<String> getSupportedScopes(Scope scopes)
|
||||||
{
|
{
|
||||||
return scopes.stream()
|
return scopes.stream()
|
||||||
.filter(scope -> SCOPES.contains(scope.getValue()))
|
.filter(this::hasAdminConsoleScope)
|
||||||
.map(Identifier::getValue)
|
.map(Identifier::getValue)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasAdminConsoleScope(Scope.Value scope)
|
||||||
|
{
|
||||||
|
return identityServiceConfig.getAdminConsoleScopes().contains(scope.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getRedirectUri(String requestURL)
|
private String getRedirectUri(String requestURL)
|
||||||
@@ -263,7 +266,7 @@ public class IdentityServiceAdminConsoleAuthenticator implements AdminConsoleAut
|
|||||||
private AccessTokenAuthorization doRefreshAuthToken(String refreshToken)
|
private AccessTokenAuthorization doRefreshAuthToken(String refreshToken)
|
||||||
{
|
{
|
||||||
AccessTokenAuthorization accessTokenAuthorization = identityServiceFacade.authorize(
|
AccessTokenAuthorization accessTokenAuthorization = identityServiceFacade.authorize(
|
||||||
AuthorizationGrant.refreshToken(refreshToken));
|
AuthorizationGrant.refreshToken(refreshToken));
|
||||||
if (accessTokenAuthorization == null || accessTokenAuthorization.getAccessToken() == null)
|
if (accessTokenAuthorization == null || accessTokenAuthorization.getAccessToken() == null)
|
||||||
{
|
{
|
||||||
throw new AuthenticationException("AccessTokenResponse is null or empty");
|
throw new AuthenticationException("AccessTokenResponse is null or empty");
|
||||||
@@ -284,7 +287,7 @@ public class IdentityServiceAdminConsoleAuthenticator implements AdminConsoleAut
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setIdentityServiceFacade(
|
public void setIdentityServiceFacade(
|
||||||
IdentityServiceFacade identityServiceFacade)
|
IdentityServiceFacade identityServiceFacade)
|
||||||
{
|
{
|
||||||
this.identityServiceFacade = identityServiceFacade;
|
this.identityServiceFacade = identityServiceFacade;
|
||||||
}
|
}
|
||||||
@@ -295,13 +298,13 @@ public class IdentityServiceAdminConsoleAuthenticator implements AdminConsoleAut
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setCookiesService(
|
public void setCookiesService(
|
||||||
AdminConsoleAuthenticationCookiesService cookiesService)
|
AdminConsoleAuthenticationCookiesService cookiesService)
|
||||||
{
|
{
|
||||||
this.cookiesService = cookiesService;
|
this.cookiesService = cookiesService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIdentityServiceConfig(
|
public void setIdentityServiceConfig(
|
||||||
IdentityServiceConfig identityServiceConfig)
|
IdentityServiceConfig identityServiceConfig)
|
||||||
{
|
{
|
||||||
this.identityServiceConfig = identityServiceConfig;
|
this.identityServiceConfig = identityServiceConfig;
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.security.authentication.identityservice.user;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import com.nimbusds.openid.connect.sdk.claims.PersonClaims;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade;
|
||||||
|
|
||||||
|
public class AccessTokenToDecodedTokenUserMapper
|
||||||
|
{
|
||||||
|
private static final String DEFAULT_USERNAME_CLAIM = PersonClaims.PREFERRED_USERNAME_CLAIM_NAME;
|
||||||
|
|
||||||
|
private final UserInfoAttrMapping userInfoAttrMapping;
|
||||||
|
|
||||||
|
public AccessTokenToDecodedTokenUserMapper(UserInfoAttrMapping userInfoAttrMapping)
|
||||||
|
{
|
||||||
|
this.userInfoAttrMapping = userInfoAttrMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the given {@link IdentityServiceFacade.DecodedAccessToken} to a {@link DecodedTokenUser}.
|
||||||
|
*
|
||||||
|
* @param token
|
||||||
|
* the token to map
|
||||||
|
* @return the mapped {@link DecodedTokenUser} or {@link Optional#empty()} if the token does not contain a username claim
|
||||||
|
*/
|
||||||
|
public Optional<DecodedTokenUser> toDecodedTokenUser(IdentityServiceFacade.DecodedAccessToken token)
|
||||||
|
{
|
||||||
|
Object firstName = token.getClaim(userInfoAttrMapping.firstNameClaim());
|
||||||
|
Object lastName = token.getClaim(userInfoAttrMapping.lastNameClaim());
|
||||||
|
Object email = token.getClaim(userInfoAttrMapping.emailClaim());
|
||||||
|
|
||||||
|
return Optional.ofNullable(token.getClaim(Optional.ofNullable(userInfoAttrMapping.usernameClaim())
|
||||||
|
.filter(StringUtils::isNotBlank)
|
||||||
|
.orElse(DEFAULT_USERNAME_CLAIM)))
|
||||||
|
.filter(String.class::isInstance)
|
||||||
|
.map(String.class::cast)
|
||||||
|
.map(username -> DecodedTokenUser.validateAndCreate(username, firstName, lastName, email));
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.security.authentication.identityservice.user;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public record DecodedTokenUser(String username, String firstName, String lastName, String email)
|
||||||
|
{
|
||||||
|
|
||||||
|
private static final String EMPTY_STRING = "";
|
||||||
|
|
||||||
|
public static DecodedTokenUser validateAndCreate(String username, Object firstName, Object lastName, Object email)
|
||||||
|
{
|
||||||
|
return new DecodedTokenUser(username, getStringVal(firstName), getStringVal(lastName), getStringVal(email));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getStringVal(Object firstName)
|
||||||
|
{
|
||||||
|
return Optional.ofNullable(firstName).filter(String.class::isInstance).map(String.class::cast).orElse(EMPTY_STRING);
|
||||||
|
}
|
||||||
|
}
|
@@ -23,7 +23,7 @@
|
|||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package org.alfresco.repo.security.authentication.identityservice;
|
package org.alfresco.repo.security.authentication.identityservice.user;
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.security.authentication.identityservice.user;
|
||||||
|
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
|
import org.alfresco.service.cmr.security.PersonService;
|
||||||
|
|
||||||
|
public class TokenUserToOIDCUserMapper
|
||||||
|
{
|
||||||
|
private final PersonService personService;
|
||||||
|
|
||||||
|
public TokenUserToOIDCUserMapper(PersonService personService)
|
||||||
|
{
|
||||||
|
this.personService = personService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a decoded token user to an OIDC user where the user id (username) is normalized.
|
||||||
|
*
|
||||||
|
* @param decodedTokenUser
|
||||||
|
* the decoded token user
|
||||||
|
* @return the OIDC user
|
||||||
|
*/
|
||||||
|
public OIDCUserInfo toOIDCUser(DecodedTokenUser decodedTokenUser)
|
||||||
|
{
|
||||||
|
return new OIDCUserInfo(usernameToUserId(decodedTokenUser.username()), decodedTokenUser.firstName(), decodedTokenUser.lastName(), decodedTokenUser.email());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes a username, taking into account existing user accounts and case sensitivity settings.
|
||||||
|
*
|
||||||
|
* @param caseSensitiveUserName
|
||||||
|
* the case-sensitive username
|
||||||
|
* @return the string
|
||||||
|
*/
|
||||||
|
private String usernameToUserId(final String caseSensitiveUserName)
|
||||||
|
{
|
||||||
|
if (caseSensitiveUserName == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String normalized = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<String>() {
|
||||||
|
@Override
|
||||||
|
public String doWork() throws Exception
|
||||||
|
{
|
||||||
|
return personService.getUserIdentifier(caseSensitiveUserName);
|
||||||
|
}
|
||||||
|
}, AuthenticationUtil.getSystemUserName());
|
||||||
|
|
||||||
|
return normalized == null ? caseSensitiveUserName : normalized;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.security.authentication.identityservice.user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The UserInfoAttrMapping record represents the mapping of claims fetched from the UserInfo endpoint to create an Alfresco user.
|
||||||
|
*
|
||||||
|
* @param usernameClaim
|
||||||
|
* the claim that represents the username
|
||||||
|
* @param firstNameClaim
|
||||||
|
* the claim that represents the first name
|
||||||
|
* @param lastNameClaim
|
||||||
|
* the claim that represents the last name
|
||||||
|
* @param emailClaim
|
||||||
|
* the claim that represents the email
|
||||||
|
*/
|
||||||
|
public record UserInfoAttrMapping(String usernameClaim, String firstNameClaim, String lastNameClaim, String emailClaim)
|
||||||
|
{}
|
@@ -82,6 +82,7 @@
|
|||||||
<property name="contentService" ref="contentService" />
|
<property name="contentService" ref="contentService" />
|
||||||
<property name="renditionService2" ref="renditionService2" />
|
<property name="renditionService2" ref="renditionService2" />
|
||||||
<property name="directAccessUrlEnabled" value="${local.transform.directAccessUrl.enabled}"/>
|
<property name="directAccessUrlEnabled" value="${local.transform.directAccessUrl.enabled}"/>
|
||||||
|
<property name="threadPoolSize" value="${local.transform.threadPoolSize}" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="synchronousTransformClient" parent="localSynchronousTransformClient" />
|
<bean id="synchronousTransformClient" parent="localSynchronousTransformClient" />
|
||||||
|
@@ -1351,6 +1351,9 @@ restApi.directAccessUrl.defaultExpiryTimeInSec=30
|
|||||||
# Controls whether direct access url URLs may be used in transforms.
|
# Controls whether direct access url URLs may be used in transforms.
|
||||||
local.transform.directAccessUrl.enabled=true
|
local.transform.directAccessUrl.enabled=true
|
||||||
|
|
||||||
|
# Controls size of thread pool used for transforms.
|
||||||
|
local.transform.threadPoolSize=8
|
||||||
|
|
||||||
# Creates additional indexes on alf_node and alf_transaction. Recommended for large repositories.
|
# Creates additional indexes on alf_node and alf_transaction. Recommended for large repositories.
|
||||||
system.new-node-transaction-indexes.ignored=true
|
system.new-node-transaction-indexes.ignored=true
|
||||||
|
|
||||||
|
@@ -149,6 +149,15 @@
|
|||||||
<property name="principalAttribute">
|
<property name="principalAttribute">
|
||||||
<value>${identity-service.principal-attribute:preferred_username}</value>
|
<value>${identity-service.principal-attribute:preferred_username}</value>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="firstNameAttribute">
|
||||||
|
<value>${identity-service.first-name-attribute:given_name}</value>
|
||||||
|
</property>
|
||||||
|
<property name="lastNameAttribute">
|
||||||
|
<value>${identity-service.last-name-attribute:family_name}</value>
|
||||||
|
</property>
|
||||||
|
<property name="emailAttribute">
|
||||||
|
<value>${identity-service.email-attribute:email}</value>
|
||||||
|
</property>
|
||||||
<property name="clientIdValidationDisabled">
|
<property name="clientIdValidationDisabled">
|
||||||
<value>${identity-service.client-id.validation.disabled:true}</value>
|
<value>${identity-service.client-id.validation.disabled:true}</value>
|
||||||
</property>
|
</property>
|
||||||
@@ -158,6 +167,18 @@
|
|||||||
<property name="signatureAlgorithms">
|
<property name="signatureAlgorithms">
|
||||||
<value>${identity-service.signature-algorithms:RS256,PS256}</value>
|
<value>${identity-service.signature-algorithms:RS256,PS256}</value>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="adminConsoleScopes">
|
||||||
|
<value>${identity-service.admin-console.scopes:openid,profile,email,offline_access}</value>
|
||||||
|
</property>
|
||||||
|
<property name="passwordGrantScopes">
|
||||||
|
<value>${identity-service.password-grant.scopes:openid,profile,email}</value>
|
||||||
|
</property>
|
||||||
|
<property name="issuerAttribute">
|
||||||
|
<value>${identity-service.issuer-attribute:issuer}</value>
|
||||||
|
</property>
|
||||||
|
<property name="jwtClockSkewMs">
|
||||||
|
<value>${identity-service.jwt-clock-skew-ms:0}</value>
|
||||||
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<!-- Enable control over mapping between request and user ID -->
|
<!-- Enable control over mapping between request and user ID -->
|
||||||
|
@@ -13,3 +13,10 @@ identity-service.credentials.secret=
|
|||||||
identity-service.public-client=true
|
identity-service.public-client=true
|
||||||
identity-service.admin-console.redirect-path=/alfresco/s/admin/admin-communitysummary
|
identity-service.admin-console.redirect-path=/alfresco/s/admin/admin-communitysummary
|
||||||
identity-service.signature-algorithms=RS256,PS256
|
identity-service.signature-algorithms=RS256,PS256
|
||||||
|
identity-service.first-name-attribute=given_name
|
||||||
|
identity-service.last-name-attribute=family_name
|
||||||
|
identity-service.email-attribute=email
|
||||||
|
identity-service.admin-console.scopes=openid,profile,email,offline_access
|
||||||
|
identity-service.password-grant.scopes=openid,profile,email
|
||||||
|
identity-service.issuer-attribute=issuer
|
||||||
|
identity-service.jwt-clock-skew-ms=0
|
||||||
|
@@ -25,6 +25,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco;
|
package org.alfresco;
|
||||||
|
|
||||||
|
import org.junit.experimental.categories.Categories;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Suite;
|
||||||
|
|
||||||
import org.alfresco.repo.security.authentication.identityservice.ClientRegistrationProviderUnitTest;
|
import org.alfresco.repo.security.authentication.identityservice.ClientRegistrationProviderUnitTest;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacadeFactoryBeanTest;
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacadeFactoryBeanTest;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceJITProvisioningHandlerUnitTest;
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceJITProvisioningHandlerUnitTest;
|
||||||
@@ -33,237 +37,236 @@ import org.alfresco.repo.security.authentication.identityservice.SpringBasedIden
|
|||||||
import org.alfresco.repo.security.authentication.identityservice.admin.AdminConsoleAuthenticationCookiesServiceUnitTest;
|
import org.alfresco.repo.security.authentication.identityservice.admin.AdminConsoleAuthenticationCookiesServiceUnitTest;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.admin.AdminConsoleHttpServletRequestWrapperUnitTest;
|
import org.alfresco.repo.security.authentication.identityservice.admin.AdminConsoleHttpServletRequestWrapperUnitTest;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.admin.IdentityServiceAdminConsoleAuthenticatorUnitTest;
|
import org.alfresco.repo.security.authentication.identityservice.admin.IdentityServiceAdminConsoleAuthenticatorUnitTest;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.AccessTokenToDecodedTokenUserMapperUnitTest;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.TokenUserToOIDCUserMapperUnitTest;
|
||||||
import org.alfresco.util.testing.category.DBTests;
|
import org.alfresco.util.testing.category.DBTests;
|
||||||
import org.alfresco.util.testing.category.NonBuildTests;
|
import org.alfresco.util.testing.category.NonBuildTests;
|
||||||
import org.junit.experimental.categories.Categories;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.Suite;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All Repository project UNIT test classes (no application context) should be added to this test suite.
|
* All Repository project UNIT test classes (no application context) should be added to this test suite. Tests marked as DBTests are automatically excluded and are run as part of {@link AllDBTestsTestSuite}.
|
||||||
* Tests marked as DBTests are automatically excluded and are run as part of {@link AllDBTestsTestSuite}.
|
|
||||||
*/
|
*/
|
||||||
@RunWith(Categories.class)
|
@RunWith(Categories.class)
|
||||||
@Categories.ExcludeCategory({DBTests.class, NonBuildTests.class})
|
@Categories.ExcludeCategory({DBTests.class, NonBuildTests.class})
|
||||||
@Suite.SuiteClasses({
|
@Suite.SuiteClasses(value = {
|
||||||
org.alfresco.repo.site.SiteMembershipTest.class,
|
org.alfresco.repo.site.SiteMembershipTest.class,
|
||||||
org.alfresco.encryption.EncryptorTest.class,
|
org.alfresco.encryption.EncryptorTest.class,
|
||||||
org.alfresco.encryption.KeyStoreKeyProviderTest.class,
|
org.alfresco.encryption.KeyStoreKeyProviderTest.class,
|
||||||
org.alfresco.filesys.config.ServerConfigurationBeanTest.class,
|
org.alfresco.filesys.config.ServerConfigurationBeanTest.class,
|
||||||
org.alfresco.filesys.repo.rules.ShuffleTest.class,
|
org.alfresco.filesys.repo.rules.ShuffleTest.class,
|
||||||
org.alfresco.opencmis.AlfrescoCmisExceptionInterceptorTest.class,
|
org.alfresco.opencmis.AlfrescoCmisExceptionInterceptorTest.class,
|
||||||
org.alfresco.repo.admin.Log4JHierarchyInitTest.class,
|
org.alfresco.repo.admin.Log4JHierarchyInitTest.class,
|
||||||
org.alfresco.repo.attributes.PropTablesCleanupJobTest.class,
|
org.alfresco.repo.attributes.PropTablesCleanupJobTest.class,
|
||||||
org.alfresco.repo.cache.AbstractCacheFactoryTest.class,
|
org.alfresco.repo.cache.AbstractCacheFactoryTest.class,
|
||||||
org.alfresco.repo.cache.DefaultCacheFactoryTest.class,
|
org.alfresco.repo.cache.DefaultCacheFactoryTest.class,
|
||||||
org.alfresco.repo.cache.DefaultSimpleCacheTest.class,
|
org.alfresco.repo.cache.DefaultSimpleCacheTest.class,
|
||||||
org.alfresco.repo.cache.InMemoryCacheStatisticsTest.class,
|
org.alfresco.repo.cache.InMemoryCacheStatisticsTest.class,
|
||||||
org.alfresco.repo.cache.TransactionStatsTest.class,
|
org.alfresco.repo.cache.TransactionStatsTest.class,
|
||||||
org.alfresco.repo.cache.lookup.EntityLookupCacheTest.class,
|
org.alfresco.repo.cache.lookup.EntityLookupCacheTest.class,
|
||||||
org.alfresco.repo.calendar.CalendarHelpersTest.class,
|
org.alfresco.repo.calendar.CalendarHelpersTest.class,
|
||||||
org.alfresco.repo.copy.CopyServiceImplUnitTest.class,
|
org.alfresco.repo.copy.CopyServiceImplUnitTest.class,
|
||||||
org.alfresco.repo.dictionary.RepoDictionaryDAOTest.class,
|
org.alfresco.repo.dictionary.RepoDictionaryDAOTest.class,
|
||||||
org.alfresco.repo.forms.processor.node.FieldProcessorTest.class,
|
org.alfresco.repo.forms.processor.node.FieldProcessorTest.class,
|
||||||
org.alfresco.repo.forms.processor.workflow.TaskFormProcessorTest.class,
|
org.alfresco.repo.forms.processor.workflow.TaskFormProcessorTest.class,
|
||||||
org.alfresco.repo.forms.processor.workflow.WorkflowFormProcessorTest.class,
|
org.alfresco.repo.forms.processor.workflow.WorkflowFormProcessorTest.class,
|
||||||
org.alfresco.repo.invitation.site.InviteSenderTest.class,
|
org.alfresco.repo.invitation.site.InviteSenderTest.class,
|
||||||
org.alfresco.repo.invitation.site.InviteModeratedSenderTest.class,
|
org.alfresco.repo.invitation.site.InviteModeratedSenderTest.class,
|
||||||
org.alfresco.repo.jscript.ScriptSearchTest.class,
|
org.alfresco.repo.jscript.ScriptSearchTest.class,
|
||||||
org.alfresco.repo.lock.LockUtilsTest.class,
|
org.alfresco.repo.lock.LockUtilsTest.class,
|
||||||
org.alfresco.repo.lock.mem.LockStoreImplTest.class,
|
org.alfresco.repo.lock.mem.LockStoreImplTest.class,
|
||||||
org.alfresco.repo.management.CheckRequiredClassesForLoggingConsoleUnitTest.class,
|
org.alfresco.repo.management.CheckRequiredClassesForLoggingConsoleUnitTest.class,
|
||||||
org.alfresco.repo.management.subsystems.CryptodocSwitchableApplicationContextFactoryTest.class,
|
org.alfresco.repo.management.subsystems.CryptodocSwitchableApplicationContextFactoryTest.class,
|
||||||
org.alfresco.repo.module.ModuleDetailsImplTest.class,
|
org.alfresco.repo.module.ModuleDetailsImplTest.class,
|
||||||
org.alfresco.repo.module.ModuleVersionNumberTest.class,
|
org.alfresco.repo.module.ModuleVersionNumberTest.class,
|
||||||
org.alfresco.repo.module.DeprecatedModulesValidatorTest.class,
|
org.alfresco.repo.module.DeprecatedModulesValidatorTest.class,
|
||||||
org.alfresco.repo.node.integrity.IntegrityEventTest.class,
|
org.alfresco.repo.node.integrity.IntegrityEventTest.class,
|
||||||
org.alfresco.repo.policy.MTPolicyComponentTest.class,
|
org.alfresco.repo.policy.MTPolicyComponentTest.class,
|
||||||
org.alfresco.repo.policy.PolicyComponentTest.class,
|
org.alfresco.repo.policy.PolicyComponentTest.class,
|
||||||
org.alfresco.repo.rendition.RenditionNodeManagerTest.class,
|
org.alfresco.repo.rendition.RenditionNodeManagerTest.class,
|
||||||
org.alfresco.repo.rendition.RenditionServiceImplTest.class,
|
org.alfresco.repo.rendition.RenditionServiceImplTest.class,
|
||||||
org.alfresco.repo.replication.ReplicationServiceImplTest.class,
|
org.alfresco.repo.replication.ReplicationServiceImplTest.class,
|
||||||
org.alfresco.repo.rule.RuleServiceImplUnitTest.class,
|
org.alfresco.repo.rule.RuleServiceImplUnitTest.class,
|
||||||
org.alfresco.repo.service.StoreRedirectorProxyFactoryTest.class,
|
org.alfresco.repo.service.StoreRedirectorProxyFactoryTest.class,
|
||||||
org.alfresco.repo.site.RoleComparatorImplTest.class,
|
org.alfresco.repo.site.RoleComparatorImplTest.class,
|
||||||
org.alfresco.repo.template.UnsafeMethodsTest.class,
|
org.alfresco.repo.template.UnsafeMethodsTest.class,
|
||||||
org.alfresco.repo.tenant.MultiTAdminServiceImplTest.class,
|
org.alfresco.repo.tenant.MultiTAdminServiceImplTest.class,
|
||||||
org.alfresco.repo.thumbnail.ThumbnailServiceImplParameterTest.class,
|
org.alfresco.repo.thumbnail.ThumbnailServiceImplParameterTest.class,
|
||||||
org.alfresco.repo.transfer.ContentChunkerImplTest.class,
|
org.alfresco.repo.transfer.ContentChunkerImplTest.class,
|
||||||
org.alfresco.repo.transfer.HttpClientTransmitterImplTest.class,
|
org.alfresco.repo.transfer.HttpClientTransmitterImplTest.class,
|
||||||
org.alfresco.repo.transfer.manifest.TransferManifestTest.class,
|
org.alfresco.repo.transfer.manifest.TransferManifestTest.class,
|
||||||
org.alfresco.repo.transfer.TransferVersionCheckerImplTest.class,
|
org.alfresco.repo.transfer.TransferVersionCheckerImplTest.class,
|
||||||
org.alfresco.service.cmr.calendar.CalendarRecurrenceHelperTest.class,
|
org.alfresco.service.cmr.calendar.CalendarRecurrenceHelperTest.class,
|
||||||
org.alfresco.service.cmr.calendar.CalendarTimezoneHelperTest.class,
|
org.alfresco.service.cmr.calendar.CalendarTimezoneHelperTest.class,
|
||||||
org.alfresco.tools.RenameUserTest.class,
|
org.alfresco.tools.RenameUserTest.class,
|
||||||
org.alfresco.util.VersionNumberTest.class,
|
org.alfresco.util.VersionNumberTest.class,
|
||||||
org.alfresco.util.FileNameValidatorTest.class,
|
org.alfresco.util.FileNameValidatorTest.class,
|
||||||
org.alfresco.util.HttpClientHelperTest.class,
|
org.alfresco.util.HttpClientHelperTest.class,
|
||||||
org.alfresco.util.JSONtoFmModelTest.class,
|
org.alfresco.util.JSONtoFmModelTest.class,
|
||||||
org.alfresco.util.ModelUtilTest.class,
|
org.alfresco.util.ModelUtilTest.class,
|
||||||
org.alfresco.util.PropertyMapTest.class,
|
org.alfresco.util.PropertyMapTest.class,
|
||||||
org.alfresco.util.ValueProtectingMapTest.class,
|
org.alfresco.util.ValueProtectingMapTest.class,
|
||||||
org.alfresco.util.json.ExceptionJsonSerializerTest.class,
|
org.alfresco.util.json.ExceptionJsonSerializerTest.class,
|
||||||
org.alfresco.util.collections.CollectionUtilsTest.class,
|
org.alfresco.util.collections.CollectionUtilsTest.class,
|
||||||
org.alfresco.util.schemacomp.DbObjectXMLTransformerTest.class,
|
org.alfresco.util.schemacomp.DbObjectXMLTransformerTest.class,
|
||||||
org.alfresco.util.schemacomp.DbPropertyTest.class,
|
org.alfresco.util.schemacomp.DbPropertyTest.class,
|
||||||
org.alfresco.util.schemacomp.DefaultComparisonUtilsTest.class,
|
org.alfresco.util.schemacomp.DefaultComparisonUtilsTest.class,
|
||||||
org.alfresco.util.schemacomp.DifferenceTest.class,
|
org.alfresco.util.schemacomp.DifferenceTest.class,
|
||||||
org.alfresco.util.schemacomp.MultiFileDumperTest.class,
|
org.alfresco.util.schemacomp.MultiFileDumperTest.class,
|
||||||
org.alfresco.util.schemacomp.RedundantDbObjectTest.class,
|
org.alfresco.util.schemacomp.RedundantDbObjectTest.class,
|
||||||
org.alfresco.util.schemacomp.SchemaComparatorTest.class,
|
org.alfresco.util.schemacomp.SchemaComparatorTest.class,
|
||||||
org.alfresco.util.schemacomp.SchemaToXMLTest.class,
|
org.alfresco.util.schemacomp.SchemaToXMLTest.class,
|
||||||
org.alfresco.util.schemacomp.ValidatingVisitorTest.class,
|
org.alfresco.util.schemacomp.ValidatingVisitorTest.class,
|
||||||
org.alfresco.util.schemacomp.ValidationResultTest.class,
|
org.alfresco.util.schemacomp.ValidationResultTest.class,
|
||||||
org.alfresco.util.schemacomp.XMLToSchemaTest.class,
|
org.alfresco.util.schemacomp.XMLToSchemaTest.class,
|
||||||
org.alfresco.util.schemacomp.model.ColumnTest.class,
|
org.alfresco.util.schemacomp.model.ColumnTest.class,
|
||||||
org.alfresco.util.schemacomp.model.ForeignKeyTest.class,
|
org.alfresco.util.schemacomp.model.ForeignKeyTest.class,
|
||||||
org.alfresco.util.schemacomp.model.IndexTest.class,
|
org.alfresco.util.schemacomp.model.IndexTest.class,
|
||||||
org.alfresco.util.schemacomp.model.PrimaryKeyTest.class,
|
org.alfresco.util.schemacomp.model.PrimaryKeyTest.class,
|
||||||
org.alfresco.util.schemacomp.model.SchemaTest.class,
|
org.alfresco.util.schemacomp.model.SchemaTest.class,
|
||||||
org.alfresco.util.schemacomp.model.SequenceTest.class,
|
org.alfresco.util.schemacomp.model.SequenceTest.class,
|
||||||
org.alfresco.util.schemacomp.model.TableTest.class,
|
org.alfresco.util.schemacomp.model.TableTest.class,
|
||||||
org.alfresco.util.schemacomp.validator.IndexColumnsValidatorTest.class,
|
org.alfresco.util.schemacomp.validator.IndexColumnsValidatorTest.class,
|
||||||
org.alfresco.util.schemacomp.validator.NameValidatorTest.class,
|
org.alfresco.util.schemacomp.validator.NameValidatorTest.class,
|
||||||
org.alfresco.util.schemacomp.validator.SchemaVersionValidatorTest.class,
|
org.alfresco.util.schemacomp.validator.SchemaVersionValidatorTest.class,
|
||||||
org.alfresco.util.schemacomp.validator.TypeNameOnlyValidatorTest.class,
|
org.alfresco.util.schemacomp.validator.TypeNameOnlyValidatorTest.class,
|
||||||
org.alfresco.util.test.OmittedTestClassFinderUnitTest.class,
|
org.alfresco.util.test.OmittedTestClassFinderUnitTest.class,
|
||||||
org.alfresco.util.test.junitrules.RetryAtMostRuleTest.class,
|
org.alfresco.util.test.junitrules.RetryAtMostRuleTest.class,
|
||||||
org.alfresco.util.test.junitrules.TemporaryMockOverrideTest.class,
|
org.alfresco.util.test.junitrules.TemporaryMockOverrideTest.class,
|
||||||
org.alfresco.repo.search.impl.solr.AbstractSolrQueryHTTPClientTest.class,
|
org.alfresco.repo.search.impl.solr.AbstractSolrQueryHTTPClientTest.class,
|
||||||
org.alfresco.repo.search.impl.solr.SpellCheckDecisionManagerTest.class,
|
org.alfresco.repo.search.impl.solr.SpellCheckDecisionManagerTest.class,
|
||||||
org.alfresco.repo.search.impl.solr.SolrStoreMappingWrapperTest.class,
|
org.alfresco.repo.search.impl.solr.SolrStoreMappingWrapperTest.class,
|
||||||
org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryEngineTest.class,
|
org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryEngineTest.class,
|
||||||
org.alfresco.repo.search.impl.querymodel.impl.db.NodePermissionAssessorLimitsTest.class,
|
org.alfresco.repo.search.impl.querymodel.impl.db.NodePermissionAssessorLimitsTest.class,
|
||||||
org.alfresco.repo.search.impl.querymodel.impl.db.NodePermissionAssessorPermissionsTest.class,
|
org.alfresco.repo.search.impl.querymodel.impl.db.NodePermissionAssessorPermissionsTest.class,
|
||||||
org.alfresco.repo.search.impl.solr.DbOrIndexSwitchingQueryLanguageTest.class,
|
org.alfresco.repo.search.impl.solr.DbOrIndexSwitchingQueryLanguageTest.class,
|
||||||
org.alfresco.repo.search.impl.solr.SolrQueryHTTPClientTest.class,
|
org.alfresco.repo.search.impl.solr.SolrQueryHTTPClientTest.class,
|
||||||
org.alfresco.repo.search.impl.solr.SolrSQLHttpClientTest.class,
|
org.alfresco.repo.search.impl.solr.SolrSQLHttpClientTest.class,
|
||||||
org.alfresco.repo.search.impl.solr.SolrStatsResultTest.class,
|
org.alfresco.repo.search.impl.solr.SolrStatsResultTest.class,
|
||||||
org.alfresco.repo.search.impl.solr.SolrJSONResultTest.class,
|
org.alfresco.repo.search.impl.solr.SolrJSONResultTest.class,
|
||||||
org.alfresco.repo.search.impl.solr.SolrSQLJSONResultMetadataSetTest.class,
|
org.alfresco.repo.search.impl.solr.SolrSQLJSONResultMetadataSetTest.class,
|
||||||
org.alfresco.repo.search.impl.solr.facet.SolrFacetComparatorTest.class,
|
org.alfresco.repo.search.impl.solr.facet.SolrFacetComparatorTest.class,
|
||||||
org.alfresco.repo.search.impl.solr.facet.FacetQNameUtilsTest.class,
|
org.alfresco.repo.search.impl.solr.facet.FacetQNameUtilsTest.class,
|
||||||
org.alfresco.util.BeanExtenderUnitTest.class,
|
org.alfresco.util.BeanExtenderUnitTest.class,
|
||||||
org.alfresco.repo.solr.SOLRTrackingComponentUnitTest.class,
|
org.alfresco.repo.solr.SOLRTrackingComponentUnitTest.class,
|
||||||
IdentityServiceFacadeFactoryBeanTest.class,
|
IdentityServiceFacadeFactoryBeanTest.class,
|
||||||
LazyInstantiatingIdentityServiceFacadeUnitTest.class,
|
LazyInstantiatingIdentityServiceFacadeUnitTest.class,
|
||||||
SpringBasedIdentityServiceFacadeUnitTest.class,
|
SpringBasedIdentityServiceFacadeUnitTest.class,
|
||||||
IdentityServiceJITProvisioningHandlerUnitTest.class,
|
IdentityServiceJITProvisioningHandlerUnitTest.class,
|
||||||
AdminConsoleAuthenticationCookiesServiceUnitTest.class,
|
AccessTokenToDecodedTokenUserMapperUnitTest.class,
|
||||||
AdminConsoleHttpServletRequestWrapperUnitTest.class,
|
TokenUserToOIDCUserMapperUnitTest.class,
|
||||||
IdentityServiceAdminConsoleAuthenticatorUnitTest.class,
|
AdminConsoleAuthenticationCookiesServiceUnitTest.class,
|
||||||
ClientRegistrationProviderUnitTest.class,
|
AdminConsoleHttpServletRequestWrapperUnitTest.class,
|
||||||
org.alfresco.repo.security.authentication.CompositePasswordEncoderTest.class,
|
IdentityServiceAdminConsoleAuthenticatorUnitTest.class,
|
||||||
org.alfresco.repo.security.authentication.PasswordHashingTest.class,
|
ClientRegistrationProviderUnitTest.class,
|
||||||
org.alfresco.repo.security.authority.script.ScriptAuthorityService_RegExTest.class,
|
org.alfresco.repo.security.authentication.CompositePasswordEncoderTest.class,
|
||||||
org.alfresco.repo.security.permissions.PermissionCheckCollectionTest.class,
|
org.alfresco.repo.security.authentication.PasswordHashingTest.class,
|
||||||
org.alfresco.repo.security.sync.LDAPUserRegistryTest.class,
|
org.alfresco.repo.security.authority.script.ScriptAuthorityService_RegExTest.class,
|
||||||
org.alfresco.traitextender.TraitExtenderIntegrationTest.class,
|
org.alfresco.repo.security.permissions.PermissionCheckCollectionTest.class,
|
||||||
org.alfresco.traitextender.AJExtensionsCompileTest.class,
|
org.alfresco.repo.security.sync.LDAPUserRegistryTest.class,
|
||||||
|
org.alfresco.traitextender.TraitExtenderIntegrationTest.class,
|
||||||
|
org.alfresco.traitextender.AJExtensionsCompileTest.class,
|
||||||
|
|
||||||
org.alfresco.repo.virtual.page.PageCollatorTest.class,
|
org.alfresco.repo.virtual.page.PageCollatorTest.class,
|
||||||
org.alfresco.repo.virtual.ref.GetChildByIdMethodTest.class,
|
org.alfresco.repo.virtual.ref.GetChildByIdMethodTest.class,
|
||||||
org.alfresco.repo.virtual.ref.GetParentReferenceMethodTest.class,
|
org.alfresco.repo.virtual.ref.GetParentReferenceMethodTest.class,
|
||||||
org.alfresco.repo.virtual.ref.NewVirtualReferenceMethodTest.class,
|
org.alfresco.repo.virtual.ref.NewVirtualReferenceMethodTest.class,
|
||||||
org.alfresco.repo.virtual.ref.PlainReferenceParserTest.class,
|
org.alfresco.repo.virtual.ref.PlainReferenceParserTest.class,
|
||||||
org.alfresco.repo.virtual.ref.PlainStringifierTest.class,
|
org.alfresco.repo.virtual.ref.PlainStringifierTest.class,
|
||||||
org.alfresco.repo.virtual.ref.ProtocolTest.class,
|
org.alfresco.repo.virtual.ref.ProtocolTest.class,
|
||||||
org.alfresco.repo.virtual.ref.ReferenceTest.class,
|
org.alfresco.repo.virtual.ref.ReferenceTest.class,
|
||||||
org.alfresco.repo.virtual.ref.ResourceParameterTest.class,
|
org.alfresco.repo.virtual.ref.ResourceParameterTest.class,
|
||||||
org.alfresco.repo.virtual.ref.StringParameterTest.class,
|
org.alfresco.repo.virtual.ref.StringParameterTest.class,
|
||||||
org.alfresco.repo.virtual.ref.VirtualProtocolTest.class,
|
org.alfresco.repo.virtual.ref.VirtualProtocolTest.class,
|
||||||
org.alfresco.repo.virtual.store.ReferenceComparatorTest.class,
|
org.alfresco.repo.virtual.store.ReferenceComparatorTest.class,
|
||||||
|
|
||||||
org.alfresco.repo.virtual.ref.ZeroReferenceParserTest.class,
|
org.alfresco.repo.virtual.ref.ZeroReferenceParserTest.class,
|
||||||
org.alfresco.repo.virtual.ref.ZeroStringifierTest.class,
|
org.alfresco.repo.virtual.ref.ZeroStringifierTest.class,
|
||||||
|
|
||||||
org.alfresco.repo.virtual.ref.HashStringifierTest.class,
|
org.alfresco.repo.virtual.ref.HashStringifierTest.class,
|
||||||
org.alfresco.repo.virtual.ref.NodeRefRadixHasherTest.class,
|
org.alfresco.repo.virtual.ref.NodeRefRadixHasherTest.class,
|
||||||
org.alfresco.repo.virtual.ref.NumericPathHasherTest.class,
|
org.alfresco.repo.virtual.ref.NumericPathHasherTest.class,
|
||||||
org.alfresco.repo.virtual.ref.StoredPathHasherTest.class,
|
org.alfresco.repo.virtual.ref.StoredPathHasherTest.class,
|
||||||
|
|
||||||
org.alfresco.repo.virtual.template.VirtualQueryImplTest.class,
|
org.alfresco.repo.virtual.template.VirtualQueryImplTest.class,
|
||||||
org.alfresco.repo.virtual.store.TypeVirtualizationMethodUnitTest.class,
|
org.alfresco.repo.virtual.store.TypeVirtualizationMethodUnitTest.class,
|
||||||
|
|
||||||
org.alfresco.repo.security.authentication.AuthenticationServiceImplTest.class,
|
org.alfresco.repo.security.authentication.AuthenticationServiceImplTest.class,
|
||||||
org.alfresco.util.EmailHelperTest.class,
|
org.alfresco.util.EmailHelperTest.class,
|
||||||
org.alfresco.repo.action.ParameterDefinitionImplTest.class,
|
org.alfresco.repo.action.ParameterDefinitionImplTest.class,
|
||||||
org.alfresco.repo.action.ActionDefinitionImplTest.class,
|
org.alfresco.repo.action.ActionDefinitionImplTest.class,
|
||||||
org.alfresco.repo.action.ActionConditionDefinitionImplTest.class,
|
org.alfresco.repo.action.ActionConditionDefinitionImplTest.class,
|
||||||
org.alfresco.repo.action.ActionImplTest.class,
|
org.alfresco.repo.action.ActionImplTest.class,
|
||||||
org.alfresco.repo.action.ActionConditionImplTest.class,
|
org.alfresco.repo.action.ActionConditionImplTest.class,
|
||||||
org.alfresco.repo.action.CompositeActionImplTest.class,
|
org.alfresco.repo.action.CompositeActionImplTest.class,
|
||||||
org.alfresco.repo.action.CompositeActionConditionImplTest.class,
|
org.alfresco.repo.action.CompositeActionConditionImplTest.class,
|
||||||
org.alfresco.repo.action.executer.TransformActionExecuterTest.class,
|
org.alfresco.repo.action.executer.TransformActionExecuterTest.class,
|
||||||
org.alfresco.repo.action.executer.ImporterActionExecutorUnitTest.class,
|
org.alfresco.repo.action.executer.ImporterActionExecutorUnitTest.class,
|
||||||
org.alfresco.repo.audit.AuditableAnnotationTest.class,
|
org.alfresco.repo.audit.AuditableAnnotationTest.class,
|
||||||
org.alfresco.repo.audit.PropertyAuditFilterTest.class,
|
org.alfresco.repo.audit.PropertyAuditFilterTest.class,
|
||||||
org.alfresco.repo.audit.access.NodeChangeTest.class,
|
org.alfresco.repo.audit.access.NodeChangeTest.class,
|
||||||
org.alfresco.repo.content.ContentServiceImplUnitTest.class,
|
org.alfresco.repo.content.ContentServiceImplUnitTest.class,
|
||||||
org.alfresco.repo.content.directurl.SystemWideDirectUrlConfigUnitTest.class,
|
org.alfresco.repo.content.directurl.SystemWideDirectUrlConfigUnitTest.class,
|
||||||
org.alfresco.repo.content.directurl.ContentStoreDirectUrlConfigUnitTest.class,
|
org.alfresco.repo.content.directurl.ContentStoreDirectUrlConfigUnitTest.class,
|
||||||
org.alfresco.repo.content.LimitedStreamCopierTest.class,
|
org.alfresco.repo.content.LimitedStreamCopierTest.class,
|
||||||
org.alfresco.repo.content.filestore.FileIOTest.class,
|
org.alfresco.repo.content.filestore.FileIOTest.class,
|
||||||
org.alfresco.repo.content.filestore.SpoofedTextContentReaderTest.class,
|
org.alfresco.repo.content.filestore.SpoofedTextContentReaderTest.class,
|
||||||
org.alfresco.repo.content.ContentDataTest.class,
|
org.alfresco.repo.content.ContentDataTest.class,
|
||||||
org.alfresco.repo.content.replication.AggregatingContentStoreUnitTest.class,
|
org.alfresco.repo.content.replication.AggregatingContentStoreUnitTest.class,
|
||||||
org.alfresco.service.cmr.repository.TransformationOptionLimitsTest.class,
|
org.alfresco.service.cmr.repository.TransformationOptionLimitsTest.class,
|
||||||
org.alfresco.service.cmr.repository.TransformationOptionPairTest.class,
|
org.alfresco.service.cmr.repository.TransformationOptionPairTest.class,
|
||||||
org.alfresco.repo.content.transform.TransformerConfigTestSuite.class,
|
org.alfresco.repo.content.transform.TransformerConfigTestSuite.class,
|
||||||
org.alfresco.repo.content.transform.TransformerDebugTest.class,
|
org.alfresco.repo.content.transform.TransformerDebugTest.class,
|
||||||
org.alfresco.service.cmr.repository.TemporalSourceOptionsTest.class,
|
org.alfresco.service.cmr.repository.TemporalSourceOptionsTest.class,
|
||||||
org.alfresco.repo.content.metadata.MetadataExtracterLimitsTest.class,
|
org.alfresco.repo.content.metadata.MetadataExtracterLimitsTest.class,
|
||||||
org.alfresco.repo.content.caching.quota.StandardQuotaStrategyMockTest.class,
|
org.alfresco.repo.content.caching.quota.StandardQuotaStrategyMockTest.class,
|
||||||
org.alfresco.repo.content.caching.quota.UnlimitedQuotaStrategyTest.class,
|
org.alfresco.repo.content.caching.quota.UnlimitedQuotaStrategyTest.class,
|
||||||
org.alfresco.repo.content.caching.CachingContentStoreTest.class,
|
org.alfresco.repo.content.caching.CachingContentStoreTest.class,
|
||||||
org.alfresco.repo.content.caching.ContentCacheImplTest.class,
|
org.alfresco.repo.content.caching.ContentCacheImplTest.class,
|
||||||
org.alfresco.repo.domain.permissions.FixedAclUpdaterUnitTest.class,
|
org.alfresco.repo.domain.permissions.FixedAclUpdaterUnitTest.class,
|
||||||
org.alfresco.repo.domain.propval.PropertyTypeConverterTest.class,
|
org.alfresco.repo.domain.propval.PropertyTypeConverterTest.class,
|
||||||
org.alfresco.repo.domain.schema.script.ScriptBundleExecutorImplTest.class,
|
org.alfresco.repo.domain.schema.script.ScriptBundleExecutorImplTest.class,
|
||||||
org.alfresco.repo.search.MLAnaysisModeExpansionTest.class,
|
org.alfresco.repo.search.MLAnaysisModeExpansionTest.class,
|
||||||
org.alfresco.repo.search.DocumentNavigatorTest.class,
|
org.alfresco.repo.search.DocumentNavigatorTest.class,
|
||||||
org.alfresco.util.NumericEncodingTest.class,
|
org.alfresco.util.NumericEncodingTest.class,
|
||||||
org.alfresco.repo.search.impl.parsers.CMIS_FTSTest.class,
|
org.alfresco.repo.search.impl.parsers.CMIS_FTSTest.class,
|
||||||
org.alfresco.repo.search.impl.parsers.CMISTest.class,
|
org.alfresco.repo.search.impl.parsers.CMISTest.class,
|
||||||
org.alfresco.repo.search.impl.parsers.FTSTest.class,
|
org.alfresco.repo.search.impl.parsers.FTSTest.class,
|
||||||
org.alfresco.repo.security.authentication.AlfrescoSSLSocketFactoryTest.class,
|
org.alfresco.repo.security.authentication.AlfrescoSSLSocketFactoryTest.class,
|
||||||
org.alfresco.repo.security.authentication.AuthorizationTest.class,
|
org.alfresco.repo.security.authentication.AuthorizationTest.class,
|
||||||
org.alfresco.repo.security.permissions.PermissionCheckedCollectionTest.class,
|
org.alfresco.repo.security.permissions.PermissionCheckedCollectionTest.class,
|
||||||
org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSetTest.class,
|
org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSetTest.class,
|
||||||
org.alfresco.repo.security.permissions.impl.acegi.ACLEntryVoterUtilsTest.class,
|
org.alfresco.repo.security.permissions.impl.acegi.ACLEntryVoterUtilsTest.class,
|
||||||
org.alfresco.repo.security.authentication.ChainingAuthenticationServiceTest.class,
|
org.alfresco.repo.security.authentication.ChainingAuthenticationServiceTest.class,
|
||||||
org.alfresco.repo.security.authentication.NameBasedUserNameGeneratorTest.class,
|
org.alfresco.repo.security.authentication.NameBasedUserNameGeneratorTest.class,
|
||||||
org.alfresco.repo.version.common.VersionImplTest.class,
|
org.alfresco.repo.version.common.VersionImplTest.class,
|
||||||
org.alfresco.repo.version.common.VersionHistoryImplTest.class,
|
org.alfresco.repo.version.common.VersionHistoryImplTest.class,
|
||||||
org.alfresco.repo.version.common.versionlabel.SerialVersionLabelPolicyTest.class,
|
org.alfresco.repo.version.common.versionlabel.SerialVersionLabelPolicyTest.class,
|
||||||
org.alfresco.repo.workflow.activiti.WorklfowObjectFactoryTest.class,
|
org.alfresco.repo.workflow.activiti.WorklfowObjectFactoryTest.class,
|
||||||
org.alfresco.repo.workflow.activiti.properties.ActivitiPriorityPropertyHandlerTest.class,
|
org.alfresco.repo.workflow.activiti.properties.ActivitiPriorityPropertyHandlerTest.class,
|
||||||
org.alfresco.repo.workflow.WorkflowSuiteContextShutdownTest.class,
|
org.alfresco.repo.workflow.WorkflowSuiteContextShutdownTest.class,
|
||||||
org.alfresco.repo.search.LuceneUtilsTest.class,
|
org.alfresco.repo.search.LuceneUtilsTest.class,
|
||||||
|
|
||||||
org.alfresco.heartbeat.HBDataCollectorServiceImplTest.class,
|
org.alfresco.heartbeat.HBDataCollectorServiceImplTest.class,
|
||||||
org.alfresco.heartbeat.jobs.LockingJobTest.class,
|
org.alfresco.heartbeat.jobs.LockingJobTest.class,
|
||||||
org.alfresco.heartbeat.jobs.QuartzJobSchedulerTest.class,
|
org.alfresco.heartbeat.jobs.QuartzJobSchedulerTest.class,
|
||||||
org.alfresco.heartbeat.AuthoritiesDataCollectorTest.class,
|
org.alfresco.heartbeat.AuthoritiesDataCollectorTest.class,
|
||||||
org.alfresco.heartbeat.ConfigurationDataCollectorTest.class,
|
org.alfresco.heartbeat.ConfigurationDataCollectorTest.class,
|
||||||
org.alfresco.heartbeat.InfoDataCollectorTest.class,
|
org.alfresco.heartbeat.InfoDataCollectorTest.class,
|
||||||
org.alfresco.heartbeat.ModelUsageDataCollectorTest.class,
|
org.alfresco.heartbeat.ModelUsageDataCollectorTest.class,
|
||||||
org.alfresco.heartbeat.SessionsUsageDataCollectorTest.class,
|
org.alfresco.heartbeat.SessionsUsageDataCollectorTest.class,
|
||||||
org.alfresco.heartbeat.SystemUsageDataCollectorTest.class,
|
org.alfresco.heartbeat.SystemUsageDataCollectorTest.class,
|
||||||
|
|
||||||
org.alfresco.util.BeanExtenderUnitTest.class,
|
org.alfresco.util.BeanExtenderUnitTest.class,
|
||||||
org.alfresco.util.bean.HierarchicalBeanLoaderTest.class,
|
org.alfresco.util.bean.HierarchicalBeanLoaderTest.class,
|
||||||
org.alfresco.util.resource.HierarchicalResourceLoaderTest.class,
|
org.alfresco.util.resource.HierarchicalResourceLoaderTest.class,
|
||||||
org.alfresco.repo.events.ClientUtilTest.class,
|
org.alfresco.repo.events.ClientUtilTest.class,
|
||||||
org.alfresco.repo.rendition2.RenditionService2Test.class,
|
org.alfresco.repo.rendition2.RenditionService2Test.class,
|
||||||
org.alfresco.repo.rendition2.TransformationOptionsConverterTest.class,
|
org.alfresco.repo.rendition2.TransformationOptionsConverterTest.class,
|
||||||
|
|
||||||
org.alfresco.repo.event2.RepoEvent2UnitSuite.class,
|
org.alfresco.repo.event2.RepoEvent2UnitSuite.class,
|
||||||
|
|
||||||
org.alfresco.util.schemacomp.SchemaDifferenceHelperUnitTest.class,
|
org.alfresco.util.schemacomp.SchemaDifferenceHelperUnitTest.class,
|
||||||
org.alfresco.repo.tagging.TaggingServiceImplUnitTest.class,
|
org.alfresco.repo.tagging.TaggingServiceImplUnitTest.class,
|
||||||
org.alfresco.repo.serviceaccount.ServiceAccountRegistryImplTest.class
|
org.alfresco.repo.serviceaccount.ServiceAccountRegistryImplTest.class
|
||||||
})
|
})
|
||||||
public class AllUnitTestsSuite
|
public class AllUnitTestsSuite
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
* #%L
|
* #%L
|
||||||
* Alfresco Repository
|
* Alfresco Repository
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
* %%
|
* %%
|
||||||
* This file is part of the Alfresco software.
|
* This file is part of the Alfresco software.
|
||||||
* If the software was purchased under a paid Alfresco license, the terms of
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
@@ -28,8 +28,14 @@ package org.alfresco.repo.action.executer;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.extensions.surf.util.I18NUtil;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.action.ActionImpl;
|
import org.alfresco.repo.action.ActionImpl;
|
||||||
|
import org.alfresco.repo.action.access.ActionAccessRestriction;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationComponent;
|
import org.alfresco.repo.security.authentication.AuthenticationComponent;
|
||||||
import org.alfresco.service.cmr.action.ActionDefinition;
|
import org.alfresco.service.cmr.action.ActionDefinition;
|
||||||
import org.alfresco.service.cmr.action.ParameterDefinition;
|
import org.alfresco.service.cmr.action.ParameterDefinition;
|
||||||
@@ -39,10 +45,6 @@ import org.alfresco.service.cmr.repository.StoreRef;
|
|||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.util.BaseSpringTest;
|
import org.alfresco.util.BaseSpringTest;
|
||||||
import org.alfresco.util.GUID;
|
import org.alfresco.util.GUID;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.springframework.extensions.surf.util.I18NUtil;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add features action execution test
|
* Add features action execution test
|
||||||
@@ -52,45 +54,40 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
@Transactional
|
@Transactional
|
||||||
public class AddFeaturesActionExecuterTest extends BaseSpringTest
|
public class AddFeaturesActionExecuterTest extends BaseSpringTest
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Id used to identify the test action created
|
||||||
|
*/
|
||||||
|
private final static String ID = GUID.generate();
|
||||||
/**
|
/**
|
||||||
* The node service
|
* The node service
|
||||||
*/
|
*/
|
||||||
private NodeService nodeService;
|
private NodeService nodeService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The store reference
|
* The store reference
|
||||||
*/
|
*/
|
||||||
private StoreRef testStoreRef;
|
private StoreRef testStoreRef;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The root node reference
|
* The root node reference
|
||||||
*/
|
*/
|
||||||
private NodeRef rootNodeRef;
|
private NodeRef rootNodeRef;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The test node reference
|
* The test node reference
|
||||||
*/
|
*/
|
||||||
private NodeRef nodeRef;
|
private NodeRef nodeRef;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The add features action executer
|
* The add features action executer
|
||||||
*/
|
*/
|
||||||
private AddFeaturesActionExecuter executer;
|
private AddFeaturesActionExecuter executer;
|
||||||
|
|
||||||
/**
|
|
||||||
* Id used to identify the test action created
|
|
||||||
*/
|
|
||||||
private final static String ID = GUID.generate();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called at the begining of all tests
|
* Called at the begining of all tests
|
||||||
*/
|
*/
|
||||||
@Before
|
@Before
|
||||||
public void before() throws Exception
|
public void before() throws Exception
|
||||||
{
|
{
|
||||||
this.nodeService = (NodeService)this.applicationContext.getBean("nodeService");
|
this.nodeService = (NodeService) this.applicationContext.getBean("nodeService");
|
||||||
|
|
||||||
AuthenticationComponent authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent");
|
AuthenticationComponent authenticationComponent = (AuthenticationComponent) applicationContext.getBean("authenticationComponent");
|
||||||
authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
|
authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
|
||||||
|
|
||||||
// Create the store and get the root node
|
// Create the store and get the root node
|
||||||
@@ -107,7 +104,7 @@ public class AddFeaturesActionExecuterTest extends BaseSpringTest
|
|||||||
ContentModel.TYPE_CONTENT).getChildRef();
|
ContentModel.TYPE_CONTENT).getChildRef();
|
||||||
|
|
||||||
// Get the executer instance
|
// Get the executer instance
|
||||||
this.executer = (AddFeaturesActionExecuter)this.applicationContext.getBean(AddFeaturesActionExecuter.NAME);
|
this.executer = (AddFeaturesActionExecuter) this.applicationContext.getBean(AddFeaturesActionExecuter.NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -161,4 +158,20 @@ public class AddFeaturesActionExecuterTest extends BaseSpringTest
|
|||||||
I18NUtil.setLocale(Locale.getDefault());
|
I18NUtil.setLocale(Locale.getDefault());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test check actionContext param is removed from adhoc properties
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCheckActionContext()
|
||||||
|
{
|
||||||
|
// Execute the action
|
||||||
|
ActionImpl action = new ActionImpl(null, ID, AddFeaturesActionExecuter.NAME, null);
|
||||||
|
action.setParameterValue(ActionAccessRestriction.ACTION_CONTEXT_PARAM_NAME, ActionAccessRestriction.V1_ACTION_CONTEXT);
|
||||||
|
action.setParameterValue(AddFeaturesActionExecuter.PARAM_ASPECT_NAME, ContentModel.ASPECT_CLASSIFIABLE);
|
||||||
|
this.executer.execute(action, this.nodeRef);
|
||||||
|
|
||||||
|
// Ensure the actionContext parameter has been removed
|
||||||
|
assertFalse(nodeService.getProperties(this.nodeRef).containsKey(QName.createQName(ActionAccessRestriction.ACTION_CONTEXT_PARAM_NAME)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
* #%L
|
* #%L
|
||||||
* Alfresco Repository
|
* Alfresco Repository
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2005 - 2022 Alfresco Software Limited
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
* %%
|
* %%
|
||||||
* This file is part of the Alfresco software.
|
* This file is part of the Alfresco software.
|
||||||
* If the software was purchased under a paid Alfresco license, the terms of
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
@@ -25,16 +25,21 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.rendition2;
|
package org.alfresco.repo.rendition2;
|
||||||
|
|
||||||
import static org.alfresco.model.ContentModel.PROP_CONTENT;
|
|
||||||
import static org.junit.Assert.assertNotEquals;
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
|
||||||
|
import static org.alfresco.model.ContentModel.PROP_CONTENT;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.model.RenditionModel;
|
import org.alfresco.model.RenditionModel;
|
||||||
|
import org.alfresco.repo.content.MimetypeMap;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
import org.alfresco.repo.security.permissions.AccessDeniedException;
|
import org.alfresco.repo.security.permissions.AccessDeniedException;
|
||||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||||
@@ -44,8 +49,6 @@ import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
|||||||
import org.alfresco.service.cmr.security.PermissionService;
|
import org.alfresco.service.cmr.security.PermissionService;
|
||||||
import org.alfresco.service.namespace.NamespaceService;
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration tests for {@link RenditionService2}
|
* Integration tests for {@link RenditionService2}
|
||||||
@@ -158,8 +161,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
|
|||||||
|
|
||||||
clearContent(ADMIN, sourceNodeRef);
|
clearContent(ADMIN, sourceNodeRef);
|
||||||
render(ADMIN, sourceNodeRef, DOC_LIB);
|
render(ADMIN, sourceNodeRef, DOC_LIB);
|
||||||
ChildAssociationRef assoc = AuthenticationUtil.runAs(() ->
|
ChildAssociationRef assoc = AuthenticationUtil.runAs(() -> renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN);
|
||||||
renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN);
|
|
||||||
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, false);
|
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, false);
|
||||||
assertNull("There should be no rendition as there was no content", assoc);
|
assertNull("There should be no rendition as there was no content", assoc);
|
||||||
}
|
}
|
||||||
@@ -190,8 +192,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
|
|||||||
|
|
||||||
clearContent(ADMIN, sourceNodeRef);
|
clearContent(ADMIN, sourceNodeRef);
|
||||||
render(ADMIN, sourceNodeRef, DOC_LIB);
|
render(ADMIN, sourceNodeRef, DOC_LIB);
|
||||||
ChildAssociationRef assoc = AuthenticationUtil.runAs(() ->
|
ChildAssociationRef assoc = AuthenticationUtil.runAs(() -> renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN);
|
||||||
renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN);
|
|
||||||
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, false);
|
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, false);
|
||||||
assertNull("There should be no rendition as there was no content", assoc);
|
assertNull("There should be no rendition as there was no content", assoc);
|
||||||
|
|
||||||
@@ -216,8 +217,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
|
|||||||
String ownerUserName = createRandomUser();
|
String ownerUserName = createRandomUser();
|
||||||
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
|
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
|
||||||
String otherUserName = createRandomUser();
|
String otherUserName = createRandomUser();
|
||||||
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
|
transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
|
||||||
{
|
|
||||||
permissionService.setPermission(sourceNodeRef, otherUserName, PermissionService.READ, true);
|
permissionService.setPermission(sourceNodeRef, otherUserName, PermissionService.READ, true);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@@ -236,8 +236,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
|
|||||||
String ownerUserName = createRandomUser();
|
String ownerUserName = createRandomUser();
|
||||||
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
|
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
|
||||||
String otherUserName = createRandomUser();
|
String otherUserName = createRandomUser();
|
||||||
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
|
transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
|
||||||
{
|
|
||||||
permissionService.setPermission(sourceNodeRef, otherUserName, PermissionService.READ, true);
|
permissionService.setPermission(sourceNodeRef, otherUserName, PermissionService.READ, true);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@@ -257,8 +256,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
|
|||||||
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
|
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
|
||||||
render(ownerUserName, sourceNodeRef, DOC_LIB);
|
render(ownerUserName, sourceNodeRef, DOC_LIB);
|
||||||
String noPermissionsUser = createRandomUser();
|
String noPermissionsUser = createRandomUser();
|
||||||
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
|
transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
|
||||||
{
|
|
||||||
permissionService.setPermission(sourceNodeRef, noPermissionsUser, PermissionService.ALL_PERMISSIONS, false);
|
permissionService.setPermission(sourceNodeRef, noPermissionsUser, PermissionService.ALL_PERMISSIONS, false);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@@ -280,12 +278,9 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
|
|||||||
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
|
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
|
||||||
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
|
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
|
||||||
transactionService.getRetryingTransactionHelper()
|
transactionService.getRetryingTransactionHelper()
|
||||||
.doInTransaction(() ->
|
.doInTransaction(() -> AuthenticationUtil.runAs(() -> renditionService.render(sourceNodeRef, doclibRendDefQName), ownerUserName));
|
||||||
AuthenticationUtil.runAs(() ->
|
|
||||||
renditionService.render(sourceNodeRef, doclibRendDefQName), ownerUserName));
|
|
||||||
|
|
||||||
NodeRef oldRendition = AuthenticationUtil.runAs(() ->
|
NodeRef oldRendition = AuthenticationUtil.runAs(() -> renditionService.getRenditionByName(sourceNodeRef, doclibRendDefQName).getChildRef(), ownerUserName);
|
||||||
renditionService.getRenditionByName(sourceNodeRef, doclibRendDefQName).getChildRef(), ownerUserName);
|
|
||||||
assertFalse("The rendition should be generated by old Rendition Service",
|
assertFalse("The rendition should be generated by old Rendition Service",
|
||||||
AuthenticationUtil.runAs(() -> nodeService.hasAspect(oldRendition, RenditionModel.ASPECT_RENDITION2), ownerUserName));
|
AuthenticationUtil.runAs(() -> nodeService.hasAspect(oldRendition, RenditionModel.ASPECT_RENDITION2), ownerUserName));
|
||||||
|
|
||||||
@@ -335,12 +330,10 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
|
|||||||
renditionService2.setEnabled(false);
|
renditionService2.setEnabled(false);
|
||||||
|
|
||||||
// Call 'clearRenditionContentData' method directly to prove rendition content will be cleaned
|
// Call 'clearRenditionContentData' method directly to prove rendition content will be cleaned
|
||||||
AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork<Void>) () ->
|
AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
|
||||||
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
|
renditionService2.clearRenditionContentData(sourceNodeRef, DOC_LIB);
|
||||||
{
|
return null;
|
||||||
renditionService2.clearRenditionContentData(sourceNodeRef, DOC_LIB);
|
}), ADMIN);
|
||||||
return null;
|
|
||||||
}), ADMIN);
|
|
||||||
|
|
||||||
// The rendition should not have content by now
|
// The rendition should not have content by now
|
||||||
assertNull("Rendition has content", nodeService.getProperty(renditionNodeRef, ContentModel.PROP_CONTENT));
|
assertNull("Rendition has content", nodeService.getProperty(renditionNodeRef, ContentModel.PROP_CONTENT));
|
||||||
@@ -356,8 +349,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
|
|||||||
/**
|
/**
|
||||||
* Tests if a rendition without content (but with contentHashCode) can be generated again.
|
* Tests if a rendition without content (but with contentHashCode) can be generated again.
|
||||||
* <p>
|
* <p>
|
||||||
* If the rendition consumption receives a null InputStream, the contentHashCode should be cleaned from the
|
* If the rendition consumption receives a null InputStream, the contentHashCode should be cleaned from the rendition node, allowing new requests to generate the rendition.
|
||||||
* rendition node, allowing new requests to generate the rendition.
|
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@@ -369,8 +361,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
|
|||||||
/**
|
/**
|
||||||
* Tests if a rendition without content (but with contentHashCode) can be generated again.
|
* Tests if a rendition without content (but with contentHashCode) can be generated again.
|
||||||
* <p>
|
* <p>
|
||||||
* If the rendition consumption receives a zero length InputStream, the contentHashCode should be cleaned from the
|
* If the rendition consumption receives a zero length InputStream, the contentHashCode should be cleaned from the rendition node, allowing new requests to generate the rendition.
|
||||||
* rendition node, allowing new requests to generate the rendition.
|
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@@ -492,22 +483,16 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
|
|||||||
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
|
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
|
||||||
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
|
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
|
||||||
transactionService.getRetryingTransactionHelper()
|
transactionService.getRetryingTransactionHelper()
|
||||||
.doInTransaction(() ->
|
.doInTransaction(() -> AuthenticationUtil.runAs(() -> renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
|
||||||
AuthenticationUtil.runAs(() ->
|
|
||||||
renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
|
|
||||||
assertNotNull("The old renditions service did not render", waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true));
|
assertNotNull("The old renditions service did not render", waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true));
|
||||||
List<String> lastThumbnailModification = transactionService.getRetryingTransactionHelper()
|
List<String> lastThumbnailModification = transactionService.getRetryingTransactionHelper()
|
||||||
.doInTransaction(() ->
|
.doInTransaction(() -> AuthenticationUtil.runAs(() -> (List<String>) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA), ADMIN));
|
||||||
AuthenticationUtil.runAs(() ->
|
|
||||||
(List<String>) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA), ADMIN));
|
|
||||||
updateContent(ADMIN, sourceNodeRef, "quick.png");
|
updateContent(ADMIN, sourceNodeRef, "quick.png");
|
||||||
List<String> newThumbnailModification = null;
|
List<String> newThumbnailModification = null;
|
||||||
for (int i = 0; i < 5; i++)
|
for (int i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
newThumbnailModification = transactionService.getRetryingTransactionHelper()
|
newThumbnailModification = transactionService.getRetryingTransactionHelper()
|
||||||
.doInTransaction(() ->
|
.doInTransaction(() -> AuthenticationUtil.runAs(() -> (List<String>) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA), ADMIN));
|
||||||
AuthenticationUtil.runAs(() ->
|
|
||||||
(List<String>) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA), ADMIN));
|
|
||||||
if (!newThumbnailModification.equals(lastThumbnailModification))
|
if (!newThumbnailModification.equals(lastThumbnailModification))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
@@ -579,9 +564,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
|
|||||||
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
|
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
|
||||||
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
|
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
|
||||||
transactionService.getRetryingTransactionHelper()
|
transactionService.getRetryingTransactionHelper()
|
||||||
.doInTransaction(() ->
|
.doInTransaction(() -> AuthenticationUtil.runAs(() -> renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
|
||||||
AuthenticationUtil.runAs(() ->
|
|
||||||
renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
|
|
||||||
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
|
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
|
||||||
|
|
||||||
renditionService2.setEnabled(true);
|
renditionService2.setEnabled(true);
|
||||||
@@ -652,9 +635,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
|
|||||||
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
|
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
|
||||||
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
|
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
|
||||||
transactionService.getRetryingTransactionHelper()
|
transactionService.getRetryingTransactionHelper()
|
||||||
.doInTransaction(() ->
|
.doInTransaction(() -> AuthenticationUtil.runAs(() -> renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
|
||||||
AuthenticationUtil.runAs(() ->
|
|
||||||
renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
|
|
||||||
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
|
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
|
||||||
|
|
||||||
renditionService2.setEnabled(true);
|
renditionService2.setEnabled(true);
|
||||||
@@ -682,4 +663,57 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
|
|||||||
renditionService2.setEnabled(true);
|
renditionService2.setEnabled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTextExtractTransformAllowedWhenThumbnailDisabled()
|
||||||
|
{
|
||||||
|
// create a source node
|
||||||
|
NodeRef sourceNodeRef = createSource(ADMIN, "quick.pdf");
|
||||||
|
assertNotNull("Node not generated", sourceNodeRef);
|
||||||
|
String replyQueue = "org.test.queue";
|
||||||
|
String targetMimetype = MimetypeMap.MIMETYPE_TEXT_PLAIN;
|
||||||
|
|
||||||
|
TransformDefinition textExtractTransform = new TransformDefinition(
|
||||||
|
targetMimetype,
|
||||||
|
java.util.Collections.emptyMap(),
|
||||||
|
"clientData",
|
||||||
|
replyQueue,
|
||||||
|
"requestId");
|
||||||
|
|
||||||
|
renditionService2.setThumbnailsEnabled(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Should NOT throw, as this is a text extract transform
|
||||||
|
AuthenticationUtil.runAs(() -> {
|
||||||
|
transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
|
||||||
|
renditionService2.transform(sourceNodeRef, textExtractTransform);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}, ADMIN);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
renditionService2.setThumbnailsEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMetadataExtractTransformAllowedWhenThumbnailDisabled()
|
||||||
|
{
|
||||||
|
// create a source node
|
||||||
|
NodeRef sourceNodeRef = createSource(ADMIN, "quick.pdf");
|
||||||
|
assertNotNull("Node not generated", sourceNodeRef);
|
||||||
|
renditionService2.setThumbnailsEnabled(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Should NOT throw, as this is a metadata extract transform
|
||||||
|
extract(ADMIN, sourceNodeRef);
|
||||||
|
waitForExtract(ADMIN, sourceNodeRef, true);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
renditionService2.setThumbnailsEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -38,8 +38,7 @@ import java.util.Set;
|
|||||||
import com.nimbusds.oauth2.sdk.ParseException;
|
import com.nimbusds.oauth2.sdk.ParseException;
|
||||||
import com.nimbusds.oauth2.sdk.Scope;
|
import com.nimbusds.oauth2.sdk.Scope;
|
||||||
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
|
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
|
||||||
|
import net.minidev.json.JSONObject;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacadeFactoryBean.ClientRegistrationProvider;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
@@ -51,12 +50,17 @@ import org.springframework.http.ResponseEntity;
|
|||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacadeFactoryBean.ClientRegistrationProvider;
|
||||||
|
|
||||||
public class ClientRegistrationProviderUnitTest
|
public class ClientRegistrationProviderUnitTest
|
||||||
{
|
{
|
||||||
private static final String CLIENT_ID = "alfresco";
|
private static final String CLIENT_ID = "alfresco";
|
||||||
private static final String OPENID_CONFIGURATION = "{\"token_endpoint\":\"https://login.serviceonline.alfresco/common/oauth2/v2.0/token\",\"token_endpoint_auth_methods_supported\":[\"client_secret_post\",\"private_key_jwt\",\"client_secret_basic\"],\"jwks_uri\":\"https://login.serviceonline.alfresco/common/discovery/v2.0/keys\",\"response_modes_supported\":[\"query\",\"fragment\",\"form_post\"],\"subject_types_supported\":[\"pairwise\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"response_types_supported\":[\"code\",\"id_token\",\"code id_token\",\"id_token token\"],\"scopes_supported\":[\"openid\",\"profile\",\"email\",\"offline_access\"],\"issuer\":\"https://login.serviceonline.alfresco/alfresco/v2.0\",\"request_uri_parameter_supported\":false,\"userinfo_endpoint\":\"https://graph.service.alfresco/oidc/userinfo\",\"authorization_endpoint\":\"https://login.serviceonline.alfresco/common/oauth2/v2.0/authorize\",\"device_authorization_endpoint\":\"https://login.serviceonline.alfresco/common/oauth2/v2.0/devicecode\",\"http_logout_supported\":true,\"frontchannel_logout_supported\":true,\"end_session_endpoint\":\"https://login.serviceonline.alfresco/common/oauth2/v2.0/logout\",\"claims_supported\":[\"sub\",\"iss\",\"cloud_instance_name\",\"cloud_instance_host_name\",\"cloud_graph_host_name\",\"msgraph_host\",\"aud\",\"exp\",\"iat\",\"auth_time\",\"acr\",\"nonce\",\"preferred_username\",\"name\",\"tid\",\"ver\",\"at_hash\",\"c_hash\",\"email\"],\"kerberos_endpoint\":\"https://login.serviceonline.alfresco/common/kerberos\",\"tenant_region_scope\":null,\"cloud_instance_name\":\"serviceonline.alfresco\",\"cloud_graph_host_name\":\"graph.oidc.net\",\"msgraph_host\":\"graph.service.alfresco\",\"rbac_url\":\"https://pas.oidc.alfresco\"}";
|
private static final String OPENID_CONFIGURATION = "{\"token_endpoint\":\"https://login.serviceonline.alfresco/common/oauth2/v2.0/token\",\"token_endpoint_auth_methods_supported\":[\"client_secret_post\",\"private_key_jwt\",\"client_secret_basic\"],\"jwks_uri\":\"https://login.serviceonline.alfresco/common/discovery/v2.0/keys\",\"response_modes_supported\":[\"query\",\"fragment\",\"form_post\"],\"subject_types_supported\":[\"pairwise\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"response_types_supported\":[\"code\",\"id_token\",\"code id_token\",\"id_token token\"],\"scopes_supported\":[\"openid\",\"profile\",\"email\",\"offline_access\"],\"issuer\":\"https://login.serviceonline.alfresco/alfresco/v2.0\",\"request_uri_parameter_supported\":false,\"userinfo_endpoint\":\"https://graph.service.alfresco/oidc/userinfo\",\"authorization_endpoint\":\"https://login.serviceonline.alfresco/common/oauth2/v2.0/authorize\",\"device_authorization_endpoint\":\"https://login.serviceonline.alfresco/common/oauth2/v2.0/devicecode\",\"http_logout_supported\":true,\"frontchannel_logout_supported\":true,\"end_session_endpoint\":\"https://login.serviceonline.alfresco/common/oauth2/v2.0/logout\",\"claims_supported\":[\"sub\",\"iss\",\"cloud_instance_name\",\"cloud_instance_host_name\",\"cloud_graph_host_name\",\"msgraph_host\",\"aud\",\"exp\",\"iat\",\"auth_time\",\"acr\",\"nonce\",\"preferred_username\",\"name\",\"tid\",\"ver\",\"at_hash\",\"c_hash\",\"email\"],\"kerberos_endpoint\":\"https://login.serviceonline.alfresco/common/kerberos\",\"tenant_region_scope\":null,\"cloud_instance_name\":\"serviceonline.alfresco\",\"cloud_graph_host_name\":\"graph.oidc.net\",\"msgraph_host\":\"graph.service.alfresco\",\"rbac_url\":\"https://pas.oidc.alfresco\"}";
|
||||||
private static final String DISCOVERY_PATH_SEGMENTS = "/.well-known/openid-configuration";
|
private static final String DISCOVERY_PATH_SEGMENTS = "/.well-known/openid-configuration";
|
||||||
private static final String AUTH_SERVER = "https://login.serviceonline.alfresco";
|
private static final String AUTH_SERVER = "https://login.serviceonline.alfresco";
|
||||||
|
private static final String ADMIN_CONSOLE_SCOPES = "openid,email,profile,offline_access";
|
||||||
|
private static final String PSSWD_GRANT_SCOPES = "openid,email,profile";
|
||||||
|
private static final String ISSUER_ATRR = "issuer";
|
||||||
|
|
||||||
private IdentityServiceConfig config;
|
private IdentityServiceConfig config;
|
||||||
private RestTemplate restTemplate;
|
private RestTemplate restTemplate;
|
||||||
@@ -70,6 +74,9 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
config = new IdentityServiceConfig();
|
config = new IdentityServiceConfig();
|
||||||
config.setAuthServerUrl(AUTH_SERVER);
|
config.setAuthServerUrl(AUTH_SERVER);
|
||||||
config.setResource(CLIENT_ID);
|
config.setResource(CLIENT_ID);
|
||||||
|
config.setAdminConsoleScopes(ADMIN_CONSOLE_SCOPES);
|
||||||
|
config.setPasswordGrantScopes(PSSWD_GRANT_SCOPES);
|
||||||
|
config.setIssuerAttribute(ISSUER_ATRR);
|
||||||
|
|
||||||
restTemplate = mock(RestTemplate.class);
|
restTemplate = mock(RestTemplate.class);
|
||||||
ResponseEntity responseEntity = mock(ResponseEntity.class);
|
ResponseEntity responseEntity = mock(ResponseEntity.class);
|
||||||
@@ -90,7 +97,7 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
||||||
|
|
||||||
ClientRegistration clientRegistration = new ClientRegistrationProvider(config).createClientRegistration(
|
ClientRegistration clientRegistration = new ClientRegistrationProvider(config).createClientRegistration(
|
||||||
restTemplate);
|
restTemplate);
|
||||||
assertThat(clientRegistration).isNotNull();
|
assertThat(clientRegistration).isNotNull();
|
||||||
assertThat(clientRegistration.getClientId()).isNotNull();
|
assertThat(clientRegistration.getClientId()).isNotNull();
|
||||||
assertThat(clientRegistration.getProviderDetails().getAuthorizationUri()).isNotNull();
|
assertThat(clientRegistration.getProviderDetails().getAuthorizationUri()).isNotNull();
|
||||||
@@ -99,7 +106,7 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint()).isNotNull();
|
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint()).isNotNull();
|
||||||
assertThat(clientRegistration.getProviderDetails().getIssuerUri()).isNotNull();
|
assertThat(clientRegistration.getProviderDetails().getIssuerUri()).isNotNull();
|
||||||
assertThat(requestEntityCaptor.getValue().getUrl().toASCIIString()).isEqualTo(
|
assertThat(requestEntityCaptor.getValue().getUrl().toASCIIString()).isEqualTo(
|
||||||
AUTH_SERVER + DISCOVERY_PATH_SEGMENTS);
|
AUTH_SERVER + DISCOVERY_PATH_SEGMENTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +119,7 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
||||||
|
|
||||||
ClientRegistration clientRegistration = new ClientRegistrationProvider(config).createClientRegistration(
|
ClientRegistration clientRegistration = new ClientRegistrationProvider(config).createClientRegistration(
|
||||||
restTemplate);
|
restTemplate);
|
||||||
assertThat(clientRegistration).isNotNull();
|
assertThat(clientRegistration).isNotNull();
|
||||||
assertThat(clientRegistration.getClientId()).isNotNull();
|
assertThat(clientRegistration.getClientId()).isNotNull();
|
||||||
assertThat(clientRegistration.getProviderDetails().getAuthorizationUri()).isNotNull();
|
assertThat(clientRegistration.getProviderDetails().getAuthorizationUri()).isNotNull();
|
||||||
@@ -121,7 +128,7 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint()).isNotNull();
|
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint()).isNotNull();
|
||||||
assertThat(clientRegistration.getProviderDetails().getIssuerUri()).isNotNull();
|
assertThat(clientRegistration.getProviderDetails().getIssuerUri()).isNotNull();
|
||||||
assertThat(requestEntityCaptor.getValue().getUrl().toASCIIString()).isEqualTo(
|
assertThat(requestEntityCaptor.getValue().getUrl().toASCIIString()).isEqualTo(
|
||||||
AUTH_SERVER + DISCOVERY_PATH_SEGMENTS);
|
AUTH_SERVER + DISCOVERY_PATH_SEGMENTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +141,7 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
||||||
|
|
||||||
assertThrows(IdentityServiceException.class,
|
assertThrows(IdentityServiceException.class,
|
||||||
() -> new ClientRegistrationProvider(config).createClientRegistration(restTemplate));
|
() -> new ClientRegistrationProvider(config).createClientRegistration(restTemplate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +155,7 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
||||||
|
|
||||||
assertThrows(IdentityServiceException.class,
|
assertThrows(IdentityServiceException.class,
|
||||||
() -> new ClientRegistrationProvider(config).createClientRegistration(restTemplate));
|
() -> new ClientRegistrationProvider(config).createClientRegistration(restTemplate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +168,7 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
||||||
|
|
||||||
assertThrows(IdentityServiceException.class,
|
assertThrows(IdentityServiceException.class,
|
||||||
() -> new ClientRegistrationProvider(config).createClientRegistration(restTemplate));
|
() -> new ClientRegistrationProvider(config).createClientRegistration(restTemplate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +181,7 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
||||||
|
|
||||||
assertThrows(IdentityServiceException.class,
|
assertThrows(IdentityServiceException.class,
|
||||||
() -> new ClientRegistrationProvider(config).createClientRegistration(restTemplate));
|
() -> new ClientRegistrationProvider(config).createClientRegistration(restTemplate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +194,7 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
||||||
|
|
||||||
assertThrows(IdentityServiceException.class,
|
assertThrows(IdentityServiceException.class,
|
||||||
() -> new ClientRegistrationProvider(config).createClientRegistration(restTemplate));
|
() -> new ClientRegistrationProvider(config).createClientRegistration(restTemplate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +207,7 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
||||||
|
|
||||||
assertThrows(IdentityServiceException.class,
|
assertThrows(IdentityServiceException.class,
|
||||||
() -> new ClientRegistrationProvider(config).createClientRegistration(restTemplate));
|
() -> new ClientRegistrationProvider(config).createClientRegistration(restTemplate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +222,7 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
|
|
||||||
new ClientRegistrationProvider(config).createClientRegistration(restTemplate);
|
new ClientRegistrationProvider(config).createClientRegistration(restTemplate);
|
||||||
assertThat(requestEntityCaptor.getValue().getUrl().toASCIIString()).isEqualTo(
|
assertThat(requestEntityCaptor.getValue().getUrl().toASCIIString()).isEqualTo(
|
||||||
AUTH_SERVER + "/realms/alfresco" + DISCOVERY_PATH_SEGMENTS);
|
AUTH_SERVER + "/realms/alfresco" + DISCOVERY_PATH_SEGMENTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,10 +234,10 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
||||||
|
|
||||||
ClientRegistration clientRegistration = new ClientRegistrationProvider(config).createClientRegistration(
|
ClientRegistration clientRegistration = new ClientRegistrationProvider(config).createClientRegistration(
|
||||||
restTemplate);
|
restTemplate);
|
||||||
assertThat(
|
assertThat(
|
||||||
clientRegistration.getScopes().containsAll(
|
clientRegistration.getScopes().containsAll(
|
||||||
Set.of("openid", "profile", "email"))).isTrue();
|
Set.of("openid", "profile", "email"))).isTrue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +250,7 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
||||||
|
|
||||||
ClientRegistration clientRegistration = new ClientRegistrationProvider(config).createClientRegistration(
|
ClientRegistration clientRegistration = new ClientRegistrationProvider(config).createClientRegistration(
|
||||||
restTemplate);
|
restTemplate);
|
||||||
assertThat(clientRegistration.getScopes().size()).isEqualTo(1);
|
assertThat(clientRegistration.getScopes().size()).isEqualTo(1);
|
||||||
assertThat(clientRegistration.getScopes().stream().findFirst().get()).isEqualTo("openid");
|
assertThat(clientRegistration.getScopes().stream().findFirst().get()).isEqualTo("openid");
|
||||||
}
|
}
|
||||||
@@ -260,7 +267,45 @@ public class ClientRegistrationProviderUnitTest
|
|||||||
|
|
||||||
new ClientRegistrationProvider(config).createClientRegistration(restTemplate);
|
new ClientRegistrationProvider(config).createClientRegistration(restTemplate);
|
||||||
assertThat(requestEntityCaptor.getValue().getUrl().toASCIIString()).isEqualTo(
|
assertThat(requestEntityCaptor.getValue().getUrl().toASCIIString()).isEqualTo(
|
||||||
"https://login.serviceonline.alfresco/alfresco/v2.0" + DISCOVERY_PATH_SEGMENTS);
|
"https://login.serviceonline.alfresco/alfresco/v2.0" + DISCOVERY_PATH_SEGMENTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldUseDefaultIssuerAttribute()
|
||||||
|
{
|
||||||
|
config.setIssuerUrl(null);
|
||||||
|
try (MockedStatic<OIDCProviderMetadata> providerMetadata = Mockito.mockStatic(OIDCProviderMetadata.class))
|
||||||
|
{
|
||||||
|
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
||||||
|
|
||||||
|
ClientRegistration clientRegistration = new ClientRegistrationProvider(config).createClientRegistration(
|
||||||
|
restTemplate);
|
||||||
|
assertThat(clientRegistration.getProviderDetails().getIssuerUri()).isEqualTo("https://login.serviceonline.alfresco/alfresco/v2.0");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldUseCustomIssuerAttribute()
|
||||||
|
{
|
||||||
|
try (MockedStatic<OIDCProviderMetadata> providerMetadata = Mockito.mockStatic(OIDCProviderMetadata.class))
|
||||||
|
{
|
||||||
|
config.setIssuerAttribute("access_token_issuer");
|
||||||
|
when(oidcResponse.getCustomParameters()).thenReturn(createJSONObject("access_token_issuer", "https://login.serviceonline.alfresco/alfresco/v2.0/at_trust"));
|
||||||
|
providerMetadata.when(() -> OIDCProviderMetadata.parse(any(String.class))).thenReturn(oidcResponse);
|
||||||
|
|
||||||
|
ClientRegistration clientRegistration = new ClientRegistrationProvider(config).createClientRegistration(
|
||||||
|
restTemplate);
|
||||||
|
assertThat(clientRegistration.getProviderDetails().getIssuerUri()).isEqualTo("https://login.serviceonline.alfresco/alfresco/v2.0/at_trust");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JSONObject createJSONObject(String fieldName, String fieldValue)
|
||||||
|
{
|
||||||
|
JSONObject jsonObject = new JSONObject();
|
||||||
|
jsonObject.appendField(fieldName, fieldValue);
|
||||||
|
return jsonObject;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -2,7 +2,7 @@
|
|||||||
* #%L
|
* #%L
|
||||||
* Alfresco Repository
|
* Alfresco Repository
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2005 - 2023 Alfresco Software Limited
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
* %%
|
* %%
|
||||||
* This file is part of the Alfresco software.
|
* This file is part of the Alfresco software.
|
||||||
* If the software was purchased under a paid Alfresco license, the terms of
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
@@ -32,21 +32,23 @@ import static org.mockito.Mockito.when;
|
|||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import org.alfresco.error.ExceptionStackUtil;
|
import org.alfresco.error.ExceptionStackUtil;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationContext;
|
import org.alfresco.repo.security.authentication.AuthenticationContext;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationException;
|
import org.alfresco.repo.security.authentication.AuthenticationException;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AccessTokenAuthorization;
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AccessTokenAuthorization;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationException;
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationException;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationGrant;
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationGrant;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.OIDCUserInfo;
|
||||||
import org.alfresco.repo.security.sync.UserRegistrySynchronizer;
|
import org.alfresco.repo.security.sync.UserRegistrySynchronizer;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.service.cmr.security.PersonService;
|
import org.alfresco.service.cmr.security.PersonService;
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.alfresco.util.BaseSpringTest;
|
import org.alfresco.util.BaseSpringTest;
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
public class IdentityServiceAuthenticationComponentTest extends BaseSpringTest
|
public class IdentityServiceAuthenticationComponentTest extends BaseSpringTest
|
||||||
{
|
{
|
||||||
@@ -67,7 +69,6 @@ public class IdentityServiceAuthenticationComponentTest extends BaseSpringTest
|
|||||||
@Autowired
|
@Autowired
|
||||||
private PersonService personService;
|
private PersonService personService;
|
||||||
|
|
||||||
|
|
||||||
private IdentityServiceJITProvisioningHandler jitProvisioning;
|
private IdentityServiceJITProvisioningHandler jitProvisioning;
|
||||||
private IdentityServiceFacade mockIdentityServiceFacade;
|
private IdentityServiceFacade mockIdentityServiceFacade;
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@ public class IdentityServiceAuthenticationComponentTest extends BaseSpringTest
|
|||||||
authenticationContext.clearCurrentSecurityContext();
|
authenticationContext.clearCurrentSecurityContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test (expected=AuthenticationException.class)
|
@Test(expected = AuthenticationException.class)
|
||||||
public void testAuthenticationFail()
|
public void testAuthenticationFail()
|
||||||
{
|
{
|
||||||
final AuthorizationGrant grant = AuthorizationGrant.password("username", "password");
|
final AuthorizationGrant grant = AuthorizationGrant.password("username", "password");
|
||||||
@@ -122,7 +123,7 @@ public class IdentityServiceAuthenticationComponentTest extends BaseSpringTest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test (expected=AuthenticationException.class)
|
@Test(expected = AuthenticationException.class)
|
||||||
public void testAuthenticationFail_otherException()
|
public void testAuthenticationFail_otherException()
|
||||||
{
|
{
|
||||||
final AuthorizationGrant grant = AuthorizationGrant.password("username", "password");
|
final AuthorizationGrant grant = AuthorizationGrant.password("username", "password");
|
||||||
@@ -145,15 +146,15 @@ public class IdentityServiceAuthenticationComponentTest extends BaseSpringTest
|
|||||||
when(accessToken.getTokenValue()).thenReturn("JWT_TOKEN");
|
when(accessToken.getTokenValue()).thenReturn("JWT_TOKEN");
|
||||||
when(mockIdentityServiceFacade.authorize(grant)).thenReturn(authorization);
|
when(mockIdentityServiceFacade.authorize(grant)).thenReturn(authorization);
|
||||||
when(jitProvisioning.extractUserInfoAndCreateUserIfNeeded("JWT_TOKEN"))
|
when(jitProvisioning.extractUserInfoAndCreateUserIfNeeded("JWT_TOKEN"))
|
||||||
.thenReturn(Optional.of(new OIDCUserInfo("username", "", "", "")));
|
.thenReturn(Optional.of(new OIDCUserInfo("username", "", "", "")));
|
||||||
|
|
||||||
authComponent.authenticateImpl("username", "password".toCharArray());
|
authComponent.authenticateImpl("username", "password".toCharArray());
|
||||||
|
|
||||||
// Check that the authenticated user has been set
|
// Check that the authenticated user has been set
|
||||||
assertEquals("User has not been set as expected.","username", authenticationContext.getCurrentUserName());
|
assertEquals("User has not been set as expected.", "username", authenticationContext.getCurrentUserName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test (expected= AuthenticationException.class)
|
@Test(expected = AuthenticationException.class)
|
||||||
public void testFallthroughWhenIdentityServiceFacadeIsNull()
|
public void testFallthroughWhenIdentityServiceFacadeIsNull()
|
||||||
{
|
{
|
||||||
authComponent.setIdentityServiceFacade(null);
|
authComponent.setIdentityServiceFacade(null);
|
||||||
|
@@ -25,29 +25,29 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.security.authentication.identityservice;
|
package org.alfresco.repo.security.authentication.identityservice;
|
||||||
|
|
||||||
import static org.mockito.Mockito.atLeast;
|
import static org.mockito.Mockito.*;
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.nimbusds.openid.connect.sdk.claims.PersonClaims;
|
import com.nimbusds.openid.connect.sdk.claims.PersonClaims;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
|
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
|
||||||
import org.alfresco.repo.management.subsystems.DefaultChildApplicationContextManager;
|
import org.alfresco.repo.management.subsystems.DefaultChildApplicationContextManager;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.OIDCUserInfo;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.UserInfoAttrMapping;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.service.cmr.security.PersonService;
|
import org.alfresco.service.cmr.security.PersonService;
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.alfresco.util.BaseSpringTest;
|
import org.alfresco.util.BaseSpringTest;
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
@SuppressWarnings("PMD.AvoidAccessibilityAlteration")
|
@SuppressWarnings("PMD.AvoidAccessibilityAlteration")
|
||||||
public class IdentityServiceJITProvisioningHandlerTest extends BaseSpringTest
|
public class IdentityServiceJITProvisioningHandlerTest extends BaseSpringTest
|
||||||
@@ -61,12 +61,12 @@ public class IdentityServiceJITProvisioningHandlerTest extends BaseSpringTest
|
|||||||
private IdentityServiceJITProvisioningHandler jitProvisioningHandler;
|
private IdentityServiceJITProvisioningHandler jitProvisioningHandler;
|
||||||
|
|
||||||
private final boolean isAuth0Enabled = Optional.ofNullable(System.getProperty("auth0.enabled"))
|
private final boolean isAuth0Enabled = Optional.ofNullable(System.getProperty("auth0.enabled"))
|
||||||
.map(Boolean::valueOf)
|
.map(Boolean::valueOf)
|
||||||
.orElse(false);
|
.orElse(false);
|
||||||
|
|
||||||
private final String userPassword = Optional.ofNullable(System.getProperty("admin.password"))
|
private final String userPassword = Optional.ofNullable(System.getProperty("admin.password"))
|
||||||
.filter(password -> isAuth0Enabled)
|
.filter(password -> isAuth0Enabled)
|
||||||
.orElse("password");
|
.orElse("password");
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup()
|
public void setup()
|
||||||
@@ -75,16 +75,16 @@ public class IdentityServiceJITProvisioningHandlerTest extends BaseSpringTest
|
|||||||
nodeService = (NodeService) applicationContext.getBean("nodeService");
|
nodeService = (NodeService) applicationContext.getBean("nodeService");
|
||||||
transactionService = (TransactionService) applicationContext.getBean("transactionService");
|
transactionService = (TransactionService) applicationContext.getBean("transactionService");
|
||||||
DefaultChildApplicationContextManager childApplicationContextManager = (DefaultChildApplicationContextManager) applicationContext
|
DefaultChildApplicationContextManager childApplicationContextManager = (DefaultChildApplicationContextManager) applicationContext
|
||||||
.getBean("Authentication");
|
.getBean("Authentication");
|
||||||
ChildApplicationContextFactory childApplicationContextFactory = childApplicationContextManager.getChildApplicationContextFactory(
|
ChildApplicationContextFactory childApplicationContextFactory = childApplicationContextManager.getChildApplicationContextFactory(
|
||||||
"identity-service1");
|
"identity-service1");
|
||||||
|
|
||||||
identityServiceFacade = (IdentityServiceFacade) childApplicationContextFactory.getApplicationContext()
|
identityServiceFacade = (IdentityServiceFacade) childApplicationContextFactory.getApplicationContext()
|
||||||
.getBean("identityServiceFacade");
|
.getBean("identityServiceFacade");
|
||||||
jitProvisioningHandler = (IdentityServiceJITProvisioningHandler) childApplicationContextFactory.getApplicationContext()
|
jitProvisioningHandler = (IdentityServiceJITProvisioningHandler) childApplicationContextFactory.getApplicationContext()
|
||||||
.getBean("jitProvisioningHandler");
|
.getBean("jitProvisioningHandler");
|
||||||
IdentityServiceConfig identityServiceConfig = (IdentityServiceConfig) childApplicationContextFactory.getApplicationContext()
|
IdentityServiceConfig identityServiceConfig = (IdentityServiceConfig) childApplicationContextFactory.getApplicationContext()
|
||||||
.getBean("identityServiceConfig");
|
.getBean("identityServiceConfig");
|
||||||
identityServiceConfig.setAllowAnyHostname(true);
|
identityServiceConfig.setAllowAnyHostname(true);
|
||||||
identityServiceConfig.setClientKeystore(null);
|
identityServiceConfig.setClientKeystore(null);
|
||||||
identityServiceConfig.setDisableTrustManager(true);
|
identityServiceConfig.setDisableTrustManager(true);
|
||||||
@@ -95,12 +95,11 @@ public class IdentityServiceJITProvisioningHandlerTest extends BaseSpringTest
|
|||||||
{
|
{
|
||||||
assertFalse(personService.personExists(IDS_USERNAME));
|
assertFalse(personService.personExists(IDS_USERNAME));
|
||||||
|
|
||||||
IdentityServiceFacade.AccessTokenAuthorization accessTokenAuthorization =
|
IdentityServiceFacade.AccessTokenAuthorization accessTokenAuthorization = identityServiceFacade.authorize(
|
||||||
identityServiceFacade.authorize(
|
|
||||||
IdentityServiceFacade.AuthorizationGrant.password(IDS_USERNAME, userPassword));
|
IdentityServiceFacade.AuthorizationGrant.password(IDS_USERNAME, userPassword));
|
||||||
|
|
||||||
Optional<OIDCUserInfo> userInfoOptional = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
Optional<OIDCUserInfo> userInfoOptional = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
||||||
accessTokenAuthorization.getAccessToken().getTokenValue());
|
accessTokenAuthorization.getAccessToken().getTokenValue());
|
||||||
|
|
||||||
NodeRef person = personService.getPerson(IDS_USERNAME);
|
NodeRef person = personService.getPerson(IDS_USERNAME);
|
||||||
|
|
||||||
@@ -125,23 +124,26 @@ public class IdentityServiceJITProvisioningHandlerTest extends BaseSpringTest
|
|||||||
assertFalse(personService.personExists(IDS_USERNAME));
|
assertFalse(personService.personExists(IDS_USERNAME));
|
||||||
|
|
||||||
String principalAttribute = isAuth0Enabled ? PersonClaims.NICKNAME_CLAIM_NAME : PersonClaims.PREFERRED_USERNAME_CLAIM_NAME;
|
String principalAttribute = isAuth0Enabled ? PersonClaims.NICKNAME_CLAIM_NAME : PersonClaims.PREFERRED_USERNAME_CLAIM_NAME;
|
||||||
IdentityServiceFacade.AccessTokenAuthorization accessTokenAuthorization =
|
IdentityServiceFacade.AccessTokenAuthorization accessTokenAuthorization = identityServiceFacade.authorize(
|
||||||
identityServiceFacade.authorize(
|
|
||||||
IdentityServiceFacade.AuthorizationGrant.password(IDS_USERNAME, userPassword));
|
IdentityServiceFacade.AuthorizationGrant.password(IDS_USERNAME, userPassword));
|
||||||
|
UserInfoAttrMapping userInfoAttrMapping = new UserInfoAttrMapping(principalAttribute, "given_name", "family_name", "email");
|
||||||
|
|
||||||
String accessToken = accessTokenAuthorization.getAccessToken().getTokenValue();
|
String accessToken = accessTokenAuthorization.getAccessToken().getTokenValue();
|
||||||
|
ClientRegistration clientRegistration = mock(ClientRegistration.class, RETURNS_DEEP_STUBS);
|
||||||
|
when(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()).thenReturn(principalAttribute);
|
||||||
IdentityServiceFacade idsServiceFacadeMock = mock(IdentityServiceFacade.class);
|
IdentityServiceFacade idsServiceFacadeMock = mock(IdentityServiceFacade.class);
|
||||||
when(idsServiceFacadeMock.decodeToken(accessToken)).thenReturn(null);
|
when(idsServiceFacadeMock.decodeToken(accessToken)).thenReturn(null);
|
||||||
when(idsServiceFacadeMock.getUserInfo(accessToken, principalAttribute)).thenReturn(identityServiceFacade.getUserInfo(accessToken, principalAttribute));
|
when(idsServiceFacadeMock.getUserInfo(accessToken, userInfoAttrMapping)).thenReturn(identityServiceFacade.getUserInfo(accessToken, userInfoAttrMapping));
|
||||||
|
when(idsServiceFacadeMock.getClientRegistration()).thenReturn(clientRegistration);
|
||||||
|
|
||||||
// Replace the original facade with a mocked one to prevent user information from being extracted from the access token.
|
// Replace the original facade with a mocked one to prevent user information from being extracted from the access token.
|
||||||
Field declaredField = jitProvisioningHandler.getClass()
|
Field declaredField = jitProvisioningHandler.getClass()
|
||||||
.getDeclaredField("identityServiceFacade");
|
.getDeclaredField("identityServiceFacade");
|
||||||
declaredField.setAccessible(true);
|
declaredField.setAccessible(true);
|
||||||
declaredField.set(jitProvisioningHandler, idsServiceFacadeMock);
|
declaredField.set(jitProvisioningHandler, idsServiceFacadeMock);
|
||||||
|
|
||||||
Optional<OIDCUserInfo> userInfoOptional = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
Optional<OIDCUserInfo> userInfoOptional = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
||||||
accessToken);
|
accessToken);
|
||||||
|
|
||||||
declaredField.set(jitProvisioningHandler, identityServiceFacade);
|
declaredField.set(jitProvisioningHandler, identityServiceFacade);
|
||||||
|
|
||||||
@@ -153,7 +155,7 @@ public class IdentityServiceJITProvisioningHandlerTest extends BaseSpringTest
|
|||||||
assertEquals("johndoe123@alfresco.com", userInfoOptional.get().email());
|
assertEquals("johndoe123@alfresco.com", userInfoOptional.get().email());
|
||||||
assertEquals("johndoe123@alfresco.com", nodeService.getProperty(person, ContentModel.PROP_EMAIL));
|
assertEquals("johndoe123@alfresco.com", nodeService.getProperty(person, ContentModel.PROP_EMAIL));
|
||||||
verify(idsServiceFacadeMock).decodeToken(accessToken);
|
verify(idsServiceFacadeMock).decodeToken(accessToken);
|
||||||
verify(idsServiceFacadeMock, atLeast(1)).getUserInfo(accessToken, principalAttribute);
|
verify(idsServiceFacadeMock, atLeast(1)).getUserInfo(accessToken, userInfoAttrMapping);
|
||||||
if (!isAuth0Enabled)
|
if (!isAuth0Enabled)
|
||||||
{
|
{
|
||||||
assertEquals("John", userInfoOptional.get().firstName());
|
assertEquals("John", userInfoOptional.get().firstName());
|
||||||
@@ -166,16 +168,15 @@ public class IdentityServiceJITProvisioningHandlerTest extends BaseSpringTest
|
|||||||
@After
|
@After
|
||||||
public void tearDown()
|
public void tearDown()
|
||||||
{
|
{
|
||||||
AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Void>()
|
AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Void>() {
|
||||||
{
|
|
||||||
@Override
|
@Override
|
||||||
public Void doWork() throws Exception
|
public Void doWork() throws Exception
|
||||||
{
|
{
|
||||||
transactionService.getRetryingTransactionHelper()
|
transactionService.getRetryingTransactionHelper()
|
||||||
.doInTransaction((RetryingTransactionCallback<Void>) () -> {
|
.doInTransaction((RetryingTransactionCallback<Void>) () -> {
|
||||||
personService.deletePerson(IDS_USERNAME);
|
personService.deletePerson(IDS_USERNAME);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -38,12 +38,17 @@ import static org.mockito.MockitoAnnotations.initMocks;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.nimbusds.openid.connect.sdk.claims.PersonClaims;
|
import com.nimbusds.openid.connect.sdk.claims.PersonClaims;
|
||||||
|
|
||||||
import org.alfresco.service.cmr.security.PersonService;
|
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mockito.Answers;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
|
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.DecodedTokenUser;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.OIDCUserInfo;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.UserInfoAttrMapping;
|
||||||
|
import org.alfresco.service.cmr.security.PersonService;
|
||||||
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
|
|
||||||
public class IdentityServiceJITProvisioningHandlerUnitTest
|
public class IdentityServiceJITProvisioningHandlerUnitTest
|
||||||
{
|
{
|
||||||
@@ -51,6 +56,9 @@ public class IdentityServiceJITProvisioningHandlerUnitTest
|
|||||||
@Mock
|
@Mock
|
||||||
private IdentityServiceFacade identityServiceFacade;
|
private IdentityServiceFacade identityServiceFacade;
|
||||||
|
|
||||||
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
|
private ClientRegistration clientRegistration;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private PersonService personService;
|
private PersonService personService;
|
||||||
|
|
||||||
@@ -64,11 +72,22 @@ public class IdentityServiceJITProvisioningHandlerUnitTest
|
|||||||
private IdentityServiceConfig identityServiceConfig;
|
private IdentityServiceConfig identityServiceConfig;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private OIDCUserInfo userInfo;
|
private DecodedTokenUser decodedTokenUser;
|
||||||
|
|
||||||
private IdentityServiceJITProvisioningHandler jitProvisioningHandler;
|
private IdentityServiceJITProvisioningHandler jitProvisioningHandler;
|
||||||
|
|
||||||
|
private UserInfoAttrMapping expectedMapping;
|
||||||
|
|
||||||
private static final String JWT_TOKEN = "myToken";
|
private static final String JWT_TOKEN = "myToken";
|
||||||
|
private static final String USERNAME = "johny123";
|
||||||
|
private static final String FIRST_NAME = "John";
|
||||||
|
private static final String LAST_NAME = "Doe";
|
||||||
|
private static final String EMAIL = "johny123@email.com";
|
||||||
|
|
||||||
|
public static final String USERNAME_CLAIM = "nickname";
|
||||||
|
public static final String EMAIL_CLAIM = "email";
|
||||||
|
public static final String FIRST_NAME_CLAIM = "given_name";
|
||||||
|
public static final String LAST_NAME_CLAIM = "family_name";
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup()
|
public void setup()
|
||||||
@@ -78,149 +97,147 @@ public class IdentityServiceJITProvisioningHandlerUnitTest
|
|||||||
when(transactionService.isReadOnly()).thenReturn(false);
|
when(transactionService.isReadOnly()).thenReturn(false);
|
||||||
when(identityServiceFacade.decodeToken(JWT_TOKEN)).thenReturn(decodedAccessToken);
|
when(identityServiceFacade.decodeToken(JWT_TOKEN)).thenReturn(decodedAccessToken);
|
||||||
when(personService.createMissingPeople()).thenReturn(true);
|
when(personService.createMissingPeople()).thenReturn(true);
|
||||||
jitProvisioningHandler = new IdentityServiceJITProvisioningHandler(identityServiceFacade,
|
when(identityServiceFacade.getClientRegistration()).thenReturn(clientRegistration);
|
||||||
personService, transactionService, identityServiceConfig);
|
when(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()).thenReturn(USERNAME_CLAIM);
|
||||||
|
when(identityServiceConfig.getEmailAttribute()).thenReturn(EMAIL_CLAIM);
|
||||||
|
when(identityServiceConfig.getFirstNameAttribute()).thenReturn(FIRST_NAME_CLAIM);
|
||||||
|
when(identityServiceConfig.getLastNameAttribute()).thenReturn(LAST_NAME_CLAIM);
|
||||||
|
expectedMapping = new UserInfoAttrMapping(USERNAME_CLAIM, FIRST_NAME_CLAIM, LAST_NAME_CLAIM, EMAIL_CLAIM);
|
||||||
|
jitProvisioningHandler = new IdentityServiceJITProvisioningHandler(identityServiceFacade, personService, transactionService, identityServiceConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldExtractUserInfoForExistingUser()
|
public void shouldExtractUserInfoForExistingUser()
|
||||||
{
|
{
|
||||||
when(personService.personExists("johny123")).thenReturn(true);
|
when(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()).thenReturn(PersonClaims.PREFERRED_USERNAME_CLAIM_NAME);
|
||||||
when(decodedAccessToken.getClaim(PersonClaims.PREFERRED_USERNAME_CLAIM_NAME)).thenReturn("johny123");
|
when(personService.personExists(USERNAME)).thenReturn(true);
|
||||||
|
when(decodedAccessToken.getClaim(PersonClaims.PREFERRED_USERNAME_CLAIM_NAME)).thenReturn(USERNAME);
|
||||||
|
|
||||||
|
jitProvisioningHandler = new IdentityServiceJITProvisioningHandler(identityServiceFacade, personService, transactionService, identityServiceConfig);
|
||||||
Optional<OIDCUserInfo> result = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
Optional<OIDCUserInfo> result = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
||||||
JWT_TOKEN);
|
JWT_TOKEN);
|
||||||
|
|
||||||
assertTrue(result.isPresent());
|
assertTrue(result.isPresent());
|
||||||
assertEquals("johny123", result.get().username());
|
assertEquals(USERNAME, result.get().username());
|
||||||
assertFalse(result.get().allFieldsNotEmpty());
|
assertFalse(result.get().allFieldsNotEmpty());
|
||||||
verify(identityServiceFacade, never()).getUserInfo(JWT_TOKEN, PersonClaims.PREFERRED_USERNAME_CLAIM_NAME);
|
verify(identityServiceFacade, never()).getUserInfo(JWT_TOKEN, expectedMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldExtractUserInfoForExistingUserWithProviderPrincipalAttribute()
|
public void shouldExtractUserInfoForExistingUserWithProviderPrincipalAttribute()
|
||||||
{
|
{
|
||||||
when(identityServiceConfig.getPrincipalAttribute()).thenReturn("nickname");
|
when(identityServiceConfig.getPrincipalAttribute()).thenReturn(USERNAME_CLAIM);
|
||||||
when(personService.personExists("johny123")).thenReturn(true);
|
when(personService.personExists(USERNAME)).thenReturn(true);
|
||||||
when(decodedAccessToken.getClaim("nickname")).thenReturn("johny123");
|
when(decodedAccessToken.getClaim(USERNAME_CLAIM)).thenReturn(USERNAME);
|
||||||
|
|
||||||
Optional<OIDCUserInfo> result = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
Optional<OIDCUserInfo> result = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
||||||
JWT_TOKEN);
|
JWT_TOKEN);
|
||||||
|
|
||||||
assertTrue(result.isPresent());
|
assertTrue(result.isPresent());
|
||||||
assertEquals("johny123", result.get().username());
|
assertEquals(USERNAME, result.get().username());
|
||||||
assertFalse(result.get().allFieldsNotEmpty());
|
assertFalse(result.get().allFieldsNotEmpty());
|
||||||
verify(identityServiceFacade, never()).getUserInfo(JWT_TOKEN, "nickname");
|
verify(identityServiceFacade, never()).getUserInfo(JWT_TOKEN, expectedMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldExtractUserInfoFromAccessTokenAndCreateUser()
|
public void shouldExtractUserInfoFromAccessTokenAndCreateUser()
|
||||||
{
|
{
|
||||||
when(personService.personExists("johny123")).thenReturn(false);
|
when(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()).thenReturn(PersonClaims.PREFERRED_USERNAME_CLAIM_NAME);
|
||||||
|
when(personService.personExists(USERNAME)).thenReturn(false);
|
||||||
when(decodedAccessToken.getClaim(PersonClaims.PREFERRED_USERNAME_CLAIM_NAME)).thenReturn("johny123");
|
when(decodedAccessToken.getClaim(PersonClaims.PREFERRED_USERNAME_CLAIM_NAME)).thenReturn(USERNAME);
|
||||||
when(decodedAccessToken.getClaim(PersonClaims.GIVEN_NAME_CLAIM_NAME)).thenReturn("John");
|
when(decodedAccessToken.getClaim(PersonClaims.GIVEN_NAME_CLAIM_NAME)).thenReturn(FIRST_NAME);
|
||||||
when(decodedAccessToken.getClaim(PersonClaims.FAMILY_NAME_CLAIM_NAME)).thenReturn("Doe");
|
when(decodedAccessToken.getClaim(PersonClaims.FAMILY_NAME_CLAIM_NAME)).thenReturn(LAST_NAME);
|
||||||
when(decodedAccessToken.getClaim(PersonClaims.EMAIL_CLAIM_NAME)).thenReturn("johny123@email.com");
|
when(decodedAccessToken.getClaim(PersonClaims.EMAIL_CLAIM_NAME)).thenReturn(EMAIL);
|
||||||
|
|
||||||
|
jitProvisioningHandler = new IdentityServiceJITProvisioningHandler(identityServiceFacade, personService, transactionService, identityServiceConfig);
|
||||||
Optional<OIDCUserInfo> result = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
Optional<OIDCUserInfo> result = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
||||||
JWT_TOKEN);
|
JWT_TOKEN);
|
||||||
|
|
||||||
assertTrue(result.isPresent());
|
assertTrue(result.isPresent());
|
||||||
assertEquals("johny123", result.get().username());
|
assertEquals(USERNAME, result.get().username());
|
||||||
assertEquals("John", result.get().firstName());
|
assertEquals(FIRST_NAME, result.get().firstName());
|
||||||
assertEquals("Doe", result.get().lastName());
|
assertEquals(LAST_NAME, result.get().lastName());
|
||||||
assertEquals("johny123@email.com", result.get().email());
|
assertEquals(EMAIL, result.get().email());
|
||||||
assertTrue(result.get().allFieldsNotEmpty());
|
assertTrue(result.get().allFieldsNotEmpty());
|
||||||
verify(personService).createPerson(any());
|
verify(personService).createPerson(any());
|
||||||
verify(identityServiceFacade, never()).getUserInfo(JWT_TOKEN, PersonClaims.PREFERRED_USERNAME_CLAIM_NAME);
|
verify(identityServiceFacade, never()).getUserInfo(JWT_TOKEN, expectedMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldExtractUserInfoFromUserInfoEndpointAndCreateUser()
|
public void shouldExtractUserInfoFromUserInfoEndpointAndCreateUser()
|
||||||
{
|
{
|
||||||
when(userInfo.username()).thenReturn("johny123");
|
when(decodedTokenUser.username()).thenReturn(USERNAME);
|
||||||
when(userInfo.firstName()).thenReturn("John");
|
when(decodedTokenUser.firstName()).thenReturn(FIRST_NAME);
|
||||||
when(userInfo.lastName()).thenReturn("Doe");
|
when(decodedTokenUser.lastName()).thenReturn(LAST_NAME);
|
||||||
when(userInfo.email()).thenReturn("johny123@email.com");
|
when(decodedTokenUser.email()).thenReturn(EMAIL);
|
||||||
|
when(personService.personExists(USERNAME)).thenReturn(false);
|
||||||
when(personService.personExists("johny123")).thenReturn(false);
|
when(decodedAccessToken.getClaim(PersonClaims.PREFERRED_USERNAME_CLAIM_NAME)).thenReturn(USERNAME);
|
||||||
|
when(identityServiceFacade.getUserInfo(JWT_TOKEN, expectedMapping)).thenReturn(Optional.of(decodedTokenUser));
|
||||||
when(decodedAccessToken.getClaim(PersonClaims.PREFERRED_USERNAME_CLAIM_NAME)).thenReturn("johny123");
|
|
||||||
when(identityServiceFacade.getUserInfo(JWT_TOKEN, PersonClaims.PREFERRED_USERNAME_CLAIM_NAME)).thenReturn(Optional.of(userInfo));
|
|
||||||
|
|
||||||
Optional<OIDCUserInfo> result = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
Optional<OIDCUserInfo> result = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
||||||
JWT_TOKEN);
|
JWT_TOKEN);
|
||||||
|
|
||||||
assertTrue(result.isPresent());
|
assertTrue(result.isPresent());
|
||||||
assertEquals("johny123", result.get().username());
|
assertEquals(USERNAME, result.get().username());
|
||||||
assertEquals("John", result.get().firstName());
|
assertEquals(FIRST_NAME, result.get().firstName());
|
||||||
assertEquals("Doe", result.get().lastName());
|
assertEquals(LAST_NAME, result.get().lastName());
|
||||||
assertEquals("johny123@email.com", result.get().email());
|
assertEquals(EMAIL, result.get().email());
|
||||||
assertTrue(result.get().allFieldsNotEmpty());
|
assertTrue(result.get().allFieldsNotEmpty());
|
||||||
verify(personService).createPerson(any());
|
verify(personService).createPerson(any());
|
||||||
verify(identityServiceFacade).getUserInfo(JWT_TOKEN, PersonClaims.PREFERRED_USERNAME_CLAIM_NAME);
|
verify(identityServiceFacade).getUserInfo(JWT_TOKEN, expectedMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldReturnEmptyOptionalIfUsernameNotExtracted()
|
public void shouldReturnEmptyOptionalIfUsernameNotExtracted()
|
||||||
{
|
{
|
||||||
|
when(identityServiceFacade.getUserInfo(JWT_TOKEN, expectedMapping)).thenReturn(Optional.of(decodedTokenUser));
|
||||||
when(identityServiceFacade.getUserInfo(JWT_TOKEN, PersonClaims.PREFERRED_USERNAME_CLAIM_NAME)).thenReturn(Optional.of(userInfo));
|
|
||||||
|
|
||||||
Optional<OIDCUserInfo> result = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
Optional<OIDCUserInfo> result = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
||||||
JWT_TOKEN);
|
JWT_TOKEN);
|
||||||
|
|
||||||
assertFalse(result.isPresent());
|
assertFalse(result.isPresent());
|
||||||
verify(personService, never()).createPerson(any());
|
verify(personService, never()).createPerson(any());
|
||||||
verify(identityServiceFacade).getUserInfo(JWT_TOKEN, PersonClaims.PREFERRED_USERNAME_CLAIM_NAME);
|
verify(identityServiceFacade).getUserInfo(JWT_TOKEN, expectedMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldCallUserInfoEndpointToGetUsername()
|
public void shouldCallUserInfoEndpointToGetUsername()
|
||||||
{
|
{
|
||||||
when(personService.personExists("johny123")).thenReturn(true);
|
when(personService.personExists(USERNAME)).thenReturn(true);
|
||||||
|
|
||||||
when(decodedAccessToken.getClaim(PersonClaims.PREFERRED_USERNAME_CLAIM_NAME)).thenReturn("");
|
when(decodedAccessToken.getClaim(PersonClaims.PREFERRED_USERNAME_CLAIM_NAME)).thenReturn("");
|
||||||
|
when(identityServiceFacade.getUserInfo(JWT_TOKEN, expectedMapping)).thenReturn(Optional.of(DecodedTokenUser.validateAndCreate(USERNAME, null, null, null)));
|
||||||
when(userInfo.username()).thenReturn("johny123");
|
|
||||||
when(identityServiceFacade.getUserInfo(JWT_TOKEN, PersonClaims.PREFERRED_USERNAME_CLAIM_NAME)).thenReturn(Optional.of(userInfo));
|
|
||||||
|
|
||||||
Optional<OIDCUserInfo> result = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
Optional<OIDCUserInfo> result = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
||||||
JWT_TOKEN);
|
JWT_TOKEN);
|
||||||
|
|
||||||
assertTrue(result.isPresent());
|
assertTrue(result.isPresent());
|
||||||
assertEquals("johny123", result.get().username());
|
assertEquals(USERNAME, result.get().username());
|
||||||
assertEquals("", result.get().firstName());
|
assertEquals("", result.get().firstName());
|
||||||
assertEquals("", result.get().lastName());
|
assertEquals("", result.get().lastName());
|
||||||
assertEquals("", result.get().email());
|
assertEquals("", result.get().email());
|
||||||
assertFalse(result.get().allFieldsNotEmpty());
|
assertFalse(result.get().allFieldsNotEmpty());
|
||||||
verify(personService, never()).createPerson(any());
|
verify(personService, never()).createPerson(any());
|
||||||
verify(identityServiceFacade).getUserInfo(JWT_TOKEN, PersonClaims.PREFERRED_USERNAME_CLAIM_NAME);
|
verify(identityServiceFacade).getUserInfo(JWT_TOKEN, expectedMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldCallUserInfoEndpointToGetUsernameWithProvidedPrincipalAttribute()
|
public void shouldCallUserInfoEndpointToGetUsernameWithProvidedPrincipalAttribute()
|
||||||
{
|
{
|
||||||
when(identityServiceConfig.getPrincipalAttribute()).thenReturn("nickname");
|
when(identityServiceConfig.getPrincipalAttribute()).thenReturn(USERNAME_CLAIM);
|
||||||
when(personService.personExists("johny123")).thenReturn(true);
|
when(personService.personExists(USERNAME)).thenReturn(true);
|
||||||
|
when(decodedAccessToken.getClaim(USERNAME_CLAIM)).thenReturn("");
|
||||||
when(decodedAccessToken.getClaim("nickname")).thenReturn("");
|
when(identityServiceFacade.getUserInfo(JWT_TOKEN, expectedMapping)).thenReturn(Optional.of(DecodedTokenUser.validateAndCreate(USERNAME, null, null, null)));
|
||||||
|
|
||||||
when(userInfo.username()).thenReturn("johny123");
|
|
||||||
when(identityServiceFacade.getUserInfo(JWT_TOKEN, "nickname")).thenReturn(Optional.of(userInfo));
|
|
||||||
|
|
||||||
Optional<OIDCUserInfo> result = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
Optional<OIDCUserInfo> result = jitProvisioningHandler.extractUserInfoAndCreateUserIfNeeded(
|
||||||
JWT_TOKEN);
|
JWT_TOKEN);
|
||||||
|
|
||||||
assertTrue(result.isPresent());
|
assertTrue(result.isPresent());
|
||||||
assertEquals("johny123", result.get().username());
|
assertEquals(USERNAME, result.get().username());
|
||||||
assertEquals("", result.get().firstName());
|
assertEquals("", result.get().firstName());
|
||||||
assertEquals("", result.get().lastName());
|
assertEquals("", result.get().lastName());
|
||||||
assertEquals("", result.get().email());
|
assertEquals("", result.get().email());
|
||||||
assertFalse(result.get().allFieldsNotEmpty());
|
assertFalse(result.get().allFieldsNotEmpty());
|
||||||
verify(personService, never()).createPerson(any());
|
verify(personService, never()).createPerson(any());
|
||||||
verify(identityServiceFacade).getUserInfo(JWT_TOKEN, "nickname");
|
verify(identityServiceFacade).getUserInfo(JWT_TOKEN, expectedMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -232,8 +249,8 @@ public class IdentityServiceJITProvisioningHandlerUnitTest
|
|||||||
verify(personService, never()).createPerson(any());
|
verify(personService, never()).createPerson(any());
|
||||||
verify(identityServiceFacade, never()).decodeToken(null);
|
verify(identityServiceFacade, never()).decodeToken(null);
|
||||||
verify(identityServiceFacade, never()).decodeToken("");
|
verify(identityServiceFacade, never()).decodeToken("");
|
||||||
verify(identityServiceFacade, never()).getUserInfo(null, PersonClaims.PREFERRED_USERNAME_CLAIM_NAME);
|
verify(identityServiceFacade, never()).getUserInfo(null, expectedMapping);
|
||||||
verify(identityServiceFacade, never()).getUserInfo("", PersonClaims.PREFERRED_USERNAME_CLAIM_NAME);
|
verify(identityServiceFacade, never()).getUserInfo(null, expectedMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -34,17 +34,18 @@ import java.time.Instant;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import com.nimbusds.openid.connect.sdk.claims.PersonClaims;
|
import com.nimbusds.openid.connect.sdk.claims.PersonClaims;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
||||||
|
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationException;
|
import org.alfresco.repo.security.authentication.AuthenticationException;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.DecodedAccessToken;
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.DecodedAccessToken;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.TokenDecodingException;
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.TokenDecodingException;
|
||||||
import org.alfresco.service.cmr.security.PersonService;
|
import org.alfresco.service.cmr.security.PersonService;
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the Identity Service based authentication subsystem.
|
* Tests the Identity Service based authentication subsystem.
|
||||||
@@ -68,7 +69,9 @@ public class IdentityServiceRemoteUserMapperTest extends TestCase
|
|||||||
|
|
||||||
public void testWrongTokenWithSilentValidation()
|
public void testWrongTokenWithSilentValidation()
|
||||||
{
|
{
|
||||||
final IdentityServiceRemoteUserMapper mapper = givenMapper(Map.of("WrOnG-ToKeN", () -> {throw new TokenDecodingException("Expected ");}));
|
final IdentityServiceRemoteUserMapper mapper = givenMapper(Map.of("WrOnG-ToKeN", () -> {
|
||||||
|
throw new TokenDecodingException("Expected ");
|
||||||
|
}));
|
||||||
mapper.setValidationFailureSilent(true);
|
mapper.setValidationFailureSilent(true);
|
||||||
|
|
||||||
HttpServletRequest mockRequest = createMockTokenRequest("WrOnG-ToKeN");
|
HttpServletRequest mockRequest = createMockTokenRequest("WrOnG-ToKeN");
|
||||||
@@ -79,7 +82,9 @@ public class IdentityServiceRemoteUserMapperTest extends TestCase
|
|||||||
|
|
||||||
public void testWrongTokenWithoutSilentValidation()
|
public void testWrongTokenWithoutSilentValidation()
|
||||||
{
|
{
|
||||||
final IdentityServiceRemoteUserMapper mapper = givenMapper(Map.of("WrOnG-ToKeN", () -> {throw new TokenDecodingException("Expected");}));
|
final IdentityServiceRemoteUserMapper mapper = givenMapper(Map.of("WrOnG-ToKeN", () -> {
|
||||||
|
throw new TokenDecodingException("Expected");
|
||||||
|
}));
|
||||||
mapper.setValidationFailureSilent(false);
|
mapper.setValidationFailureSilent(false);
|
||||||
|
|
||||||
HttpServletRequest mockRequest = createMockTokenRequest("WrOnG-ToKeN");
|
HttpServletRequest mockRequest = createMockTokenRequest("WrOnG-ToKeN");
|
||||||
@@ -92,12 +97,14 @@ public class IdentityServiceRemoteUserMapperTest extends TestCase
|
|||||||
private IdentityServiceRemoteUserMapper givenMapper(Map<String, Supplier<String>> tokenToUser)
|
private IdentityServiceRemoteUserMapper givenMapper(Map<String, Supplier<String>> tokenToUser)
|
||||||
{
|
{
|
||||||
final TransactionService transactionService = mock(TransactionService.class);
|
final TransactionService transactionService = mock(TransactionService.class);
|
||||||
final IdentityServiceFacade facade = mock(IdentityServiceFacade.class);
|
final IdentityServiceFacade facade = mock(IdentityServiceFacade.class, Mockito.RETURNS_DEEP_STUBS);
|
||||||
final PersonService personService = mock(PersonService.class);
|
final PersonService personService = mock(PersonService.class);
|
||||||
final IdentityServiceConfig identityServiceConfig = mock(IdentityServiceConfig.class);
|
final IdentityServiceConfig identityServiceConfig = mock(IdentityServiceConfig.class);
|
||||||
when(transactionService.isReadOnly()).thenReturn(true);
|
when(transactionService.isReadOnly()).thenReturn(true);
|
||||||
when(facade.decodeToken(anyString()))
|
when(facade.decodeToken(anyString()))
|
||||||
.thenAnswer(i -> new TestDecodedToken(tokenToUser.get(i.getArgument(0, String.class))));
|
.thenAnswer(i -> new TestDecodedToken(tokenToUser.get(i.getArgument(0, String.class))));
|
||||||
|
when(facade.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName())
|
||||||
|
.thenReturn(PersonClaims.PREFERRED_USERNAME_CLAIM_NAME);
|
||||||
|
|
||||||
when(personService.getUserIdentifier(anyString())).thenAnswer(i -> i.getArgument(0, String.class));
|
when(personService.getUserIdentifier(anyString())).thenAnswer(i -> i.getArgument(0, String.class));
|
||||||
|
|
||||||
@@ -108,14 +115,14 @@ public class IdentityServiceRemoteUserMapperTest extends TestCase
|
|||||||
mapper.setActive(true);
|
mapper.setActive(true);
|
||||||
mapper.setBearerTokenResolver(new DefaultBearerTokenResolver());
|
mapper.setBearerTokenResolver(new DefaultBearerTokenResolver());
|
||||||
|
|
||||||
|
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility method for creating a mocked Servlet request with a token.
|
* Utility method for creating a mocked Servlet request with a token.
|
||||||
*
|
*
|
||||||
* @param token The token to add to the Authorization header
|
* @param token
|
||||||
|
* The token to add to the Authorization header
|
||||||
* @return The mocked request object
|
* @return The mocked request object
|
||||||
*/
|
*/
|
||||||
private HttpServletRequest createMockTokenRequest(String token)
|
private HttpServletRequest createMockTokenRequest(String token)
|
||||||
|
@@ -31,20 +31,23 @@ import static org.mockito.ArgumentMatchers.any;
|
|||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationException;
|
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationGrant;
|
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.TokenDecodingException;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||||
import org.springframework.web.client.RestOperations;
|
import org.springframework.web.client.RestOperations;
|
||||||
|
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationException;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationGrant;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.TokenDecodingException;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.user.UserInfoAttrMapping;
|
||||||
|
|
||||||
public class SpringBasedIdentityServiceFacadeUnitTest
|
public class SpringBasedIdentityServiceFacadeUnitTest
|
||||||
{
|
{
|
||||||
private static final String USER_NAME = "user";
|
private static final String USER_NAME = "user";
|
||||||
private static final String PASSWORD = "password";
|
private static final String PASSWORD = "password";
|
||||||
private static final String TOKEN = "tEsT-tOkEn";
|
private static final String TOKEN = "tEsT-tOkEn";
|
||||||
|
private static final UserInfoAttrMapping USER_INFO_ATTR_MAPPING = new UserInfoAttrMapping("preferred_username", "given_name", "family_name", "email");
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldThrowVerificationExceptionOnFailure()
|
public void shouldThrowVerificationExceptionOnFailure()
|
||||||
@@ -74,7 +77,6 @@ public class SpringBasedIdentityServiceFacadeUnitTest
|
|||||||
.havingCause().withNoCause().withMessage("Expected");
|
.havingCause().withNoCause().withMessage("Expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldReturnEmptyOptionalOnFailure()
|
public void shouldReturnEmptyOptionalOnFailure()
|
||||||
{
|
{
|
||||||
@@ -82,17 +84,16 @@ public class SpringBasedIdentityServiceFacadeUnitTest
|
|||||||
final JwtDecoder jwtDecoder = mock(JwtDecoder.class);
|
final JwtDecoder jwtDecoder = mock(JwtDecoder.class);
|
||||||
final SpringBasedIdentityServiceFacade facade = new SpringBasedIdentityServiceFacade(restOperations, testRegistration(), jwtDecoder);
|
final SpringBasedIdentityServiceFacade facade = new SpringBasedIdentityServiceFacade(restOperations, testRegistration(), jwtDecoder);
|
||||||
|
|
||||||
|
assertThat(facade.getUserInfo(TOKEN, USER_INFO_ATTR_MAPPING).isEmpty()).isTrue();
|
||||||
assertThat(facade.getUserInfo(TOKEN, "preferred_username").isEmpty()).isTrue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClientRegistration testRegistration()
|
private ClientRegistration testRegistration()
|
||||||
{
|
{
|
||||||
return ClientRegistration.withRegistrationId("test")
|
return ClientRegistration.withRegistrationId("test")
|
||||||
.tokenUri("http://localhost")
|
.tokenUri("http://localhost")
|
||||||
.clientId("test")
|
.clientId("test")
|
||||||
.userInfoUri("http://localhost/userinfo")
|
.userInfoUri("http://localhost/userinfo")
|
||||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -38,18 +38,11 @@ import java.io.IOException;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import com.nimbusds.oauth2.sdk.Scope;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.alfresco.repo.security.authentication.external.RemoteUserMapper;
|
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceConfig;
|
import com.nimbusds.oauth2.sdk.Scope;
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade;
|
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AccessToken;
|
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AccessTokenAuthorization;
|
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationException;
|
|
||||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationGrant;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
@@ -58,6 +51,14 @@ import org.mockito.Mock;
|
|||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
|
||||||
|
|
||||||
|
import org.alfresco.repo.security.authentication.external.RemoteUserMapper;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceConfig;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AccessToken;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AccessTokenAuthorization;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationException;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationGrant;
|
||||||
|
|
||||||
@SuppressWarnings("PMD.AvoidStringBufferField")
|
@SuppressWarnings("PMD.AvoidStringBufferField")
|
||||||
public class IdentityServiceAdminConsoleAuthenticatorUnitTest
|
public class IdentityServiceAdminConsoleAuthenticatorUnitTest
|
||||||
{
|
{
|
||||||
@@ -118,7 +119,7 @@ public class IdentityServiceAdminConsoleAuthenticatorUnitTest
|
|||||||
{
|
{
|
||||||
when(cookiesService.getCookie(ALFRESCO_ACCESS_TOKEN, request)).thenReturn("JWT_TOKEN");
|
when(cookiesService.getCookie(ALFRESCO_ACCESS_TOKEN, request)).thenReturn("JWT_TOKEN");
|
||||||
when(cookiesService.getCookie(ALFRESCO_TOKEN_EXPIRATION, request)).thenReturn(
|
when(cookiesService.getCookie(ALFRESCO_TOKEN_EXPIRATION, request)).thenReturn(
|
||||||
String.valueOf(Instant.now().plusSeconds(60).toEpochMilli()));
|
String.valueOf(Instant.now().plusSeconds(60).toEpochMilli()));
|
||||||
when(remoteUserMapper.getRemoteUser(requestCaptor.capture())).thenReturn("admin");
|
when(remoteUserMapper.getRemoteUser(requestCaptor.capture())).thenReturn("admin");
|
||||||
|
|
||||||
String username = authenticator.getAdminConsoleUser(request, response);
|
String username = authenticator.getAdminConsoleUser(request, response);
|
||||||
@@ -134,7 +135,7 @@ public class IdentityServiceAdminConsoleAuthenticatorUnitTest
|
|||||||
when(cookiesService.getCookie(ALFRESCO_ACCESS_TOKEN, request)).thenReturn("EXPIRED_JWT_TOKEN");
|
when(cookiesService.getCookie(ALFRESCO_ACCESS_TOKEN, request)).thenReturn("EXPIRED_JWT_TOKEN");
|
||||||
when(cookiesService.getCookie(ALFRESCO_REFRESH_TOKEN, request)).thenReturn("REFRESH_TOKEN");
|
when(cookiesService.getCookie(ALFRESCO_REFRESH_TOKEN, request)).thenReturn("REFRESH_TOKEN");
|
||||||
when(cookiesService.getCookie(ALFRESCO_TOKEN_EXPIRATION, request)).thenReturn(
|
when(cookiesService.getCookie(ALFRESCO_TOKEN_EXPIRATION, request)).thenReturn(
|
||||||
String.valueOf(Instant.now().minusSeconds(60).toEpochMilli()));
|
String.valueOf(Instant.now().minusSeconds(60).toEpochMilli()));
|
||||||
when(accessToken.getTokenValue()).thenReturn("REFRESHED_JWT_TOKEN");
|
when(accessToken.getTokenValue()).thenReturn("REFRESHED_JWT_TOKEN");
|
||||||
when(accessToken.getExpiresAt()).thenReturn(Instant.now().plusSeconds(60));
|
when(accessToken.getExpiresAt()).thenReturn(Instant.now().plusSeconds(60));
|
||||||
when(accessTokenAuthorization.getAccessToken()).thenReturn(accessToken);
|
when(accessTokenAuthorization.getAccessToken()).thenReturn(accessToken);
|
||||||
@@ -155,10 +156,11 @@ public class IdentityServiceAdminConsoleAuthenticatorUnitTest
|
|||||||
{
|
{
|
||||||
String redirectPath = "/alfresco/s/admin/admin-communitysummary";
|
String redirectPath = "/alfresco/s/admin/admin-communitysummary";
|
||||||
|
|
||||||
|
when(identityServiceConfig.getAdminConsoleScopes()).thenReturn(Set.of("openid", "email", "profile", "offline_access"));
|
||||||
when(identityServiceConfig.getAdminConsoleRedirectPath()).thenReturn("/alfresco/s/admin/admin-communitysummary");
|
when(identityServiceConfig.getAdminConsoleRedirectPath()).thenReturn("/alfresco/s/admin/admin-communitysummary");
|
||||||
ArgumentCaptor<String> authenticationRequest = ArgumentCaptor.forClass(String.class);
|
ArgumentCaptor<String> authenticationRequest = ArgumentCaptor.forClass(String.class);
|
||||||
String expectedUri = "http://localhost:8999/auth?client_id=alfresco&redirect_uri=%s%s&response_type=code&scope="
|
String expectedUri = "http://localhost:8999/auth?client_id=alfresco&redirect_uri=%s%s&response_type=code&scope="
|
||||||
.formatted("http://localhost:8080", redirectPath);
|
.formatted("http://localhost:8080", redirectPath);
|
||||||
|
|
||||||
authenticator.requestAuthentication(request, response);
|
authenticator.requestAuthentication(request, response);
|
||||||
|
|
||||||
@@ -178,9 +180,10 @@ public class IdentityServiceAdminConsoleAuthenticatorUnitTest
|
|||||||
String redirectPath = "/alfresco/s/admin/admin-communitysummary";
|
String redirectPath = "/alfresco/s/admin/admin-communitysummary";
|
||||||
when(identityServiceConfig.getAudience()).thenReturn(audience);
|
when(identityServiceConfig.getAudience()).thenReturn(audience);
|
||||||
when(identityServiceConfig.getAdminConsoleRedirectPath()).thenReturn(redirectPath);
|
when(identityServiceConfig.getAdminConsoleRedirectPath()).thenReturn(redirectPath);
|
||||||
|
when(identityServiceConfig.getAdminConsoleScopes()).thenReturn(Set.of("openid", "email", "profile", "offline_access"));
|
||||||
ArgumentCaptor<String> authenticationRequest = ArgumentCaptor.forClass(String.class);
|
ArgumentCaptor<String> authenticationRequest = ArgumentCaptor.forClass(String.class);
|
||||||
String expectedUri = "http://localhost:8999/auth?client_id=alfresco&redirect_uri=%s%s&response_type=code&scope="
|
String expectedUri = "http://localhost:8999/auth?client_id=alfresco&redirect_uri=%s%s&response_type=code&scope="
|
||||||
.formatted("http://localhost:8080", redirectPath);
|
.formatted("http://localhost:8080", redirectPath);
|
||||||
|
|
||||||
authenticator.requestAuthentication(request, response);
|
authenticator.requestAuthentication(request, response);
|
||||||
|
|
||||||
@@ -200,7 +203,7 @@ public class IdentityServiceAdminConsoleAuthenticatorUnitTest
|
|||||||
when(cookiesService.getCookie(ALFRESCO_ACCESS_TOKEN, request)).thenReturn("EXPIRED_JWT_TOKEN");
|
when(cookiesService.getCookie(ALFRESCO_ACCESS_TOKEN, request)).thenReturn("EXPIRED_JWT_TOKEN");
|
||||||
when(cookiesService.getCookie(ALFRESCO_REFRESH_TOKEN, request)).thenReturn("REFRESH_TOKEN");
|
when(cookiesService.getCookie(ALFRESCO_REFRESH_TOKEN, request)).thenReturn("REFRESH_TOKEN");
|
||||||
when(cookiesService.getCookie(ALFRESCO_TOKEN_EXPIRATION, request)).thenReturn(
|
when(cookiesService.getCookie(ALFRESCO_TOKEN_EXPIRATION, request)).thenReturn(
|
||||||
String.valueOf(Instant.now().minusSeconds(60).toEpochMilli()));
|
String.valueOf(Instant.now().minusSeconds(60).toEpochMilli()));
|
||||||
|
|
||||||
when(identityServiceFacade.authorize(any(AuthorizationGrant.class))).thenThrow(AuthorizationException.class);
|
when(identityServiceFacade.authorize(any(AuthorizationGrant.class))).thenThrow(AuthorizationException.class);
|
||||||
|
|
||||||
@@ -221,8 +224,8 @@ public class IdentityServiceAdminConsoleAuthenticatorUnitTest
|
|||||||
when(accessTokenAuthorization.getAccessToken()).thenReturn(accessToken);
|
when(accessTokenAuthorization.getAccessToken()).thenReturn(accessToken);
|
||||||
when(accessTokenAuthorization.getRefreshTokenValue()).thenReturn("REFRESH_TOKEN");
|
when(accessTokenAuthorization.getRefreshTokenValue()).thenReturn("REFRESH_TOKEN");
|
||||||
when(identityServiceFacade.authorize(
|
when(identityServiceFacade.authorize(
|
||||||
AuthorizationGrant.authorizationCode("auth_code", adminConsoleURL.toString())))
|
AuthorizationGrant.authorizationCode("auth_code", adminConsoleURL.toString())))
|
||||||
.thenReturn(accessTokenAuthorization);
|
.thenReturn(accessTokenAuthorization);
|
||||||
when(remoteUserMapper.getRemoteUser(requestCaptor.capture())).thenReturn("admin");
|
when(remoteUserMapper.getRemoteUser(requestCaptor.capture())).thenReturn("admin");
|
||||||
|
|
||||||
String username = authenticator.getAdminConsoleUser(request, response);
|
String username = authenticator.getAdminConsoleUser(request, response);
|
||||||
|
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.security.authentication.identityservice.user;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.mockito.MockitoAnnotations.initMocks;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade;
|
||||||
|
|
||||||
|
public class AccessTokenToDecodedTokenUserMapperUnitTest
|
||||||
|
{
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IdentityServiceFacade.DecodedAccessToken decodedAccessToken;
|
||||||
|
|
||||||
|
private AccessTokenToDecodedTokenUserMapper tokenToDecodedTokenUserMapper;
|
||||||
|
|
||||||
|
public static final String USERNAME_CLAIM = "nickname";
|
||||||
|
public static final String EMAIL_CLAIM = "email";
|
||||||
|
public static final String FIRST_NAME_CLAIM = "given_name";
|
||||||
|
public static final String LAST_NAME_CLAIM = "family_name";
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
initMocks(this);
|
||||||
|
UserInfoAttrMapping userInfoAttrMapping = new UserInfoAttrMapping(USERNAME_CLAIM, FIRST_NAME_CLAIM, LAST_NAME_CLAIM, EMAIL_CLAIM);
|
||||||
|
tokenToDecodedTokenUserMapper = new AccessTokenToDecodedTokenUserMapper(userInfoAttrMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldMapToDecodedTokenUserWithAllFieldsPopulated()
|
||||||
|
{
|
||||||
|
when(decodedAccessToken.getClaim(USERNAME_CLAIM)).thenReturn("johny123");
|
||||||
|
when(decodedAccessToken.getClaim(FIRST_NAME_CLAIM)).thenReturn("John");
|
||||||
|
when(decodedAccessToken.getClaim(LAST_NAME_CLAIM)).thenReturn("Doe");
|
||||||
|
when(decodedAccessToken.getClaim(EMAIL_CLAIM)).thenReturn("johny123@email.com");
|
||||||
|
|
||||||
|
Optional<DecodedTokenUser> result = tokenToDecodedTokenUserMapper.toDecodedTokenUser(decodedAccessToken);
|
||||||
|
|
||||||
|
assertTrue(result.isPresent());
|
||||||
|
assertEquals("johny123", result.get().username());
|
||||||
|
assertEquals("John", result.get().firstName());
|
||||||
|
assertEquals("Doe", result.get().lastName());
|
||||||
|
assertEquals("johny123@email.com", result.get().email());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldMapToDecodedTokenUserWithSomeFieldsEmpty()
|
||||||
|
{
|
||||||
|
when(decodedAccessToken.getClaim(USERNAME_CLAIM)).thenReturn("johny123");
|
||||||
|
when(decodedAccessToken.getClaim(FIRST_NAME_CLAIM)).thenReturn("");
|
||||||
|
when(decodedAccessToken.getClaim(LAST_NAME_CLAIM)).thenReturn("Doe");
|
||||||
|
when(decodedAccessToken.getClaim(EMAIL_CLAIM)).thenReturn("");
|
||||||
|
|
||||||
|
Optional<DecodedTokenUser> result = tokenToDecodedTokenUserMapper.toDecodedTokenUser(decodedAccessToken);
|
||||||
|
|
||||||
|
assertTrue(result.isPresent());
|
||||||
|
assertEquals("johny123", result.get().username());
|
||||||
|
assertEquals("", result.get().firstName());
|
||||||
|
assertEquals("Doe", result.get().lastName());
|
||||||
|
assertEquals("", result.get().email());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldReturnEmptyOptionalForNullUsername()
|
||||||
|
{
|
||||||
|
when(decodedAccessToken.getClaim(USERNAME_CLAIM)).thenReturn(null);
|
||||||
|
when(decodedAccessToken.getClaim(FIRST_NAME_CLAIM)).thenReturn("John");
|
||||||
|
when(decodedAccessToken.getClaim(LAST_NAME_CLAIM)).thenReturn("Doe");
|
||||||
|
when(decodedAccessToken.getClaim(EMAIL_CLAIM)).thenReturn("johny123@email.com");
|
||||||
|
|
||||||
|
Optional<DecodedTokenUser> result = tokenToDecodedTokenUserMapper.toDecodedTokenUser(decodedAccessToken);
|
||||||
|
|
||||||
|
assertFalse(result.isPresent());
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.security.authentication.identityservice.user;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.mockito.MockitoAnnotations.initMocks;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.security.PersonService;
|
||||||
|
|
||||||
|
public class TokenUserToOIDCUserMapperUnitTest
|
||||||
|
{
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PersonService personService;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private TokenUserToOIDCUserMapper tokenUserToOIDCUserMapper;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
initMocks(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldMapToOIDCUserWithAllFieldsPopulated()
|
||||||
|
{
|
||||||
|
DecodedTokenUser decodedTokenUser = new DecodedTokenUser("JOHNY123", "John", "Doe", "johny123@email.com");
|
||||||
|
when(personService.getUserIdentifier("JOHNY123")).thenReturn("johny123");
|
||||||
|
|
||||||
|
OIDCUserInfo oidcUserInfo = tokenUserToOIDCUserMapper.toOIDCUser(decodedTokenUser);
|
||||||
|
|
||||||
|
assertEquals("johny123", oidcUserInfo.username());
|
||||||
|
assertEquals("John", oidcUserInfo.firstName());
|
||||||
|
assertEquals("Doe", oidcUserInfo.lastName());
|
||||||
|
assertEquals("johny123@email.com", oidcUserInfo.email());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldMapToOIDCUserWithSomeFieldsEmpty()
|
||||||
|
{
|
||||||
|
DecodedTokenUser decodedTokenUser = new DecodedTokenUser("johny123", "", "Doe", "");
|
||||||
|
when(personService.getUserIdentifier("johny123")).thenReturn("johny123");
|
||||||
|
|
||||||
|
OIDCUserInfo oidcUserInfo = tokenUserToOIDCUserMapper.toOIDCUser(decodedTokenUser);
|
||||||
|
|
||||||
|
assertEquals("johny123", oidcUserInfo.username());
|
||||||
|
assertEquals("", oidcUserInfo.firstName());
|
||||||
|
assertEquals("Doe", oidcUserInfo.lastName());
|
||||||
|
assertEquals("", oidcUserInfo.email());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldReturnNullForNullUsername()
|
||||||
|
{
|
||||||
|
DecodedTokenUser decodedTokenUser = new DecodedTokenUser(null, "John", "Doe", "johny123@email.com");
|
||||||
|
|
||||||
|
OIDCUserInfo oidcUserInfo = tokenUserToOIDCUserMapper.toOIDCUser(decodedTokenUser);
|
||||||
|
|
||||||
|
assertNull(oidcUserInfo.username());
|
||||||
|
assertEquals("John", oidcUserInfo.firstName());
|
||||||
|
assertEquals("Doe", oidcUserInfo.lastName());
|
||||||
|
assertEquals("johny123@email.com", oidcUserInfo.email());
|
||||||
|
}
|
||||||
|
}
|
@@ -28,6 +28,9 @@ identity-service.register-node-at-startup=true
|
|||||||
identity-service.register-node-period=50
|
identity-service.register-node-period=50
|
||||||
identity-service.token-store=SESSION
|
identity-service.token-store=SESSION
|
||||||
identity-service.principal-attribute=preferred_username
|
identity-service.principal-attribute=preferred_username
|
||||||
|
identity-service.first-name-attribute=given_name
|
||||||
|
identity-service.last-name-attribute=family_name
|
||||||
|
identity-service.email-attribute=email
|
||||||
identity-service.turn-off-change-session-id-on-login=true
|
identity-service.turn-off-change-session-id-on-login=true
|
||||||
identity-service.token-minimum-time-to-live=10
|
identity-service.token-minimum-time-to-live=10
|
||||||
identity-service.min-time-between-jwks-requests=60
|
identity-service.min-time-between-jwks-requests=60
|
||||||
|
Reference in New Issue
Block a user