Compare commits

..

86 Commits

Author SHA1 Message Date
alfresco-build
d97d8fba04 [maven-release-plugin][skip ci] prepare release 23.6.0.4 2025-06-24 06:43:04 +00:00
KushalBanik
368b571d9c [MNT-10187] common-beanutils bumbed up to 1.11.0 (#3395) 2025-06-24 11:29:03 +05:30
alfresco-build
e9da7d222b [maven-release-plugin][skip ci] prepare for next development iteration 2025-06-17 15:58:11 +00:00
alfresco-build
8c059460f9 [maven-release-plugin][skip ci] prepare release 23.6.0.3 2025-06-17 15:58:09 +00:00
jakubkochman
bd0aaa08b3 ACS-6928 backported to 23.N (#3390) 2025-06-17 17:12:37 +02:00
alfresco-build
93d678dc30 [maven-release-plugin][skip ci] prepare for next development iteration 2025-06-05 11:35:36 +00:00
alfresco-build
9648189827 [maven-release-plugin][skip ci] prepare release 23.6.0.2 2025-06-05 11:35:34 +00:00
SatyamSah5
9bfd274127 [ACS-9697] Backport to 23.N (#3375) 2025-06-05 16:23:23 +05:30
jakubkochman
dcf9f65f6b ACS-9646 backport to 23.N (#3378) 2025-06-05 12:12:22 +02:00
alfresco-build
784fae5834 [maven-release-plugin][skip ci] prepare for next development iteration 2025-06-03 12:58:29 +00:00
alfresco-build
eddd8a1065 [maven-release-plugin][skip ci] prepare release 23.6.0.1 2025-06-03 12:58:26 +00:00
cezary-witkowski
0941746518 [MNT-24859] Basic Auth still possible with Keycloak enabled (#3373)
Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>
2025-06-03 14:14:49 +02:00
Damian Ujma
a1f0f35f60 Updating 23.N branch to 23.6.0 after 23.5.0 ACS release [skip ci] 2025-06-03 13:18:18 +02:00
alfresco-build
25d96a50cd [maven-release-plugin][skip ci] prepare for next development iteration 2025-06-02 08:05:26 +00:00
alfresco-build
3c7f024fed [maven-release-plugin][skip ci] prepare release 23.5.0.19 2025-06-02 08:05:25 +00:00
rrajoria
6a1a197701 Bump AOS Version 3.3.0 2025-06-02 12:48:39 +05:30
alfresco-build
5cff5092a0 [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-26 16:39:08 +00:00
alfresco-build
b50f912df2 [maven-release-plugin][skip ci] prepare release 23.5.0.18 2025-05-26 16:39:05 +00:00
varapathijanakiram
b4f00dddb8 Merge pull request #3368 from Alfresco/revert-3364-fix/MNT-24776-Backport
Revert "Backported the MNT-24776"
2025-05-26 21:26:29 +05:30
varapathijanakiram
02c103f39a Revert "Backported the MNT-24776 (#3364)"
This reverts commit d86415401d.
2025-05-26 20:47:07 +05:30
alfresco-build
3c23fa20c5 [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-23 12:25:05 +00:00
alfresco-build
576b6faac9 [maven-release-plugin][skip ci] prepare release 23.5.0.17 2025-05-23 12:25:03 +00:00
varapathijanakiram
d86415401d Backported the MNT-24776 (#3364) 2025-05-23 17:11:13 +05:30
alfresco-build
460cc1f2cd [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-21 11:36:58 +00:00
alfresco-build
370fef10fd [maven-release-plugin][skip ci] prepare release 23.5.0.16 2025-05-21 11:36:56 +00:00
varapathijanakiram
efadc239d4 Merge pull request #3360 from Alfresco/fix/MNT-24172
[fix/MNT-24172-fixRecreationOfRendition2] Backport Fix of recreation of rendition2
2025-05-21 16:25:03 +05:30
alfresco-build
de90e37578 [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-20 11:27:29 +00:00
alfresco-build
6e438d2e4f [maven-release-plugin][skip ci] prepare release 23.5.0.15 2025-05-20 11:27:26 +00:00
SatyamSah5
a86fa21880 [ACS-9572] Show proper message for duplicate unzipping (#3356) 2025-05-20 15:15:42 +05:30
DurgDineshsai
db74a6e7f2 Merge pull request #3357 from Alfresco/fix/MNT-24146
Backport MNT 24146 changes
2025-05-20 11:49:28 +05:30
rrajoria
8c773ac97c fix precommit 2025-05-20 10:53:27 +05:30
vjanakiram
5a2b3cf64d [fix/MNT-24172-fixRecreationOfRendition2] Backport fix for recreation of rendition2 2025-05-19 19:45:06 +05:30
rrajoria
8ab910d2b1 Fix PreCommit 2025-05-19 12:25:24 +05:30
alfresco-build
fa70f1cd45 [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-19 05:57:38 +00:00
alfresco-build
75a2e0f901 [maven-release-plugin][skip ci] prepare release 23.5.0.14 2025-05-19 05:57:36 +00:00
bsayan2
84997bcf86 MNT-25069 help documentation url updated. backport to 23.N (#3343) 2025-05-19 10:40:00 +05:30
alfresco-build
536b3ddd6d [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-16 15:13:54 +00:00
alfresco-build
bb86c97b11 [maven-release-plugin][skip ci] prepare release 23.5.0.13 2025-05-16 15:13:52 +00:00
Gerard Olenski
558f117f24 ACS-9578 Improve stability in AddToHoldsBulkV1Tests (#3355)
(cherry picked from commit d163410e3d)
2025-05-16 16:31:44 +02:00
alfresco-build
bbae71658d [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-16 13:04:38 +00:00
alfresco-build
9ee9653463 [maven-release-plugin][skip ci] prepare release 23.5.0.12 2025-05-16 13:04:36 +00:00
Sara
44570cec8a Merge pull request #3353 from Alfresco/feature/ACS-9642_Prep_23-N_for_ACS-23-5
ACS-9642 Prepare 23.N forACS-23-5
2025-05-16 12:37:13 +01:00
alfresco-build
33eb0354fa [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-16 10:51:22 +00:00
alfresco-build
0d3e2dc8bb [maven-release-plugin][skip ci] prepare release 23.5.0.11 2025-05-16 10:51:20 +00:00
Gerard Olenski
ac62c52a33 ACS-9652 [Es Connector] Prepare ACS 23.N for release (with ES Connector 5.1.0) (#3354)
Co-authored-by: Belal Ansari <belal.ansari@hyland.com>
2025-05-16 12:05:27 +02:00
Sara Aspery
0d30d40d8f Merge branch 'refs/heads/release/23.N' into feature/ACS-9642_Prep_23-N_for_ACS-23-5 2025-05-16 10:16:29 +01:00
alfresco-build
87e365df7e [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-16 09:02:12 +00:00
alfresco-build
6ee01e808b [maven-release-plugin][skip ci] prepare release 23.5.0.10 2025-05-16 09:02:09 +00:00
tathagta15
57427f4765 [PRODSEC-10096]-Bump-poi-version-backport (#3352) 2025-05-16 13:50:06 +05:30
Tiago Salvado
f6a12760c9 [MNT-24992] Add method to force renditions content hash code (#3288) (#3291) 2025-05-15 22:48:15 +01:00
tathagta15
560a050af3 Bump org.springframework.security:spring-security to 6.3.8 (#3322) 2025-05-15 18:59:55 +01:00
cezary-witkowski
5ed82930d2 [MNT-24937] Fix EventTableOutbox messages flooding the logs on bootstrap failure (#3286) (#3297)
Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>
2025-05-15 18:55:08 +01:00
Belal Ansari
f8a32022c3 PRODSEC-10029 - to bump apache camel and netty to latest safe version along with roundRobin Load Balancer upgrade 2025-05-15 18:51:50 +01:00
Belal Ansari
137df0ff4c PRODSEC-10029 - to bumping apache camel and netty to latest safe compatible version 2025-05-15 18:51:50 +01:00
Kacper Magdziarz
23bd8c064c [PRODSEC-10013] Bump JSON Smart to 2.5.2 2025-05-15 18:02:22 +01:00
purusothaman-mm
e3384eaee4 [PRODSEC-9888] Fix for cxf-core and netty-common (#3217) 2025-05-15 18:00:54 +01:00
Kacper Magdziarz
c7e5716a4a [ACS-9490] Backport: Use FixedThreadPool for ExecutorService (#3301) (#3302) 2025-05-15 17:45:34 +01:00
Belal Ansari
7fa0d30100 Fix/MNT-24891 backport to 23.3 (#3312)
Co-authored-by: Kacper Magdziarz <kacper.magdziarz@hyland.com>
2025-05-15 17:44:02 +01:00
alfresco-build
6d33e57d05 [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-15 13:09:09 +00:00
alfresco-build
f0bfc647e4 [maven-release-plugin][skip ci] prepare release 23.5.0.9 2025-05-15 13:09:07 +00:00
DurgDineshsai
757dbbbb1f Merge pull request #3349 from Alfresco/fix/MNT-23926- backport fix
MNT-23926 backport fix
2025-05-15 17:54:58 +05:30
alfresco-build
8c555b62d4 [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-15 12:00:43 +00:00
alfresco-build
d0138c9702 [maven-release-plugin][skip ci] prepare release 23.5.0.8 2025-05-15 12:00:42 +00:00
DurgDineshsai
71849cd4ac MNT-24146 -backport fix & precommit changes 2025-05-15 17:30:20 +05:30
Damian Ujma
dba27bb86c ACS-9416 Backport ACS-9414 Enhance the Identity Provider configuration (#3263) (#3347) 2025-05-15 13:08:41 +02:00
DurgDineshsai
6a37bc93c0 MNT-23926 backport fix 2025-05-15 15:41:21 +05:30
DurgDineshsai
686ffcb19c MNT-24146 backport fix 2025-05-15 15:04:46 +05:30
alfresco-build
b36a1a9364 [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-05 07:22:07 +00:00
alfresco-build
a12a31120d [maven-release-plugin][skip ci] prepare release 23.5.0.7 2025-05-05 07:22:06 +00:00
KushalBanik
bf2e53344d Backport/[MNT-24490] Reference for AlfrescoSQLServerDialect changed to SQLServerDialect 2025-05-05 11:25:09 +05:30
alfresco-build
aa4570c895 [maven-release-plugin][skip ci] prepare for next development iteration 2025-04-21 06:24:28 +00:00
alfresco-build
6e2e3f705f [maven-release-plugin][skip ci] prepare release 23.5.0.6 2025-04-21 06:24:26 +00:00
SatyamSah5
462625165f [MNT-24623] fix for unzipping zip files having accent char in folder … (#3317) 2025-04-21 11:07:40 +05:30
alfresco-build
d341a3bab8 [maven-release-plugin][skip ci] prepare for next development iteration 2025-04-04 11:42:48 +00:00
alfresco-build
ecf658dd82 [maven-release-plugin][skip ci] prepare release 23.5.0.5 2025-04-04 11:42:46 +00:00
Eva Vasques
feba13d274 [MNT-24913] Added fallback method to obtain deployment category in order to check if workflow is secure (#3300)
Co-authored-by: Tiago Salvado <9038083+tiagosalvado10@users.noreply.github.com>
Co-authored-by: Tiago Salvado <tiagosalvado10@gmail.com>
2025-04-04 12:01:18 +01:00
alfresco-build
b2023bed63 [maven-release-plugin][skip ci] prepare for next development iteration 2025-04-03 14:11:34 +00:00
alfresco-build
2c1203f1f5 [maven-release-plugin][skip ci] prepare release 23.5.0.4 2025-04-03 14:11:33 +00:00
Eva Vasques
0edebd7df1 Revert "[MNT-24137] Audit Issue Internal Server Error fix" (#3292) 2025-04-03 14:20:27 +01:00
alfresco-build
e059aa060a [maven-release-plugin][skip ci] prepare for next development iteration 2025-02-10 12:12:34 +00:00
alfresco-build
bd92569cec [maven-release-plugin][skip ci] prepare release 23.5.0.3 2025-02-10 12:12:32 +00:00
Kacper Magdziarz
2b521fd5a7 [ACS-9181] Bump Keycloak to 26.1.0 on ACS 23.N (#3173)
* [ACS-9181] Bump Keycloak to 26.1.0

* [ACS-9181] Bump Keycloak
2025-02-10 12:25:38 +01:00
alfresco-build
1ecfa6c18e [maven-release-plugin][skip ci] prepare for next development iteration 2025-02-03 14:49:23 +00:00
alfresco-build
60eb7d2630 [maven-release-plugin][skip ci] prepare release 23.5.0.2 2025-02-03 14:49:21 +00:00
Cezary Witkowski
65a70c64c5 [ACS-9205] Backport MNT-24807 to the release/23.N branch (#3183)
* [MNT-24807] repo event2 is exposing user password hash and salt (#3147)

* [MNT-24807] Implemented PropertyReplacer that replaces values of sensitive properties (e.g. passwords) during creation of NodeResource for event2

* [MNT-24807] Fix failing tests

* Revert "[MNT-24807] Fix failing tests"

This reverts commit c118f713f2.

* [MNT-24807] Fix failing tests without reformat

Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>

* [MNT-24807] Introduced interface to keep convention

Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>

* [MNT-24807] Added ability to configure property filter and mapper for user

Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>

* [MNT-24807] Fixed npe and pmd issues

Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>

* [MNT-24807] Fixed more pmd comments, applied pre-commit formatting

Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>

* [MNT-24807] Renamed user configured properties to indicate what they do, added failsafe when userConfiguredReplacementText is not configured at all

Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>

* [MNT-24807] Added unit tests

Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>

* [MNT-24807] Additional config to disable property mapper entirely

* [MNT-24807] PMD again

* [MNT-24807] Updated year in licence for some files I missed

Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>

---------

Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>

* [ACS-9205] Apply spotless

Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>

---------

Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>
2025-02-03 14:46:23 +01:00
alfresco-build
d23279227a [maven-release-plugin][skip ci] prepare for next development iteration 2025-01-30 12:56:38 +00:00
178 changed files with 21019 additions and 18410 deletions

View File

@@ -44,14 +44,14 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.2.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.2.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.2.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- id: changed-files
uses: Alfresco/alfresco-build-tools/.github/actions/github-list-changes@v8.2.0
uses: Alfresco/alfresco-build-tools/.github/actions/github-list-changes@v8.16.0
with:
write-list-to-env: true
- uses: Alfresco/alfresco-build-tools/.github/actions/pre-commit@v8.2.0
- uses: Alfresco/alfresco-build-tools/.github/actions/pre-commit@v8.16.0
- name: "Init"
run: bash ./scripts/ci/init.sh
- name: "Prepare maven cache and check compilation"
@@ -69,12 +69,12 @@ jobs:
!contains(github.event.head_commit.message, '[force')
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Init"
run: bash ./scripts/ci/init.sh
- uses: Alfresco/alfresco-build-tools/.github/actions/veracode@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/veracode@v8.16.0
continue-on-error: true
with:
srcclr-api-token: ${{ secrets.SRCCLR_API_TOKEN }}
@@ -92,10 +92,10 @@ jobs:
!contains(github.event.head_commit.message, '[force')
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/github-download-file@v7.1.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.16.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.16.0
with:
token: ${{ secrets.BOT_GITHUB_TOKEN }}
repository: "Alfresco/veracode-baseline-archive"
@@ -142,10 +142,10 @@ jobs:
!contains(github.event.head_commit.message, '[skip tests]') &&
!contains(github.event.head_commit.message, '[force]')
steps:
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.0
- uses: Alfresco/ya-pmd-scan@v4.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- uses: Alfresco/ya-pmd-scan@v4.3.0
with:
classpath-build-command: "mvn test-compile -ntp -Pags -pl \"-:alfresco-community-repo-docker\""
@@ -175,14 +175,14 @@ jobs:
testAttributes: "-Dtest=AllMmtUnitTestSuite"
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Init"
run: bash ./scripts/ci/init.sh
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.testModule }}
@@ -213,7 +213,7 @@ jobs:
continue-on-error: true
- name: "Summarize Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
id: rp-summarize
with:
tests-outcome: ${{ steps.run-tests.outcome }}
@@ -255,9 +255,9 @@ jobs:
REQUIRES_INSTALLED_ARTIFACTS: true
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Build"
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
run: |
@@ -270,7 +270,7 @@ jobs:
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.testSuite }}
@@ -301,7 +301,7 @@ jobs:
continue-on-error: true
- name: "Summarize Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
id: rp-summarize
with:
tests-outcome: ${{ steps.run-tests.outcome }}
@@ -334,9 +334,9 @@ jobs:
version: ['10.2.18', '10.4', '10.5']
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Init"
run: bash ./scripts/ci/init.sh
- name: Run MariaDB ${{ matrix.version }} database
@@ -345,7 +345,7 @@ jobs:
MARIADB_VERSION: ${{ matrix.version }}
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.version }}
@@ -376,7 +376,7 @@ jobs:
continue-on-error: true
- name: "Summarize Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
id: rp-summarize
with:
tests-outcome: ${{ steps.run-tests.outcome }}
@@ -405,9 +405,9 @@ jobs:
!contains(github.event.head_commit.message, '[force')
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Init"
run: bash ./scripts/ci/init.sh
- name: "Run MariaDB 10.6 database"
@@ -416,7 +416,7 @@ jobs:
MARIADB_VERSION: 10.6
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
@@ -447,7 +447,7 @@ jobs:
continue-on-error: true
- name: "Summarize Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
id: rp-summarize
with:
tests-outcome: ${{ steps.run-tests.outcome }}
@@ -476,9 +476,9 @@ jobs:
!contains(github.event.head_commit.message, '[force')
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Init"
run: bash ./scripts/ci/init.sh
- name: "Run MySQL 8 database"
@@ -487,7 +487,7 @@ jobs:
MYSQL_VERSION: 8
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
@@ -518,7 +518,7 @@ jobs:
continue-on-error: true
- name: "Summarize Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
id: rp-summarize
with:
tests-outcome: ${{ steps.run-tests.outcome }}
@@ -546,9 +546,9 @@ jobs:
!contains(github.event.head_commit.message, '[force')
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Init"
run: bash ./scripts/ci/init.sh
- name: "Run PostgreSQL 13.12 database"
@@ -557,7 +557,7 @@ jobs:
POSTGRES_VERSION: 13.12
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
@@ -588,7 +588,7 @@ jobs:
continue-on-error: true
- name: "Summarize Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
id: rp-summarize
with:
tests-outcome: ${{ steps.run-tests.outcome }}
@@ -616,9 +616,9 @@ jobs:
!contains(github.event.head_commit.message, '[force')
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Init"
run: bash ./scripts/ci/init.sh
- name: "Run PostgreSQL 14.9 database"
@@ -627,7 +627,7 @@ jobs:
POSTGRES_VERSION: 14.9
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
@@ -658,7 +658,7 @@ jobs:
continue-on-error: true
- name: "Summarize Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
id: rp-summarize
with:
tests-outcome: ${{ steps.run-tests.outcome }}
@@ -686,9 +686,9 @@ jobs:
!contains(github.event.head_commit.message, '[force')
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Init"
run: bash ./scripts/ci/init.sh
- name: "Run PostgreSQL 15.4 database"
@@ -697,7 +697,7 @@ jobs:
POSTGRES_VERSION: 15.4
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
@@ -728,7 +728,7 @@ jobs:
continue-on-error: true
- name: "Summarize Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
id: rp-summarize
with:
tests-outcome: ${{ steps.run-tests.outcome }}
@@ -754,16 +754,16 @@ jobs:
!contains(github.event.head_commit.message, '[force')
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Init"
run: bash ./scripts/ci/init.sh
- name: "Run ActiveMQ"
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile activemq up -d
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
@@ -794,7 +794,7 @@ jobs:
continue-on-error: true
- name: "Summarize Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
id: rp-summarize
with:
tests-outcome: ${{ steps.run-tests.outcome }}
@@ -854,9 +854,9 @@ jobs:
mvn-options: '-Dencryption.ssl.keystore.location=${CI_WORKSPACE}/keystores/alfresco/alfresco.keystore -Dencryption.ssl.truststore.location=${CI_WORKSPACE}/keystores/alfresco/alfresco.truststore'
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Init"
run: bash ./scripts/ci/init.sh
- name: "Set transformers tag"
@@ -879,7 +879,7 @@ jobs:
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.testSuite }} ${{ matrix.idp }}
@@ -910,7 +910,7 @@ jobs:
continue-on-error: true
- name: "Summarize Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
id: rp-summarize
with:
tests-outcome: ${{ steps.run-tests.outcome }}
@@ -968,9 +968,9 @@ jobs:
REQUIRES_LOCAL_IMAGES: true
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Build"
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
run: |
@@ -986,7 +986,7 @@ jobs:
run: mvn install -pl :alfresco-community-repo-integration-test -am -DskipTests -Pall-tas-tests
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.test-name }}
@@ -1024,7 +1024,7 @@ jobs:
continue-on-error: true
- name: "Summarize Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
id: rp-summarize
with:
tests-outcome: ${{ steps.tests.outcome }}
@@ -1050,16 +1050,16 @@ jobs:
!contains(github.event.head_commit.message, '[force')
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Init"
run: bash ./scripts/ci/init.sh
- name: "Run Postgres 15.4 database"
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile postgres up -d
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
@@ -1090,7 +1090,7 @@ jobs:
continue-on-error: true
- name: "Summarize Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
id: rp-summarize
with:
tests-outcome: ${{ steps.run-tests.outcome }}
@@ -1124,9 +1124,9 @@ jobs:
REQUIRES_INSTALLED_ARTIFACTS: true
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Build"
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
run: |
@@ -1134,7 +1134,7 @@ jobs:
bash ./scripts/ci/build.sh
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} 0${{ matrix.part }} - (PostgreSQL) ${{ matrix.test-name }}
@@ -1170,9 +1170,9 @@ jobs:
REQUIRES_INSTALLED_ARTIFACTS: true
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Build"
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
run: |
@@ -1180,7 +1180,7 @@ jobs:
bash ./scripts/ci/build.sh
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} 0${{ matrix.part }} - (MySQL) ${{ matrix.test-name }}
@@ -1212,9 +1212,9 @@ jobs:
REQUIRES_LOCAL_IMAGES: true
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Build"
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
run: |
@@ -1228,7 +1228,7 @@ jobs:
mvn -B install -pl :alfresco-governance-services-automation-community-rest-api -am -Pags -Pall-tas-tests -DskipTests
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v8.16.0
id: rp-prepare
with:
rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }}
@@ -1260,7 +1260,7 @@ jobs:
continue-on-error: true
- name: "Summarize Report Portal"
if: github.ref_name == 'master'
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v7.1.0
uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v8.16.0
id: rp-summarize
with:
tests-outcome: ${{ steps.run-tests.outcome }}
@@ -1302,9 +1302,9 @@ jobs:
!contains(github.event.head_commit.message, '[force]')
steps:
- uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Build"
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
run: |

View File

@@ -34,12 +34,12 @@ jobs:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Init"
run: bash ./scripts/ci/init.sh
- uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v8.16.0
with:
username: ${{ env.GIT_USERNAME }}
email: ${{ env.GIT_EMAIL }}
@@ -63,12 +63,12 @@ jobs:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.1.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.16.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
- name: "Init"
run: bash ./scripts/ci/init.sh
- uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v7.1.0
- uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v8.16.0
with:
username: ${{ env.GIT_USERNAME }}
email: ${{ env.GIT_EMAIL }}

View File

@@ -1273,7 +1273,7 @@
"filename": "repository/src/main/resources/alfresco/repository.properties",
"hashed_secret": "84551ae5442affc9f1a2d3b4c86ae8b24860149d",
"is_verified": false,
"line_number": 770,
"line_number": 771,
"is_secret": false
}
],
@@ -1539,7 +1539,7 @@
"filename": "repository/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java",
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
"is_verified": false,
"line_number": 127,
"line_number": 130,
"is_secret": false
}
],
@@ -1627,7 +1627,7 @@
"filename": "repository/src/test/java/org/alfresco/repo/security/authentication/identityservice/SpringBasedIdentityServiceFacadeUnitTest.java",
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
"is_verified": false,
"line_number": 46,
"line_number": 48,
"is_secret": false
}
],
@@ -1888,5 +1888,5 @@
}
]
},
"generated_at": "2024-10-09T09:32:52Z"
}
"generated_at": "2025-05-15T21:47:13Z"
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-automation-community-repo</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<build>

View File

@@ -26,13 +26,6 @@
*/
package org.alfresco.rest.rm.community.hold;
import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION;
import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_FILING;
import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_READ_RECORDS;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -44,12 +37,25 @@ import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.HttpStatus.UNAUTHORIZED;
import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION;
import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_FILING;
import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_READ_RECORDS;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.utility.report.log.Step.STEP;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.alfresco.dataprep.CMISUtil;
import org.alfresco.dataprep.ContentActions;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
@@ -71,10 +77,6 @@ import org.alfresco.utility.constants.UserRole;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* API tests for adding items to holds via the bulk process
@@ -82,7 +84,7 @@ import org.testng.annotations.Test;
public class AddToHoldsBulkV1Tests extends BaseRMRestTest
{
private static final String ACCESS_DENIED_ERROR_MESSAGE = "Access Denied. You do not have the appropriate " +
"permissions to perform this operation.";
"permissions to perform this operation.";
private static final int NUMBER_OF_FILES = 5;
private final List<FileModel> addedFiles = new ArrayList<>();
private final List<UserModel> users = new ArrayList<>();
@@ -102,8 +104,9 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
{
STEP("Create a hold.");
hold = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold(
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(), FILE_PLAN_ALIAS);
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(),
FILE_PLAN_ALIAS);
holds.add(hold);
STEP("Create test files.");
@@ -117,8 +120,8 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
for (int i = 0; i < NUMBER_OF_FILES; i++)
{
FileModel documentHeld = dataContent.usingAdmin()
.usingResource(i % 2 == 0 ? folder1 : folder2)
.createContent(CMISUtil.DocumentType.TEXT_PLAIN);
.usingResource(i % 2 == 0 ? folder1 : folder2)
.createContent(CMISUtil.DocumentType.TEXT_PLAIN);
addedFiles.add(documentHeld);
}
@@ -128,29 +131,37 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Wait until all files are searchable.");
await().atMost(30, TimeUnit.SECONDS)
.until(() -> getRestAPIFactory().getSearchAPI(null).search(searchRequest).getPagination()
.getTotalItems() == NUMBER_OF_FILES);
.until(() -> getRestAPIFactory().getSearchAPI(null).search(searchRequest).getPagination()
.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()
.query(queryReq)
.op(HoldBulkOperationType.ADD).build();
.query(queryReq)
.op(HoldBulkOperationType.ADD).build();
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* Then the content is added to the hold and the status of the bulk operation is DONE
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API Then the content is added to the hold and the status of the bulk operation is DONE
*/
@Test
public void addContentFromTestSiteToHoldUsingBulkAPI()
{
UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole.SiteCollaborator, hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
UserRole.SiteCollaborator, hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
users.add(userAddHoldPermission);
STEP("Add content from the site to the hold using the bulk API.");
HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.startBulkProcess(holdBulkOperation, hold.getId());
.startBulkProcess(holdBulkOperation, hold.getId());
// Verify the status code
assertStatusCode(ACCEPTED);
@@ -158,50 +169,49 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Wait until all files are added to the hold.");
await().atMost(20, TimeUnit.SECONDS).until(
() -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold.getId()).getEntries().size()
== NUMBER_OF_FILES);
() -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold.getId()).getEntries().size() == NUMBER_OF_FILES);
List<String> holdChildrenNodeRefs = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getChildren(hold.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map(
HoldChild::getId).toList();
.getChildren(hold.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map(
HoldChild::getId)
.toList();
assertEquals(addedFiles.stream().map(FileModel::getNodeRefWithoutVersion).sorted().toList(),
holdChildrenNodeRefs.stream().sorted().toList());
holdChildrenNodeRefs.stream().sorted().toList());
STEP("Check the bulk status.");
HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId());
.getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId());
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 0, null, holdBulkOperation);
STEP("Check the bulk statuses.");
HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatuses(hold.getId());
.getBulkStatuses(hold.getId());
assertEquals(Arrays.asList(holdBulkStatus),
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a folder and all subfolders to a hold using the bulk API
* Then the content is added to the hold and the status of the bulk operation is DONE
* Given a user with the add to hold capability and hold filing permission When the user adds content from a folder and all subfolders to a hold using the bulk API Then the content is added to the hold and the status of the bulk operation is DONE
*/
@Test
public void addContentFromFolderAndAllSubfoldersToHoldUsingBulkAPI()
{
hold3 = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold(
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(), FILE_PLAN_ALIAS);
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(),
FILE_PLAN_ALIAS);
holds.add(hold3);
UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole.SiteCollaborator, hold3.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
UserRole.SiteCollaborator, hold3.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
users.add(userAddHoldPermission);
STEP("Add content from the site to the hold using the bulk API.");
// Get content from folder and all subfolders of the root folder
HoldBulkOperation bulkOperation = HoldBulkOperation.builder()
.query(getContentFromFolderAndAllSubfoldersQuery(rootFolder.getNodeRefWithoutVersion()))
.op(HoldBulkOperationType.ADD).build();
.query(getContentFromFolderAndAllSubfoldersQuery(rootFolder.getNodeRefWithoutVersion()))
.op(HoldBulkOperationType.ADD).build();
HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.startBulkProcess(bulkOperation, hold3.getId());
.startBulkProcess(bulkOperation, hold3.getId());
// Verify the status code
assertStatusCode(ACCEPTED);
@@ -209,43 +219,40 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Wait until all files are added to the hold.");
await().atMost(20, TimeUnit.SECONDS).until(
() -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold3.getId()).getEntries().size()
== NUMBER_OF_FILES);
() -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold3.getId()).getEntries().size() == NUMBER_OF_FILES);
List<String> holdChildrenNodeRefs = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getChildren(hold3.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map(
HoldChild::getId).toList();
.getChildren(hold3.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map(
HoldChild::getId)
.toList();
assertEquals(addedFiles.stream().map(FileModel::getNodeRefWithoutVersion).sorted().toList(),
holdChildrenNodeRefs.stream().sorted().toList());
holdChildrenNodeRefs.stream().sorted().toList());
STEP("Check the bulk status.");
HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatus(hold3.getId(), bulkOperationEntry.getBulkStatusId());
.getBulkStatus(hold3.getId(), bulkOperationEntry.getBulkStatusId());
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 0, null, bulkOperation);
STEP("Check the bulk statuses.");
HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatuses(hold3.getId());
.getBulkStatuses(hold3.getId());
assertEquals(List.of(holdBulkStatus),
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
}
/**
* Given a user without the add to hold capability
* When the user adds content from a site to a hold using the bulk API
* Then the user receives access denied error
* Given a user without the add to hold capability When the user adds content from a site to a hold using the bulk API Then the user receives access denied error
*/
@Test
public void testBulkProcessWithUserWithoutAddToHoldCapability()
{
UserModel userWithoutAddToHoldCapability = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole
.SiteCollaborator,
hold.getId(), UserRoles.ROLE_RM_POWER_USER, PERMISSION_FILING);
UserRole.SiteCollaborator,
hold.getId(), UserRoles.ROLE_RM_POWER_USER, PERMISSION_FILING);
users.add(userWithoutAddToHoldCapability);
STEP("Add content from the site to the hold using the bulk API.");
getRestAPIFactory().getHoldsAPI(userWithoutAddToHoldCapability)
.startBulkProcess(holdBulkOperation, hold.getId());
.startBulkProcess(holdBulkOperation, hold.getId());
STEP("Verify the response status code and the error message.");
assertStatusCode(FORBIDDEN);
@@ -253,21 +260,19 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user without the filing permission on a hold
* When the user adds content from a site to a hold using the bulk API
* Then the user receives access denied error
* Given a user without the filing permission on a hold When the user adds content from a site to a hold using the bulk API Then the user receives access denied error
*/
@Test
public void testBulkProcessWithUserWithoutFilingPermissionOnAHold()
{
// User without filing permission on a hold
UserModel userWithoutPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole.SiteCollaborator, hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_READ_RECORDS);
UserRole.SiteCollaborator, hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_READ_RECORDS);
users.add(userWithoutPermission);
STEP("Add content from the site to the hold using the bulk API.");
getRestAPIFactory().getHoldsAPI(userWithoutPermission)
.startBulkProcess(holdBulkOperation, hold.getId());
.startBulkProcess(holdBulkOperation, hold.getId());
STEP("Verify the response status code and the error message.");
assertStatusCode(FORBIDDEN);
@@ -276,68 +281,63 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user without the write permission on all the content
* When the user adds content from a site to a hold using the bulk API
* Then all processed items are marked as errors and the last error message contains access denied error
* Given a user without the write permission on all the content When the user adds content from a site to a hold using the bulk API Then all processed items are marked as errors and the last error message contains access denied error
*/
@Test
public void testBulkProcessWithUserWithoutWritePermissionOnTheContent()
{
// User without write permission on the content
UserModel userWithoutPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(
testSite, UserRole.SiteConsumer,
hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
testSite, UserRole.SiteConsumer,
hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
users.add(userWithoutPermission);
// Wait until permissions are reverted
SearchRequest searchRequest = new SearchRequest();
searchRequest.setQuery(holdBulkOperation.getQuery());
await().atMost(30, TimeUnit.SECONDS)
.until(() -> getRestAPIFactory().getSearchAPI(userWithoutPermission).search(searchRequest).getPagination()
.getTotalItems() == NUMBER_OF_FILES);
.until(() -> getRestAPIFactory().getSearchAPI(userWithoutPermission).search(searchRequest).getPagination()
.getTotalItems() == NUMBER_OF_FILES);
STEP("Add content from the site to the hold using the bulk API.");
HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(
userWithoutPermission).startBulkProcess(holdBulkOperation, hold.getId());
userWithoutPermission).startBulkProcess(holdBulkOperation, hold.getId());
STEP("Verify the response.");
assertStatusCode(ACCEPTED);
await().atMost(20, TimeUnit.SECONDS).until(() ->
Objects.equals(getRestAPIFactory().getHoldsAPI(userWithoutPermission)
await().atMost(20, TimeUnit.SECONDS).until(() -> Objects.equals(getRestAPIFactory().getHoldsAPI(userWithoutPermission)
.getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId()).getStatus(), "DONE"));
HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userWithoutPermission)
.getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId());
.getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId());
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, NUMBER_OF_FILES, ACCESS_DENIED_ERROR_MESSAGE,
holdBulkOperation);
holdBulkOperation);
}
/**
* Given a user without the write permission on one file
* When the user adds content from a site to a hold using the bulk API
* Then all processed items are added to the hold except the one that the user does not have write permission
* And the status of the bulk operation is DONE, contains the error message and the number of errors is 1
* Given a user without the write permission on one file When the user adds content from a site to a hold using the bulk API Then all processed items are added to the hold except the one that the user does not have write permission And the status of the bulk operation is DONE, contains the error message and the number of errors is 1
*/
@Test
public void testBulkProcessWithUserWithoutWritePermissionOnOneFile()
{
hold2 = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold(
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(), FILE_PLAN_ALIAS);
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(),
FILE_PLAN_ALIAS);
holds.add(hold2);
UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole.SiteCollaborator, hold2.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
UserRole.SiteCollaborator, hold2.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
users.add(userAddHoldPermission);
contentActions.setPermissionForUser(getAdminUser().getUsername(), getAdminUser().getPassword(),
testSite.getId(), addedFiles.get(0).getName(), userAddHoldPermission.getUsername(),
UserRole.SiteConsumer.getRoleId(), false);
testSite.getId(), addedFiles.get(0).getName(), userAddHoldPermission.getUsername(),
UserRole.SiteConsumer.getRoleId(), false);
STEP("Add content from the site to the hold using the bulk API.");
HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.startBulkProcess(holdBulkOperation, hold2.getId());
.startBulkProcess(holdBulkOperation, hold2.getId());
// Verify the status code
assertStatusCode(ACCEPTED);
@@ -345,56 +345,50 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Wait until all files are added to the hold.");
await().atMost(30, TimeUnit.SECONDS).until(
() -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold2.getId()).getEntries().size()
== NUMBER_OF_FILES - 1);
() -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold2.getId()).getEntries().size() == NUMBER_OF_FILES - 1);
await().atMost(30, TimeUnit.SECONDS).until(
() -> getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId()).getProcessedItems()
== NUMBER_OF_FILES);
() -> getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId()).getProcessedItems() == NUMBER_OF_FILES);
List<String> holdChildrenNodeRefs = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getChildren(hold2.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map(
HoldChild::getId).toList();
.getChildren(hold2.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map(
HoldChild::getId)
.toList();
assertEquals(addedFiles.stream().skip(1).map(FileModel::getNodeRefWithoutVersion).sorted().toList(),
holdChildrenNodeRefs.stream().sorted().toList());
holdChildrenNodeRefs.stream().sorted().toList());
STEP("Check the bulk status.");
HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId());
.getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId());
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 1, ACCESS_DENIED_ERROR_MESSAGE, holdBulkOperation);
STEP("Check the bulk statuses.");
HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatuses(hold2.getId());
.getBulkStatuses(hold2.getId());
assertEquals(List.of(holdBulkStatus),
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
// Revert the permissions
contentActions.setPermissionForUser(getAdminUser().getUsername(), getAdminUser().getPassword(),
testSite.getId(), addedFiles.get(0).getName(), userAddHoldPermission.getUsername(),
UserRole.SiteCollaborator.getRoleId(), true);
testSite.getId(), addedFiles.get(0).getName(), userAddHoldPermission.getUsername(),
UserRole.SiteCollaborator.getRoleId(), true);
}
/**
* Given an unauthenticated user
* When the user adds content from a site to a hold using the bulk API
* Then the user receives unauthorized error
* Given an unauthenticated user When the user adds content from a site to a hold using the bulk API Then the user receives unauthorized error
*/
@Test
public void testBulkProcessAsUnauthenticatedUser()
{
STEP("Start bulk process as unauthenticated user");
getRestAPIFactory().getHoldsAPI(new UserModel(getAdminUser().getUsername(), "wrongPassword"))
.startBulkProcess(holdBulkOperation, hold.getId());
.startBulkProcess(holdBulkOperation, hold.getId());
STEP("Verify the response status code.");
assertStatusCode(UNAUTHORIZED);
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* And the hold does not exist
* Then the user receives not found error
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API And the hold does not exist Then the user receives not found error
*/
@Test
public void testBulkProcessForNonExistentHold()
@@ -407,10 +401,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* and the bulk operation is invalid
* Then the user receives bad request error
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API and the bulk operation is invalid Then the user receives bad request error
*/
@Test
public void testGetBulkStatusesForInvalidOperation()
@@ -418,7 +409,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Start bulk process for non existent hold");
HoldBulkOperation invalidHoldBulkOperation = HoldBulkOperation.builder().op(null)
.query(holdBulkOperation.getQuery()).build();
.query(holdBulkOperation.getQuery()).build();
getRestAPIFactory().getHoldsAPI(getAdminUser()).startBulkProcess(invalidHoldBulkOperation, hold.getId());
STEP("Verify the response status code.");
@@ -426,10 +417,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* And the hold does not exist
* Then the user receives not found error
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API And the hold does not exist Then the user receives not found error
*/
@Test
public void testGetBulkStatusForNonExistentHold()
@@ -442,10 +430,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* And the bulk status does not exist
* Then the user receives not found error
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API And the bulk status does not exist Then the user receives not found error
*/
@Test
public void testGetBulkStatusForNonExistentBulkStatus()
@@ -458,10 +443,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* And the hold does not exist
* Then the user receives not found error
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API And the hold does not exist Then the user receives not found error
*/
@Test
public void testGetBulkStatusesForNonExistentHold()
@@ -474,9 +456,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from all sites to a hold using the bulk API to exceed the limit (30 items)
* Then the user receives bad request error
* Given a user with the add to hold capability and hold filing permission When the user adds content from all sites to a hold using the bulk API to exceed the limit (30 items) Then the user receives bad request error
*/
@Test
public void testExceedingBulkOperationLimit()
@@ -486,8 +466,8 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
queryReq.setLanguage("afts");
HoldBulkOperation exceedLimitOp = HoldBulkOperation.builder()
.query(queryReq)
.op(HoldBulkOperationType.ADD).build();
.query(queryReq)
.op(HoldBulkOperationType.ADD).build();
STEP("Start bulk process to exceed the limit");
getRestAPIFactory().getHoldsAPI(getAdminUser()).startBulkProcess(exceedLimitOp, hold.getId());
@@ -497,26 +477,24 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* And then the user cancels the bulk operation
* Then the user receives OK status code
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API And then the user cancels the bulk operation Then the user receives OK status code
*/
@Test
public void testBulkProcessCancellationWithAllowedUser()
{
Hold hold4 = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold(
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(), FILE_PLAN_ALIAS);
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(),
FILE_PLAN_ALIAS);
holds.add(hold4);
UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole.SiteCollaborator, hold4.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
UserRole.SiteCollaborator, hold4.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
users.add(userAddHoldPermission);
STEP("Add content from the site to the hold using the bulk API.");
HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.startBulkProcess(holdBulkOperation, hold4.getId());
.startBulkProcess(holdBulkOperation, hold4.getId());
// Verify the status code
assertStatusCode(ACCEPTED);
@@ -524,47 +502,44 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Cancel the bulk operation.");
getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.cancelBulkOperation(hold4.getId(), bulkOperationEntry.getBulkStatusId(), new BulkBodyCancel());
.cancelBulkOperation(hold4.getId(), bulkOperationEntry.getBulkStatusId(), new BulkBodyCancel());
// Verify the status code
assertStatusCode(OK);
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* And a 2nd user without the add to hold capability cancels the bulk operation
* Then the 2nd user receives access denied error
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API And a 2nd user without the add to hold capability cancels the bulk operation Then the 2nd user receives access denied error
*/
@Test
public void testBulkProcessCancellationWithUserWithoutAddToHoldCapability()
{
Hold hold5 = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold(
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(), FILE_PLAN_ALIAS);
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(),
FILE_PLAN_ALIAS);
holds.add(hold5);
UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole.SiteCollaborator, hold5.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
UserRole.SiteCollaborator, hold5.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
users.add(userAddHoldPermission);
STEP("Add content from the site to the hold using the bulk API.");
HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.startBulkProcess(holdBulkOperation, hold5.getId());
.startBulkProcess(holdBulkOperation, hold5.getId());
// Verify the status code
assertStatusCode(ACCEPTED);
assertEquals(NUMBER_OF_FILES, bulkOperationEntry.getTotalItems());
UserModel userWithoutAddToHoldCapability = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole
.SiteCollaborator,
hold5.getId(), UserRoles.ROLE_RM_POWER_USER, PERMISSION_FILING);
UserRole.SiteCollaborator,
hold5.getId(), UserRoles.ROLE_RM_POWER_USER, PERMISSION_FILING);
users.add(userWithoutAddToHoldCapability);
STEP("Cancel the bulk operation.");
getRestAPIFactory().getHoldsAPI(userWithoutAddToHoldCapability)
.cancelBulkOperation(hold5.getId(), bulkOperationEntry.getBulkStatusId(), new BulkBodyCancel());
.cancelBulkOperation(hold5.getId(), bulkOperationEntry.getBulkStatusId(), new BulkBodyCancel());
STEP("Verify the response status code and the error message.");
assertStatusCode(FORBIDDEN);
@@ -572,7 +547,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
private void assertBulkProcessStatus(HoldBulkStatus holdBulkStatus, long expectedProcessedItems,
int expectedErrorsCount, String expectedErrorMessage, HoldBulkOperation holdBulkOperation)
int expectedErrorsCount, String expectedErrorMessage, HoldBulkOperation holdBulkOperation)
{
assertEquals("DONE", holdBulkStatus.getStatus());
assertEquals(expectedProcessedItems, holdBulkStatus.getTotalItems());

View File

@@ -23,7 +23,7 @@ Recorded content can be explicitly destroyed whilst maintaining the original nod
* License: Alfresco Community
* Issue Tracker Link: [JIRA RM](https://issues.alfresco.com/jira/projects/RM/summary)
* Contribution Model: Alfresco Closed Source
* Documentation: [docs.alfresco.com (Records Management)](http://docs.alfresco.com/rm2.4/concepts/welcome-rm.html)
* Documentation: [docs.alfresco.com (Records Management)](https://support.hyland.com/r/Alfresco/Alfresco-Governance-Services-Community-Edition/23.4/Alfresco-Governance-Services-Community-Edition/Introduction)
***

View File

@@ -21,18 +21,18 @@ RM is split into two main parts - a repository integration and a Share integrati
* [Community License](../LICENSE.txt)
* [Enterprise License](../../rm-enterprise/LICENSE.txt) (this file will only be present in clones of the Enterprise repository)
* [Issue Tracker Link](https://issues.alfresco.com/jira/projects/RM)
* [Community Documentation Link](http://docs.alfresco.com/rm-community/concepts/welcome-rm.html)
* [Enterprise Documentation Link](http://docs.alfresco.com/rm/concepts/welcome-rm.html)
* [Community Documentation Link](https://support.hyland.com/r/Alfresco/Alfresco-Governance-Services-Community-Edition/23.4/Alfresco-Governance-Services-Community-Edition/Introduction)
* [Enterprise Documentation Link](https://support.hyland.com/r/Alfresco/Alfresco-Governance-Services/23.4/Alfresco-Governance-Services/Introduction)
* [Contribution Model](../../CONTRIBUTING.md)
***
### Prerequisite Knowledge
An understanding of Alfresco Content Services is assumed. The following pages from the [developer documentation](http://docs.alfresco.com/5.2/concepts/dev-for-developers.html) give useful background information:
An understanding of Alfresco Content Services is assumed. The following pages from the [developer documentation](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services-Community-Edition/23.4/Alfresco-Content-Services-Community-Edition/Develop) give useful background information:
* [ACS Architecture](http://docs.alfresco.com/5.2/concepts/dev-arch-overview.html)
* [Platform Extensions](http://docs.alfresco.com/5.2/concepts/dev-platform-extensions.html)
* [Share Extensions](http://docs.alfresco.com/5.2/concepts/dev-extensions-share.html)
* [ACS Architecture](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Software-Architecture)
* [Platform Extensions](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Extension-Points-Overview)
* [Share Extensions](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Share-UI-Extension-Points)
***
@@ -44,12 +44,12 @@ The RM Share module communicates with the repository module via REST APIs. Inter
* A DAO layer responsible for CRUD operations against the database.
#### REST API
The REST API endpoints fall into two main types - v0 (Webscripts) and v1. The [v0 API](http://docs.alfresco.com/5.2/references/dev-extension-points-webscripts.html) is older and not recommended for integrations. The [v1 API](http://docs.alfresco.com/5.1/pra/1/topics/pra-welcome-aara.html) is newer but isn't yet feature complete. If you are running RM locally then the GS API Explorer will be available at [this link](http://localhost:8080/gs-api-explorer/).
The REST API endpoints fall into two main types - v0 (Webscripts) and v1. The [v0 API](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/In-Process-Platform-Extension-Points/Web-Scripts) is older and not recommended for integrations. The [v1 API](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/REST-API-Guide) is newer but isn't yet feature complete. If you are running RM locally then the GS API Explorer will be available at [this link](http://localhost:8080/gs-api-explorer/).
Internally the GS v1 REST API is built on the [Alfresco v1 REST API framework](https://community.alfresco.com/community/ecm/blog/2016/10/11/v1-rest-api-part-1-introduction). It aims to be consistent with this in terms of behaviour and naming.
#### Java Public API
The Java service layer is fronted by a [Java Public API](http://docs.alfresco.com/5.2/concepts/java-public-api-list.html), which we will ensure backward compatible with previous releases. Before we remove any methods there will first be a release containing that method deprecated to allow third party integrations to migrate to a new method. The Java Public API also includes a set of POJO objects which are needed to communicate with the services. It is easy to identify classes that are part of the Java Public API as they are annotated `@AlfrescoPublicApi`.
The Java service layer is fronted by a [Java Public API](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Reference/Java-Foundation-API), which we will ensure backward compatible with previous releases. Before we remove any methods there will first be a release containing that method deprecated to allow third party integrations to migrate to a new method. The Java Public API also includes a set of POJO objects which are needed to communicate with the services. It is easy to identify classes that are part of the Java Public API as they are annotated `@AlfrescoPublicApi`.
Each Java service will have at least four beans defined for it:
@@ -61,7 +61,7 @@ Each Java service will have at least four beans defined for it:
#### DAOs
The DAOs are not part of the Java Public API, but handle CRUD operations against RM stored data. We have some custom queries to improve performance for particularly heavy operations.
We use standard Alfresco [data modelling](http://docs.alfresco.com/5.2/references/dev-extension-points-content-model.html) to store RM metadata. We extend the [Alfresco patching mechanism](http://docs.alfresco.com/5.2/references/dev-extension-points-patch.html) to provide community and enterprise schema upgrades.
We use standard Alfresco [data modelling](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/In-Process-Platform-Extension-Points/Content-Model-Extension-Point) to store RM metadata. We extend the [Alfresco patching mechanism](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/In-Process-Platform-Extension-Points/Patches) to provide community and enterprise schema upgrades.
***

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<modules>

View File

@@ -34,4 +34,7 @@
<!-- content cleanser -->
<bean id="contentCleanser.522022M" class="org.alfresco.module.org_alfresco_module_rm.content.cleanser.ContentCleanser522022M"/>
<!-- content cleanser -->
<bean id="contentCleanser.SevenPass" class="org.alfresco.module.org_alfresco_module_rm.content.cleanser.ContentCleanserSevenPass"/>
</beans>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<properties>

View File

@@ -0,0 +1,51 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* 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.module.org_alfresco_module_rm.content.cleanser;
import java.io.File;
/**
* DoD 5220-22M Seven Pass data cleansing implementation.
*
*/
public class ContentCleanserSevenPass extends ContentCleanser522022M
{
/**
* @see org.alfresco.module.org_alfresco_module_rm.content.cleanser.ContentCleanser#cleanse(java.io.File)
*/
@Override
public void cleanse(File file)
{
super.cleanse(file);
overwrite(file, overwriteZeros);
overwrite(file, overwriteZeros);
overwrite(file, overwriteOnes);
overwrite(file, overwriteRandom);
}
}

View File

@@ -4,7 +4,7 @@
# Version label
version.major=23
version.minor=5
version.minor=6
version.revision=0
version.label=

View File

@@ -0,0 +1,100 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* 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.module.org_alfresco_module_rm.content.cleanser;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.File;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
import org.alfresco.service.cmr.repository.ContentIOException;
/**
* Eager content store cleaner unit test.
*
*/
public class ContentCleanserSevenPassUnitTest extends BaseUnitTest
{
@InjectMocks
@Spy
private ContentCleanserSevenPass contentCleanserSevenPass = new ContentCleanserSevenPass()
{
/** dummy implementations */
@Override
protected void overwrite(File file, OverwriteOperation overwriteOperation)
{
// Intentionally left empty
}
};
@Mock
private File mockedFile;
/**
* Given that a file exists When I cleanse it Then the content is overwritten
*/
@Test
public void cleanseFile()
{
when(mockedFile.exists()).thenReturn(true);
when(mockedFile.canWrite()).thenReturn(true);
contentCleanserSevenPass.cleanse(mockedFile);
verify(contentCleanserSevenPass, times(2)).overwrite(mockedFile, contentCleanserSevenPass.overwriteOnes);
verify(contentCleanserSevenPass, times(3)).overwrite(mockedFile, contentCleanserSevenPass.overwriteZeros);
verify(contentCleanserSevenPass, times(2)).overwrite(mockedFile, contentCleanserSevenPass.overwriteRandom);
}
/**
* Given that the file does not exist When I cleanse it Then an exception is thrown
*/
@Test(expected = ContentIOException.class)
public void fileDoesNotExist()
{
when(mockedFile.exists()).thenReturn(false);
when(mockedFile.canWrite()).thenReturn(true);
contentCleanserSevenPass.cleanse(mockedFile);
}
/**
* Given that I can not write to the file When I cleanse it Then an exception is thrown
*/
@Test(expected = ContentIOException.class)
public void cantWriteToFile()
{
when(mockedFile.exists()).thenReturn(true);
when(mockedFile.canWrite()).thenReturn(false);
contentCleanserSevenPass.cleanse(mockedFile);
}
}

View File

@@ -33,14 +33,15 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAO;
import org.alfresco.repo.domain.propval.PropertyStringValueEntity;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAO;
import org.alfresco.repo.domain.propval.PropertyStringValueEntity;
/**
* RM V3.3 Hold audit entries values patch unit test
*
@@ -55,7 +56,6 @@ public class RMv33HoldAuditEntryValuesPatchUnitTest
@InjectMocks
private RMv33HoldAuditEntryValuesPatch patch;
@Before
public void setUp()
{
@@ -93,15 +93,15 @@ public class RMv33HoldAuditEntryValuesPatchUnitTest
verify(mockedRecordsManagementQueryDAO, times(1)).updatePropertyStringValueEntity(deleteHoldPropertyStringValueEntity);
assertEquals("Add To Hold", addToHoldPropertyStringValueEntity.getStringValue());
assertEquals("add to hold", addToHoldPropertyStringValueEntity.getStringLower());
assertEquals("add to hold", addToHoldPropertyStringValueEntity.getStringEndLower());
assertEquals(Long.valueOf(770_786_109L), addToHoldPropertyStringValueEntity.getStringCrc());
assertEquals("Remove From Hold", removeFromHoldPropertyStringValueEntity.getStringValue());
assertEquals("remove from hold", removeFromHoldPropertyStringValueEntity.getStringLower());
assertEquals("remove from hold", removeFromHoldPropertyStringValueEntity.getStringEndLower());
assertEquals(Long.valueOf(2_967_613_012L), removeFromHoldPropertyStringValueEntity.getStringCrc());
assertEquals("Delete Hold", deleteHoldPropertyStringValueEntity.getStringValue());
assertEquals("delete hold", deleteHoldPropertyStringValueEntity.getStringLower());
assertEquals("delete hold", deleteHoldPropertyStringValueEntity.getStringEndLower());
assertEquals(Long.valueOf(132_640_810L), deleteHoldPropertyStringValueEntity.getStringCrc());
}
@@ -123,5 +123,3 @@ public class RMv33HoldAuditEntryValuesPatchUnitTest
verify(mockedRecordsManagementQueryDAO, times(0)).updatePropertyStringValueEntity(any());
}
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<build>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<modules>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<properties>

View File

@@ -80,6 +80,11 @@ function runAction(p_params)
{
result.fileExist = true;
}
if (error.indexOf("FolderExistsException") != -1)
{
result.fileExist = true;
result.type = "folder";
}
}
results.push(result);

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<dependencies>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<dependencies>

View File

@@ -9,6 +9,6 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
</project>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<modules>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<modules>

View File

@@ -27,7 +27,7 @@
## Synopsis
**TAS**( **T**est **A**utomation **S**ystem)- **CMIS** is the project that handles the automated tests related only to CMIS API integrated with Alfresco One [Alfresco CMIS API](http://docs.alfresco.com/5.1/pra/1/topics/cmis-welcome.html).
**TAS**( **T**est **A**utomation **S**ystem)- **CMIS** is the project that handles the automated tests related only to CMIS API integrated with Alfresco One [Alfresco CMIS API](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Reference/CMIS-API).
It is based on Apache Maven, compatible with major IDEs and is using also Spring capabilities for dependency injection.

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<organization>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<developers>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<developers>

View File

@@ -27,7 +27,7 @@ Back to [TAS Master Documentation](https://git.alfresco.com/tas/alfresco-tas-uti
## Synopsis
**TAS**( **T**est **A**utomation **S**ystem)- **RESTAPI** is the project that handles the automated tests related only to [Alfresco REST API](http://docs.alfresco.com/5.1/pra/1/topics/pra-welcome.html).
**TAS**( **T**est **A**utomation **S**ystem)- **RESTAPI** is the project that handles the automated tests related only to [Alfresco REST API](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/REST-API-Guide).
It is based on Apache Maven, compatible with major IDEs and is using also Spring capabilities for dependency injection.
@@ -271,7 +271,7 @@ restClient.onResponse().assertThat().body("entry.modifiedBy.firstName", org.hamc
### How to generate models or check coverage
There are some simple generators that could parse [Swagger YAML](http://docs.alfresco.com/community/concepts/alfresco-sdk-tutorials-using-rest-api-explorer.html) files and provide some usefull information to you like:
There are some simple generators that could parse [Swagger YAML](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/REST-API-Guide/Things-to-Know-Before-You-Start/The-API-Explorer-is-Your-Source-of-Truth) files and provide some usefull information to you like:
a) Show on screen the actual coverage of TAS vs requests that exists in each YAML file - defined in pom.xml)

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<properties>

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* 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
@@ -26,6 +26,13 @@
package org.alfresco.rest.rules;
import static java.util.stream.Collectors.toList;
import static org.junit.Assert.assertEquals;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.ERROR_MESSAGE_ACCESS_RESTRICTED;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.MAIL_ACTION;
import static org.alfresco.rest.rules.RulesTestsUtils.CHECKIN_ACTION;
@@ -45,11 +52,6 @@ import static org.alfresco.utility.model.FileModel.getRandomFileModel;
import static org.alfresco.utility.model.FileType.TEXT_PLAIN;
import static org.alfresco.utility.model.UserModel.getRandomUserModel;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.junit.Assert.assertEquals;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import java.io.Serializable;
import java.util.Collections;
@@ -61,10 +63,13 @@ import java.util.stream.IntStream;
import jakarta.json.Json;
import jakarta.json.JsonObject;
import org.apache.chemistry.opencmis.client.api.CmisObject;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.alfresco.rest.model.RestActionBodyExecTemplateModel;
import org.alfresco.rest.model.RestActionConstraintModel;
import org.alfresco.rest.model.RestCompositeConditionDefinitionModel;
import org.alfresco.rest.model.RestPaginationModel;
import org.alfresco.rest.model.RestRuleModel;
import org.alfresco.rest.model.RestRuleModelsCollection;
import org.alfresco.utility.constants.UserRole;
@@ -74,9 +79,6 @@ import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.model.UserModel;
import org.apache.chemistry.opencmis.client.api.CmisObject;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* Tests for POST /nodes/{nodeId}/rule-sets/{ruleSetId}/rules.
@@ -101,13 +103,13 @@ public class CreateRulesTests extends RulesRestTest
* <p>
* Also check that the isShared field is not returned when not requested.
*/
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY})
public void createRule()
{
RestRuleModel ruleModel = rulesUtils.createRuleModelWithModifiedValues();
RestRuleModel rule = restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
.createSingleRule(ruleModel);
RestRuleModel expectedRuleModel = rulesUtils.createRuleModelWithModifiedValues();
restClient.assertStatusCodeIs(CREATED);
@@ -117,7 +119,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check creating a rule in a non-existent folder returns an error. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleInNonExistentFolder()
{
STEP("Try to create a rule in non-existent folder.");
@@ -134,7 +136,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check creating a rule in a non-existent rule set returns an error. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleInNonExistentRuleSet()
{
STEP("Try to create a rule in non-existent rule set.");
@@ -148,7 +150,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Try to create a rule without a name and check the error. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleWithEmptyName()
{
RestRuleModel ruleModel = rulesUtils.createRuleModel("");
@@ -160,7 +162,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check we can create two rules with the same name. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void duplicateRuleNameIsAcceptable()
{
RestRuleModel ruleModel = rulesUtils.createRuleModel("duplicateRuleName");
@@ -175,7 +177,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check that a user without permission to view the folder cannot create a rule in it. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void requireReadPermissionToCreateRule()
{
STEP("Create a user and use them to create a private site containing a folder");
@@ -194,7 +196,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check that a Collaborator cannot create a rule in a folder in a private site. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void siteCollaboratorCannotCreateRule()
{
testRolePermissionsWith(SiteCollaborator);
@@ -204,7 +206,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check that a Contributor cannot create a rule in a private folder. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void siteContributorCannotCreateRule()
{
testRolePermissionsWith(SiteContributor);
@@ -214,7 +216,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check that a Consumer cannot create a rule in a folder in a private site. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void siteConsumerCannotCreateRule()
{
testRolePermissionsWith(SiteConsumer);
@@ -224,7 +226,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check that a siteManager can create a rule in a folder in a private site. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void siteManagerCanCreateRule()
{
testRolePermissionsWith(SiteManager)
@@ -234,7 +236,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check we can't create a rule under a document node. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void tryToCreateRuleUnderDocument()
{
STEP("Create a document.");
@@ -250,7 +252,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check we can create several rules. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRules()
{
STEP("Create a list of rules in one POST request");
@@ -258,19 +260,18 @@ public class CreateRulesTests extends RulesRestTest
List<RestRuleModel> ruleModels = ruleNames.stream().map(rulesUtils::createRuleModel).collect(toList());
RestRuleModelsCollection rules = restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createListOfRules(ruleModels);
.createListOfRules(ruleModels);
restClient.assertStatusCodeIs(CREATED);
assertEquals("Unexpected number of rules received in response.", ruleNames.size(), rules.getEntries().size());
IntStream.range(0, ruleModels.size()).forEach(i ->
rules.getEntries().get(i).onModel()
.assertThat().field("id").isNotNull()
.assertThat().field("name").is(ruleNames.get(i)));
IntStream.range(0, ruleModels.size()).forEach(i -> rules.getEntries().get(i).onModel()
.assertThat().field("id").isNotNull()
.assertThat().field("name").is(ruleNames.get(i)));
}
/** Check we can create over 100 rules and get them all back in response. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createOver100Rules()
{
STEP("Create a list of 120 rules in one POST request");
@@ -287,10 +288,9 @@ public class CreateRulesTests extends RulesRestTest
restClient.assertStatusCodeIs(CREATED);
assertEquals("Unexpected number of rules received in response.", ruleCount, rules.getEntries().size());
IntStream.range(0, ruleModels.size()).forEach(i ->
rules.getEntries().get(i).onModel()
.assertThat().field("id").isNotNull()
.assertThat().field("name").is(ruleNamePrefix + (i + 1)));
IntStream.range(0, ruleModels.size()).forEach(i -> rules.getEntries().get(i).onModel()
.assertThat().field("id").isNotNull()
.assertThat().field("name").is(ruleNamePrefix + (i + 1)));
rules.getPagination()
.assertThat().field("count").is(ruleCount)
@@ -302,7 +302,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Try to create several rules with an error in one of them. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRulesWithOneError()
{
STEP("Try to create a three rules but the middle one has an error.");
@@ -319,55 +319,55 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check we can create a rule without description. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleWithoutDescription()
{
RestRuleModel ruleModel = rulesUtils.createRuleModelWithDefaultValues();
UserModel admin = dataUser.getAdminUser();
RestRuleModel rule = restClient.authenticateUser(admin).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
.createSingleRule(ruleModel);
restClient.assertStatusCodeIs(CREATED);
rule.assertThat().field("id").isNotNull()
.assertThat().field("name").is(RULE_NAME_DEFAULT)
.assertThat().field("description").isNull();
.assertThat().field("name").is(RULE_NAME_DEFAULT)
.assertThat().field("description").isNull();
}
/** Check we can create a rule without specifying triggers but with the default "inbound" value. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleWithoutTriggers()
{
RestRuleModel ruleModel = rulesUtils.createRuleModelWithDefaultValues();
UserModel admin = dataUser.getAdminUser();
RestRuleModel rule = restClient.authenticateUser(admin).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
.createSingleRule(ruleModel);
restClient.assertStatusCodeIs(CREATED);
rule.assertThat().field("id").isNotNull()
.assertThat().field("name").is(RULE_NAME_DEFAULT)
.assertThat().field("triggers").is(List.of("inbound"));
.assertThat().field("name").is(RULE_NAME_DEFAULT)
.assertThat().field("triggers").is(List.of("inbound"));
}
/** Check we can create a rule without error script. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleWithoutErrorScript()
{
RestRuleModel ruleModel = rulesUtils.createRuleModelWithDefaultValues();
UserModel admin = dataUser.getAdminUser();
RestRuleModel rule = restClient.authenticateUser(admin).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
.createSingleRule(ruleModel);
restClient.assertStatusCodeIs(CREATED);
rule.assertThat().field("id").isNotNull()
.assertThat().field("name").is(RULE_NAME_DEFAULT)
.assertThat().field("errorScript").isNull();
.assertThat().field("name").is(RULE_NAME_DEFAULT)
.assertThat().field("errorScript").isNull();
}
/** Check we can create a rule with irrelevant isShared flag, and it doesn't have impact to the process. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleWithSharedFlag()
{
RestRuleModel ruleModel = rulesUtils.createRuleModelWithDefaultValues();
@@ -375,23 +375,23 @@ public class CreateRulesTests extends RulesRestTest
UserModel admin = dataUser.getAdminUser();
RestRuleModel rule = restClient.authenticateUser(admin).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
.createSingleRule(ruleModel);
restClient.assertStatusCodeIs(CREATED);
rule.assertThat().field("id").isNotNull()
.assertThat().field("name").is(RULE_NAME_DEFAULT)
.assertThat().field("isShared").isNull();
.assertThat().field("name").is(RULE_NAME_DEFAULT)
.assertThat().field("isShared").isNull();
}
/** Check we can create a rule. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY})
public void createRuleAndIncludeFieldsInResponse()
{
RestRuleModel ruleModel = rulesUtils.createRuleModel("ruleName");
RestRuleModel rule = restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.include("isShared")
.createSingleRule(ruleModel);
.include("isShared")
.createSingleRule(ruleModel);
restClient.assertStatusCodeIs(CREATED);
rule.assertThat().field("isShared").isNotNull();
@@ -412,7 +412,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check that the folder's owner can create rules, even if it is in a private site they aren't a member of. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void checkOwnerCanCreateRule()
{
STEP("Use admin to create a private site.");
@@ -431,7 +431,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check that an administrator can create a rule in a private site even if they aren't a member. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void checkAdminCanCreateRule()
{
STEP("Use a user to create a private site with a folder.");
@@ -446,7 +446,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check that a coordinator can create rules in folders outside sites. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void checkCoordinatorCanCreateRule()
{
STEP("Create a folder in the user's file space.");
@@ -454,11 +454,7 @@ public class CreateRulesTests extends RulesRestTest
STEP("Create another user as a coordinator for this folder.");
UserModel coordinator = dataUser.createRandomTestUser("Rules");
/*
Update folder node properties to add a coordinator
{ "permissions": { "isInheritanceEnabled": true, "locallySet": { "authorityId": "coordinator.getUsername()",
"name": "Coordinator", "accessStatus":"ALLOWED" } } }
*/
/* Update folder node properties to add a coordinator { "permissions": { "isInheritanceEnabled": true, "locallySet": { "authorityId": "coordinator.getUsername()", "name": "Coordinator", "accessStatus":"ALLOWED" } } } */
String putBody = getAddPermissionsBody(coordinator.getUsername(), "Coordinator");
restClient.authenticateUser(user).withCoreAPI().usingNode(folder).updateNode(putBody);
@@ -470,7 +466,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check that an editor cannot create rules in folders outside sites. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void checkEditorCannotCreateRule()
{
STEP("Create a folder in the user's file space.");
@@ -478,11 +474,7 @@ public class CreateRulesTests extends RulesRestTest
STEP("Create another user as a editor for this folder.");
UserModel editor = dataUser.createRandomTestUser();
/*
Update folder node properties to add an editor
{ "permissions": { "isInheritanceEnabled": true, "locallySet": { "authorityId": "editor.getUsername()",
"name": "Coordinator", "accessStatus":"ALLOWED" } } }
*/
/* Update folder node properties to add an editor { "permissions": { "isInheritanceEnabled": true, "locallySet": { "authorityId": "editor.getUsername()", "name": "Coordinator", "accessStatus":"ALLOWED" } } } */
String putBody = getAddPermissionsBody(editor.getUsername(), "Editor");
restClient.authenticateUser(user).withCoreAPI().usingNode(folder).updateNode(putBody);
@@ -494,7 +486,7 @@ public class CreateRulesTests extends RulesRestTest
}
/** Check that a collaborator cannot create rules in folders outside sites. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void checkCollaboratorCannotCreateRule()
{
STEP("Create a folder in the user's file space.");
@@ -502,11 +494,7 @@ public class CreateRulesTests extends RulesRestTest
STEP("Create another user as a collaborator for this folder.");
UserModel collaborator = dataUser.createRandomTestUser();
/*
Update folder node properties to add a collaborator
{ "permissions": { "isInheritanceEnabled": true, "locallySet": { "authorityId": "collaborator.getUsername()",
"name": "Coordinator", "accessStatus":"ALLOWED" } } }
*/
/* Update folder node properties to add a collaborator { "permissions": { "isInheritanceEnabled": true, "locallySet": { "authorityId": "collaborator.getUsername()", "name": "Coordinator", "accessStatus":"ALLOWED" } } } */
String putBody = getAddPermissionsBody(collaborator.getUsername(), "Collaborator");
restClient.authenticateUser(user).withCoreAPI().usingNode(folder).updateNode(putBody);
@@ -572,10 +560,10 @@ public class CreateRulesTests extends RulesRestTest
public void createRuleWithActions_userCannotUsePrivateAction()
{
restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(rulesUtils.createRuleWithPrivateAction());
.createSingleRule(rulesUtils.createRuleWithPrivateAction());
restClient.assertStatusCodeIs(FORBIDDEN)
.assertLastError().containsSummary(ERROR_MESSAGE_ACCESS_RESTRICTED);
.assertLastError().containsSummary(ERROR_MESSAGE_ACCESS_RESTRICTED);
}
/** Check that an administrator can create rules that use private actions. */
@@ -583,7 +571,7 @@ public class CreateRulesTests extends RulesRestTest
public void createRuleWithActions_adminCanUsePrivateAction()
{
restClient.authenticateUser(dataUser.getAdminUser()).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(rulesUtils.createRuleWithPrivateAction());
.createSingleRule(rulesUtils.createRuleWithPrivateAction());
restClient.assertStatusCodeIs(CREATED);
}
@@ -656,8 +644,7 @@ public class CreateRulesTests extends RulesRestTest
public void createRuleWithNotApplicableActionShouldFail()
{
final RestRuleModel ruleModel = rulesUtils.createRuleModelWithDefaultValues();
final RestActionBodyExecTemplateModel invalidAction =
rulesUtils.createCustomActionModel(RulesTestsUtils.DELETE_RENDITION_ACTION, Map.of("dummy-key", "dummy-value"));
final RestActionBodyExecTemplateModel invalidAction = rulesUtils.createCustomActionModel(RulesTestsUtils.DELETE_RENDITION_ACTION, Map.of("dummy-key", "dummy-value"));
ruleModel.setActions(List.of(invalidAction));
restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet().createSingleRule(ruleModel);
@@ -673,8 +660,7 @@ public class CreateRulesTests extends RulesRestTest
public void createRuleWithMissingActionParametersShouldFail()
{
final RestRuleModel ruleModel = rulesUtils.createRuleModelWithDefaultValues();
final RestActionBodyExecTemplateModel invalidAction =
rulesUtils.createCustomActionModel(RulesTestsUtils.COPY_ACTION, Collections.emptyMap());
final RestActionBodyExecTemplateModel invalidAction = rulesUtils.createCustomActionModel(RulesTestsUtils.COPY_ACTION, Collections.emptyMap());
ruleModel.setActions(List.of(invalidAction));
restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
@@ -736,7 +722,7 @@ public class CreateRulesTests extends RulesRestTest
public void createRuleWithoutMandatoryActionParametersShouldFail()
{
final RestRuleModel ruleModel = rulesUtils.createRuleModelWithDefaultValues();
final RestActionBodyExecTemplateModel invalidAction = rulesUtils.createCustomActionModel(COPY_ACTION, Map.of("deep-copy",false));
final RestActionBodyExecTemplateModel invalidAction = rulesUtils.createCustomActionModel(COPY_ACTION, Map.of("deep-copy", false));
ruleModel.setActions(List.of(invalidAction));
restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
@@ -749,7 +735,7 @@ public class CreateRulesTests extends RulesRestTest
/**
* Check we get error when attempting to create a rule that copies files to a non-existent folder.
*/
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleThatUsesNonExistentNode()
{
RestRuleModel ruleModel = rulesUtils.createRuleModelWithDefaultValues();
@@ -758,16 +744,16 @@ public class CreateRulesTests extends RulesRestTest
ruleModel.setActions(List.of(invalidAction));
restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
.createSingleRule(ruleModel);
restClient.assertStatusCodeIs(NOT_FOUND);
restClient.assertLastError().containsSummary("The entity with id: non-existent-node was not found");
restClient.assertLastError().containsSummary("Destination folder having Id: non-existent-node no longer exists. Please update your rule definition.");
}
/**
* Check we get error when attempting to create a rule that references a folder that the user does not have read permission for.
*/
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleThatUsesNodeWithoutReadPermission()
{
SiteModel privateSite = dataSite.usingAdmin().createPrivateRandomSite();
@@ -779,7 +765,7 @@ public class CreateRulesTests extends RulesRestTest
ruleModel.setActions(List.of(invalidAction));
restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
.createSingleRule(ruleModel);
restClient.assertStatusCodeIs(NOT_FOUND);
restClient.assertLastError().containsSummary("The entity with id: " + privateFolder.getNodeRef() + " was not found");
@@ -788,7 +774,7 @@ public class CreateRulesTests extends RulesRestTest
/**
* Check we get error when attempting to create a rule that copies files to a folder that a user only has read permission for.
*/
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleThatWritesToNodeWithoutPermission()
{
SiteModel privateSite = dataSite.usingAdmin().createPrivateRandomSite();
@@ -802,7 +788,7 @@ public class CreateRulesTests extends RulesRestTest
ruleModel.setActions(List.of(invalidAction));
restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
.createSingleRule(ruleModel);
restClient.assertStatusCodeIs(FORBIDDEN);
restClient.assertLastError().containsSummary("No proper permissions for node: " + privateFolder.getNodeRef());
@@ -828,7 +814,6 @@ public class CreateRulesTests extends RulesRestTest
restClient.assertLastError().containsSummary("Node is not a folder " + fileModel.getNodeRef());
}
/**
* Check we get error when attempting to create a rule with mail action defined with non-existing mail template.
*/
@@ -850,7 +835,7 @@ public class CreateRulesTests extends RulesRestTest
ruleModel.setActions(List.of(mailAction));
restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
.createSingleRule(ruleModel);
restClient.assertStatusCodeIs(BAD_REQUEST);
restClient.assertLastError().containsSummary("Action parameter: template has invalid value (" + mailTemplate +
@@ -860,7 +845,7 @@ public class CreateRulesTests extends RulesRestTest
/**
* Check the user can create a rule with a script.
*/
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void checkCanUseScriptInRule()
{
RestRuleModel ruleModel = rulesUtils.createRuleModelWithDefaultValues();
@@ -869,7 +854,7 @@ public class CreateRulesTests extends RulesRestTest
ruleModel.setActions(List.of(scriptAction));
restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
.createSingleRule(ruleModel);
restClient.assertStatusCodeIs(CREATED);
}
@@ -877,7 +862,7 @@ public class CreateRulesTests extends RulesRestTest
/**
* Check the script has to be stored in the scripts directory in the data dictionary.
*/
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void checkCantUseNodeOutsideScriptsDirectory()
{
STEP("Copy script to location outside data dictionary.");
@@ -898,16 +883,16 @@ public class CreateRulesTests extends RulesRestTest
ruleModel.setActions(List.of(scriptAction));
restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
.createSingleRule(ruleModel);
restClient.assertStatusCodeIs(BAD_REQUEST)
.assertLastError().containsSummary("script-ref has invalid value");
.assertLastError().containsSummary("script-ref has invalid value");
}
/**
* Check a real category needs to be supplied when linking to a category.
*/
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void checkLinkToCategoryNeedsRealCategory()
{
STEP("Attempt to link to a category with a folder node, rather than a category node.");
@@ -918,7 +903,7 @@ public class CreateRulesTests extends RulesRestTest
ruleModel.setActions(List.of(categoryAction));
restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
.createSingleRule(ruleModel);
restClient.assertStatusCodeIs(BAD_REQUEST);
}
@@ -933,7 +918,7 @@ public class CreateRulesTests extends RulesRestTest
ruleModel.setConditions(rulesUtils.createVariousConditions());
RestRuleModel rule = restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
.createSingleRule(ruleModel);
RestRuleModel expectedRuleModel = rulesUtils.createRuleModelWithDefaultValues();
expectedRuleModel.setConditions(rulesUtils.createVariousConditions());
@@ -952,7 +937,7 @@ public class CreateRulesTests extends RulesRestTest
ruleModel.setConditions(rulesUtils.createCompositeCondition(null));
RestRuleModel rule = restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
.createSingleRule(ruleModel);
RestRuleModel expectedRuleModel = rulesUtils.createRuleModelWithDefaultValues();
expectedRuleModel.setTriggers(List.of("inbound"));
@@ -969,10 +954,8 @@ public class CreateRulesTests extends RulesRestTest
STEP("Try to create a rule with non existing category in conditions.");
String fakeCategoryId = "bdba5f9f-fake-id22-803b-349bcfd06fd1";
RestCompositeConditionDefinitionModel conditions = rulesUtils.createCompositeCondition(List.of(
rulesUtils.createCompositeCondition(!INVERTED, List.of(
rulesUtils.createSimpleCondition("category", "equals", fakeCategoryId)
))
));
rulesUtils.createCompositeCondition(!INVERTED, List.of(
rulesUtils.createSimpleCondition("category", "equals", fakeCategoryId)))));
RestRuleModel ruleModel = rulesUtils.createRuleModelWithDefaultValues();
ruleModel.setConditions(conditions);
@@ -992,9 +975,7 @@ public class CreateRulesTests extends RulesRestTest
final String comparator = "greaterthan";
RestCompositeConditionDefinitionModel conditions = rulesUtils.createCompositeCondition(List.of(
rulesUtils.createCompositeCondition(!INVERTED, List.of(
rulesUtils.createSimpleCondition("size", comparator, "500")
))
));
rulesUtils.createSimpleCondition("size", comparator, "500")))));
RestRuleModel ruleModel = rulesUtils.createRuleModelWithDefaultValues();
ruleModel.setConditions(conditions);

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<developers>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<properties>

View File

@@ -4,7 +4,7 @@
<!-- Resource defaultTransactionIsolation="-1" defaultAutoCommit="false" maxActive="100" initialSize="10" password="alfresco" username="alfresco" url="jdbc:mysql:///alfresco" driverClassName="org.gjt.mm.mysql.Driver" type="javax.sql.DataSource" auth="Container" name="jdbc/dataSource"/-->
<Environment override="false" type="java.lang.Boolean" name="properties/startup.enable" description="A flag that globally enables or disables startup of the major Alfresco subsystems." value="true"/>
<Environment override="false" type="java.lang.String" name="properties/dir.root" description="The filesystem directory below which content and index data is stored. Should be on a shared disk if this is a clustered installation."/>
<Environment override="false" type="java.lang.String" name="properties/hibernate.dialect" description="The fully qualified name of a org.hibernate.dialect.Dialect subclass that allows Hibernate to generate SQL optimized for a particular relational database. Choose from org.hibernate.dialect.DerbyDialect, org.hibernate.dialect.MySQLInnoDBDialect, org.alfresco.repo.domain.hibernate.dialect.AlfrescoOracle9Dialect, org.alfresco.repo.domain.hibernate.dialect.AlfrescoSybaseAnywhereDialect, org.alfresco.repo.domain.hibernate.dialect.AlfrescoSQLServerDialect, org.hibernate.dialect.PostgreSQLDialect"/>
<Environment override="false" type="java.lang.String" name="properties/hibernate.dialect" description="The fully qualified name of a org.hibernate.dialect.Dialect subclass that allows Hibernate to generate SQL optimized for a particular relational database. Choose from org.hibernate.dialect.DerbyDialect, org.hibernate.dialect.MySQLInnoDBDialect, org.alfresco.repo.domain.hibernate.dialect.AlfrescoOracle9Dialect, org.alfresco.repo.domain.hibernate.dialect.AlfrescoSybaseAnywhereDialect, org.alfresco.repo.domain.hibernate.dialect.SQLServerDialect, org.hibernate.dialect.PostgreSQLDialect"/>
<Environment override="false" type="java.lang.String" name="properties/hibernate.query.substitutions" description="Mapping from tokens in Hibernate queries to SQL tokens. For PostgreSQL, set this to &quot;true TRUE, false FALSE&quot;."/>
<Environment override="false" type="java.lang.Boolean" name="properties/hibernate.jdbc.use_get_generated_keys" description="Enable use of JDBC3 PreparedStatement.getGeneratedKeys() to retrieve natively generated keys after insert. Requires JDBC3+ driver. Set to false if your driver has problems with the Hibernate identifier generators. By default, tries to determine the driver capabilities using connection metadata."/>
<Environment override="false" type="java.lang.String" name="properties/hibernate.default_schema" description="Qualify unqualified table names with the given schema/tablespace in generated SQL. It may be necessary to set this when the target database has more than one schema."/>

View File

@@ -500,7 +500,7 @@
org.hibernate.dialect.MySQLInnoDBDialect,
org.alfresco.repo.domain.hibernate.dialect.AlfrescoOracle9Dialect,
org.alfresco.repo.domain.hibernate.dialect.AlfrescoSybaseAnywhereDialect,
org.alfresco.repo.domain.hibernate.dialect.AlfrescoSQLServerDialect, org.hibernate.dialect.PostgreSQLDialect</description>
org.alfresco.repo.domain.hibernate.dialect.SQLServerDialect, org.hibernate.dialect.PostgreSQLDialect</description>
<env-entry-name>properties/hibernate.dialect</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value/> <!-- Empty value included for JBoss compatibility -->

View File

@@ -74,7 +74,7 @@ ModuleDetails shareServicesModule = moduleService.getModule("alfresco-share-serv
<div class="index-list">
<h4><%=descriptorService.getServerDescriptor().getEdition()%></h4>
<p></p>
<p><a href="http://docs.alfresco.com/">Online Documentation</a></p>
<p><a href="https://support.hyland.com/p/alfresco">Online Documentation</a></p>
<p></p>
<%
if (shareServicesModule != null && ModuleInstallState.INSTALLED.equals(shareServicesModule.getInstallState()))

22
pom.xml
View File

@@ -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">
<modelVersion>4.0.0</modelVersion>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
<packaging>pom</packaging>
<name>Alfresco Community Repo Parent</name>
@@ -24,7 +24,7 @@
<properties>
<acs.version.major>23</acs.version.major>
<acs.version.minor>5</acs.version.minor>
<acs.version.minor>6</acs.version.minor>
<acs.version.revision>0</acs.version.revision>
<acs.version.label />
<amp.min.version>${acs.version.major}.0.0</amp.min.version>
@@ -58,10 +58,10 @@
<dependency.aspectj.version>1.9.22.1</dependency.aspectj.version>
<dependency.spring.version>6.1.14</dependency.spring.version>
<dependency.spring-security.version>6.3.4</dependency.spring-security.version>
<dependency.spring-security.version>6.3.8</dependency.spring-security.version>
<dependency.antlr.version>3.5.3</dependency.antlr.version>
<dependency.jackson.version>2.17.2</dependency.jackson.version>
<dependency.cxf.version>4.0.5</dependency.cxf.version>
<dependency.cxf.version>4.1.0</dependency.cxf.version>
<dependency.opencmis.version>1.0.0-jakarta-1</dependency.opencmis.version>
<dependency.webscripts.version>9.4</dependency.webscripts.version>
<dependency.bouncycastle.version>1.78.1</dependency.bouncycastle.version>
@@ -83,10 +83,10 @@
<dependency.groovy.version>3.0.22</dependency.groovy.version>
<dependency.tika.version>2.9.2</dependency.tika.version>
<dependency.truezip.version>7.7.10</dependency.truezip.version>
<dependency.poi.version>5.3.0</dependency.poi.version>
<dependency.poi.version>5.4.1</dependency.poi.version>
<dependency.jboss.logging.version>3.5.0.Final</dependency.jboss.logging.version>
<dependency.camel.version>4.6.0</dependency.camel.version> <!-- when bumping this version, please keep track/sync with included netty.io dependencies -->
<dependency.netty.version>4.1.113.Final</dependency.netty.version> <!-- must be in sync with camels transitive dependencies, e.g.: netty-common -->
<dependency.camel.version>4.10.2</dependency.camel.version> <!-- when bumping this version, please keep track/sync with included netty.io dependencies -->
<dependency.netty.version>4.1.118.Final</dependency.netty.version> <!-- must be in sync with camels transitive dependencies, e.g.: netty-common -->
<dependency.activemq.version>5.18.3</dependency.activemq.version>
<dependency.apache-compress.version>1.27.1</dependency.apache-compress.version>
<dependency.awaitility.version>4.2.2</dependency.awaitility.version>
@@ -111,9 +111,9 @@
<dependency.jakarta-ee-json-api.version>2.1.3</dependency.jakarta-ee-json-api.version>
<dependency.jakarta-ee-json-impl.version>1.1.7</dependency.jakarta-ee-json-impl.version>
<dependency.jakarta-json-path.version>2.9.0</dependency.jakarta-json-path.version>
<dependency.json-smart.version>2.5.1</dependency.json-smart.version>
<dependency.json-smart.version>2.5.2</dependency.json-smart.version>
<alfresco.googledrive.version>4.1.0</alfresco.googledrive.version>
<alfresco.aos-module.version>3.2.0</alfresco.aos-module.version>
<alfresco.aos-module.version>3.3.0</alfresco.aos-module.version>
<alfresco.api-explorer.version>23.4.0</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share -->
<alfresco.maven-plugin.version>2.2.0</alfresco.maven-plugin.version>
@@ -154,7 +154,7 @@
<connection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</connection>
<developerConnection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</developerConnection>
<url>https://github.com/Alfresco/alfresco-community-repo</url>
<tag>23.5.0.1</tag>
<tag>23.6.0.4</tag>
</scm>
<distributionManagement>
@@ -409,7 +409,7 @@
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
<version>1.11.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<dependencies>

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* 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
@@ -34,7 +34,7 @@ import org.alfresco.repo.management.subsystems.ActivateableBean;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.external.AdminConsoleAuthenticator;
import org.alfresco.repo.security.authentication.external.ExternalUserAuthenticator;
import org.alfresco.repo.security.authentication.external.RemoteUserMapper;
import org.alfresco.repo.web.auth.AuthenticationListener;
import org.alfresco.repo.web.auth.TicketCredentials;
@@ -55,7 +55,7 @@ import java.util.List;
import java.util.Set;
/**
* Authenticator to provide Remote User based Header authentication dropping back to Basic Auth otherwise.
* Authenticator to provide Remote User based Header authentication dropping back to Basic Auth otherwise.
* Statelessly authenticating via a secure header now does not require a Session so can be used with
* request-level load balancers which was not previously possible.
* <p>
@@ -73,9 +73,11 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
protected RemoteUserMapper remoteUserMapper;
protected AuthenticationComponent authenticationComponent;
protected AdminConsoleAuthenticator adminConsoleAuthenticator;
protected ExternalUserAuthenticator adminConsoleAuthenticator;
protected ExternalUserAuthenticator webScriptsHomeAuthenticator;
private boolean alwaysAllowBasicAuthForAdminConsole = true;
private boolean alwaysAllowBasicAuthForWebScriptsHome = true;
List<String> adminConsoleScriptFamilies;
long getRemoteUserTimeoutMilliseconds = GET_REMOTE_USER_TIMEOUT_MILLISECONDS_DEFAULT;
@@ -84,7 +86,7 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
{
this.remoteUserMapper = remoteUserMapper;
}
public void setAuthenticationComponent(AuthenticationComponent authenticationComponent)
{
this.authenticationComponent = authenticationComponent;
@@ -100,6 +102,16 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
this.alwaysAllowBasicAuthForAdminConsole = alwaysAllowBasicAuthForAdminConsole;
}
public boolean isAlwaysAllowBasicAuthForWebScriptsHome()
{
return alwaysAllowBasicAuthForWebScriptsHome;
}
public void setAlwaysAllowBasicAuthForWebScriptsHome(boolean alwaysAllowBasicAuthForWebScriptsHome)
{
this.alwaysAllowBasicAuthForWebScriptsHome = alwaysAllowBasicAuthForWebScriptsHome;
}
public List<String> getAdminConsoleScriptFamilies()
{
return adminConsoleScriptFamilies;
@@ -121,11 +133,17 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
}
public void setAdminConsoleAuthenticator(
AdminConsoleAuthenticator adminConsoleAuthenticator)
ExternalUserAuthenticator adminConsoleAuthenticator)
{
this.adminConsoleAuthenticator = adminConsoleAuthenticator;
}
public void setWebScriptsHomeAuthenticator(
ExternalUserAuthenticator webScriptsHomeAuthenticator)
{
this.webScriptsHomeAuthenticator = webScriptsHomeAuthenticator;
}
@Override
public Authenticator create(WebScriptServletRequest req, WebScriptServletResponse res)
{
@@ -139,11 +157,13 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
*/
public class RemoteUserAuthenticator extends BasicHttpAuthenticator
{
private static final String WEB_SCRIPTS_BASE_PATH = "org/springframework/extensions/webscripts";
public RemoteUserAuthenticator(WebScriptServletRequest req, WebScriptServletResponse res, AuthenticationListener listener)
{
super(req, res, listener);
}
@Override
public boolean authenticate(RequiredAuthentication required, boolean isGuest)
{
@@ -159,24 +179,47 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
{
if (servletReq.getServiceMatch() != null &&
isAdminConsoleWebScript(servletReq.getServiceMatch().getWebScript()) && isAdminConsoleAuthenticatorActive())
isAdminConsole(servletReq.getServiceMatch().getWebScript()) && isAdminConsoleAuthenticatorActive())
{
userId = getAdminConsoleUser();
}
else if (servletReq.getServiceMatch() != null &&
isWebScriptsHome(servletReq.getServiceMatch().getWebScript()) && isWebScriptsHomeAuthenticatorActive())
{
userId = getWebScriptsHomeUser();
}
if (userId == null)
{
if (isAlwaysAllowBasicAuthForAdminConsole())
{
final boolean useTimeoutForAdminAccessingAdminConsole = shouldUseTimeoutForAdminAccessingAdminConsole(required, isGuest);
boolean shouldUseTimeout = shouldUseTimeoutForAdminAccessingAdminConsole(required, isGuest);
if (useTimeoutForAdminAccessingAdminConsole && isBasicAuthHeaderPresentForAdmin())
if (shouldUseTimeout && isBasicAuthHeaderPresentForAdmin())
{
return callBasicAuthForAdminConsoleAccess(required, isGuest);
return callBasicAuthForAdminConsoleOrWebScriptsHomeAccess(required, isGuest);
}
try
{
userId = getRemoteUserWithTimeout(useTimeoutForAdminAccessingAdminConsole);
userId = getRemoteUserWithTimeout(shouldUseTimeout);
}
catch (AuthenticationTimeoutException e)
{
// return basic auth challenge
return false;
}
}
else if (isAlwaysAllowBasicAuthForWebScriptsHome())
{
boolean shouldUseTimeout = shouldUseTimeoutForAdminAccessingWebScriptsHome(required, isGuest);
if (shouldUseTimeout && isBasicAuthHeaderPresentForAdmin())
{
return callBasicAuthForAdminConsoleOrWebScriptsHomeAccess(required, isGuest);
}
try
{
userId = getRemoteUserWithTimeout(shouldUseTimeout);
}
catch (AuthenticationTimeoutException e)
{
@@ -255,38 +298,63 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
authenticated = super.authenticate(required, isGuest);
}
}
if(!authenticated && servletReq.getServiceMatch() != null &&
isAdminConsoleWebScript(servletReq.getServiceMatch().getWebScript()) && isAdminConsoleAuthenticatorActive())
if (!authenticated && servletReq.getServiceMatch() != null)
{
adminConsoleAuthenticator.requestAuthentication(this.servletReq.getHttpServletRequest(), this.servletRes.getHttpServletResponse());
WebScript webScript = servletReq.getServiceMatch().getWebScript();
if (isAdminConsole(webScript) && isAdminConsoleAuthenticatorActive())
{
adminConsoleAuthenticator.requestAuthentication(
this.servletReq.getHttpServletRequest(),
this.servletRes.getHttpServletResponse());
}
else if (isWebScriptsHome(webScript)
&& isWebScriptsHomeAuthenticatorActive())
{
webScriptsHomeAuthenticator.requestAuthentication(
this.servletReq.getHttpServletRequest(),
this.servletRes.getHttpServletResponse());
}
}
return authenticated;
}
private boolean callBasicAuthForAdminConsoleAccess(RequiredAuthentication required, boolean isGuest)
private boolean callBasicAuthForAdminConsoleOrWebScriptsHomeAccess(RequiredAuthentication required, boolean isGuest)
{
// return REST call, after a timeout/basic auth challenge
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("An Admin Console request has come in with Basic Auth headers present for an admin user.");
LOGGER.trace("An Admin Console or WebScripts Home request has come in with Basic Auth headers present for an admin user.");
}
// In order to prompt for another password, in case it was not entered correctly,
// the output of this method should be returned by the calling "authenticate" method;
// This would also mean, that once the admin basic auth header is present,
// the authentication chain will not be used for the admin console access
// the authentication chain will not be used for access
return super.authenticate(required, isGuest);
}
private boolean shouldUseTimeoutForAdminAccessingAdminConsole(RequiredAuthentication required, boolean isGuest)
{
boolean useTimeoutForAdminAccessingAdminConsole = RequiredAuthentication.admin.equals(required) && !isGuest &&
servletReq.getServiceMatch() != null && isAdminConsoleWebScript(servletReq.getServiceMatch().getWebScript());
boolean adminConsoleTimeout = RequiredAuthentication.admin.equals(required) && !isGuest &&
servletReq.getServiceMatch() != null && isAdminConsole(servletReq.getServiceMatch().getWebScript());
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("Should ensure that the admins can login with basic auth: " + useTimeoutForAdminAccessingAdminConsole);
LOGGER.trace("Should ensure that the admins can login with basic auth: " + adminConsoleTimeout);
}
return useTimeoutForAdminAccessingAdminConsole;
return adminConsoleTimeout;
}
private boolean shouldUseTimeoutForAdminAccessingWebScriptsHome(RequiredAuthentication required, boolean isGuest)
{
boolean adminWebScriptsHomeTimeout = RequiredAuthentication.admin.equals(required) && !isGuest &&
servletReq.getServiceMatch() != null && isWebScriptsHome(servletReq.getServiceMatch().getWebScript());
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("Should ensure that the admins can login with basic auth: " + adminWebScriptsHomeTimeout);
}
return adminWebScriptsHomeTimeout;
}
private boolean isRemoteUserMapperActive()
@@ -299,7 +367,12 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
return adminConsoleAuthenticator != null && (!(adminConsoleAuthenticator instanceof ActivateableBean) || ((ActivateableBean) adminConsoleAuthenticator).isActive());
}
protected boolean isAdminConsoleWebScript(WebScript webScript)
private boolean isWebScriptsHomeAuthenticatorActive()
{
return webScriptsHomeAuthenticator != null && (!(webScriptsHomeAuthenticator instanceof ActivateableBean) || ((ActivateableBean) webScriptsHomeAuthenticator).isActive());
}
protected boolean isAdminConsole(WebScript webScript)
{
if (webScript == null || adminConsoleScriptFamilies == null || webScript.getDescription() == null
|| webScript.getDescription().getFamilys() == null)
@@ -313,7 +386,7 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
}
// intersect the "family" sets defined
Set<String> families = new HashSet<String>(webScript.getDescription().getFamilys());
Set<String> families = new HashSet<>(webScript.getDescription().getFamilys());
families.retainAll(adminConsoleScriptFamilies);
final boolean isAdminConsole = !families.isEmpty();
@@ -325,6 +398,23 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
return isAdminConsole;
}
protected boolean isWebScriptsHome(WebScript webScript)
{
if (webScript == null || webScript.toString() == null)
{
return false;
}
boolean isWebScriptsHome = webScript.toString().startsWith(WEB_SCRIPTS_BASE_PATH);
if (LOGGER.isTraceEnabled() && isWebScriptsHome)
{
LOGGER.trace("Detected a WebScripts Home webscript: " + webScript);
}
return isWebScriptsHome;
}
protected String getRemoteUserWithTimeout(boolean useTimeout) throws AuthenticationTimeoutException
{
if (!useTimeout)
@@ -394,7 +484,7 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
protected String getRemoteUser()
{
String userId = null;
// If the remote user mapper is configured, we may be able to map in an externally authenticated user
if (isRemoteUserMapperActive())
{
@@ -423,7 +513,21 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
if (isRemoteUserMapperActive())
{
userId = adminConsoleAuthenticator.getAdminConsoleUser(this.servletReq.getHttpServletRequest(), this.servletRes.getHttpServletResponse());
userId = adminConsoleAuthenticator.getUserId(this.servletReq.getHttpServletRequest(), this.servletRes.getHttpServletResponse());
}
logRemoteUserID(userId);
return userId;
}
protected String getWebScriptsHomeUser()
{
String userId = null;
if (isRemoteUserMapperActive())
{
userId = webScriptsHomeAuthenticator.getUserId(this.servletReq.getHttpServletRequest(), this.servletRes.getHttpServletResponse());
}
logRemoteUserID(userId);

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -21,7 +21,7 @@
*
* 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%
* #L%
*/
package org.alfresco.rest.api.impl;
@@ -85,7 +85,7 @@ public class AuditImpl implements Audit
// list of equals filter's auditEntry (via where clause)
private final static Set<String> LIST_AUDIT_ENTRY_EQUALS_QUERY_PROPERTIES = new HashSet<>(
Arrays.asList(new String[] { CREATED_BY_USER, VALUES_KEY, VALUES_VALUE }));
Arrays.asList(new String[]{CREATED_BY_USER, VALUES_KEY, VALUES_VALUE}));
// map of sort parameters for the moment one createdAt
private final static Map<String, String> SORT_PARAMS_TO_NAMES;
@@ -295,23 +295,29 @@ public class AuditImpl implements Audit
}
else
{
if (hasMoreItems) {
if (q != null) {
if (hasMoreItems)
{
if (q != null)
{
// filtering via "where" clause
AuditEntryQueryWalker propertyWalker = new AuditEntryQueryWalker();
QueryHelper.walk(q, propertyWalker);
totalItems = getAuditEntriesCountByAppAndProperties(auditApplication, propertyWalker);
} else {
}
else
{
totalItems = getAuditEntriesCountByApp(auditApplication);
}
} else {
}
else
{
totalItems = totalRetrievedItems;
}
}
entriesAudit = (skipCount >= totalRetrievedItems)
? Collections.emptyList()
: entriesAudit.subList(skipCount, end);
? Collections.emptyList()
: entriesAudit.subList(skipCount, end);
return CollectionWithPagingInfo.asPaged(paging, entriesAudit, hasMoreItems, totalItems);
}
@@ -475,8 +481,7 @@ public class AuditImpl implements Audit
final Map<String, UserInfo> mapUserInfo = new HashMap<>(10);
// create the callback for auditQuery method
final AuditQueryCallback callback = new AuditQueryCallback()
{
final AuditQueryCallback callback = new AuditQueryCallback() {
public boolean valuesRequired()
{
return ((includeParam != null) && (includeParam.contains(PARAM_INCLUDE_VALUES)));
@@ -532,7 +537,7 @@ public class AuditImpl implements Audit
params.setApplicationName(auditApplication.getName());
params.setFromId(auditEntryId);
params.setToId(auditEntryId + 1);
List<String> includeParam = new ArrayList<>();
if (parameters != null)
{
@@ -545,8 +550,7 @@ public class AuditImpl implements Audit
final List<AuditEntry> results = new ArrayList<>();
// create the callback for auditQuery method
final AuditQueryCallback callback = new AuditQueryCallback()
{
final AuditQueryCallback callback = new AuditQueryCallback() {
public boolean valuesRequired()
{
return ((includeParam != null) && (includeParam.contains(PARAM_INCLUDE_VALUES)));
@@ -710,8 +714,7 @@ public class AuditImpl implements Audit
@Override
public CollectionWithPagingInfo<AuditEntry> listAuditEntriesByNodeId(String nodeId, Parameters parameters)
{
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
AuthenticationUtil.runAs(new RunAsWork<Void>() {
public Void doWork() throws Exception
{
checkEnabled();
@@ -772,8 +775,7 @@ public class AuditImpl implements Audit
final List<AuditEntry> results = new ArrayList<>();
String auditAppId = "alfresco-access";
String auditApplicationName = AuthenticationUtil.runAs(new RunAsWork<String>()
{
String auditApplicationName = AuthenticationUtil.runAs(new RunAsWork<String>() {
public String doWork() throws Exception
{
return findAuditAppByIdOr404(auditAppId).getName();
@@ -781,8 +783,7 @@ public class AuditImpl implements Audit
}, AuthenticationUtil.getSystemUserName());
// create the callback for auditQuery method
final AuditQueryCallback callback = new AuditQueryCallback()
{
final AuditQueryCallback callback = new AuditQueryCallback() {
public boolean valuesRequired()
{
return ((includeParam != null) && (includeParam.contains(PARAM_INCLUDE_VALUES)));
@@ -808,8 +809,7 @@ public class AuditImpl implements Audit
Long toTime = propertyWalker.getToTime();
validateWhereBetween(nodeRef.getId(), fromTime, toTime);
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
AuthenticationUtil.runAs(new RunAsWork<Object>() {
public Object doWork() throws Exception
{
// QueryParameters
@@ -820,7 +820,7 @@ public class AuditImpl implements Audit
pathParams.setFromTime(fromTime);
pathParams.setToTime(toTime);
pathParams.setApplicationName(auditApplicationName);
pathParams.addSearchKey("/"+auditAppId+"/transaction/path", nodePath);
pathParams.addSearchKey("/" + auditAppId + "/transaction/path", nodePath);
auditService.auditQuery(callback, pathParams, limit);
AuditQueryParameters copyFromPathParams = new AuditQueryParameters();
@@ -830,7 +830,7 @@ public class AuditImpl implements Audit
copyFromPathParams.setFromTime(fromTime);
copyFromPathParams.setToTime(toTime);
copyFromPathParams.setApplicationName(auditApplicationName);
copyFromPathParams.addSearchKey("/"+auditAppId+"/transaction/copy/from/path", nodePath);
copyFromPathParams.addSearchKey("/" + auditAppId + "/transaction/copy/from/path", nodePath);
auditService.auditQuery(callback, copyFromPathParams, limit);
AuditQueryParameters moveFromPathParams = new AuditQueryParameters();
@@ -840,7 +840,7 @@ public class AuditImpl implements Audit
moveFromPathParams.setFromTime(fromTime);
moveFromPathParams.setToTime(toTime);
moveFromPathParams.setApplicationName(auditApplicationName);
moveFromPathParams.addSearchKey("/"+auditAppId+"/transaction/move/from/path", nodePath);
moveFromPathParams.addSearchKey("/" + auditAppId + "/transaction/move/from/path", nodePath);
auditService.auditQuery(callback, moveFromPathParams, limit);
return null;
@@ -857,7 +857,7 @@ public class AuditImpl implements Audit
public AuditEntriesByNodeIdQueryWalker()
{
super(new HashSet<>(Arrays.asList(new String[] { CREATED_BY_USER })), null);
super(new HashSet<>(Arrays.asList(new String[]{CREATED_BY_USER})), null);
}
@Override
@@ -909,14 +909,16 @@ public class AuditImpl implements Audit
public int getAuditEntriesCountByAppAndProperties(AuditService.AuditApplication auditApplication, AuditEntryQueryWalker propertyWalker)
{
final String applicationName = auditApplication.getKey().substring(1);
AuditQueryParameters parameters = new AuditQueryParameters();
parameters.setApplicationName(auditApplication.getName());
parameters.setApplicationName(applicationName);
parameters.setFromTime(propertyWalker.getFromTime());
parameters.setToTime(propertyWalker.getToTime());
parameters.setFromId(propertyWalker.getFromId());
parameters.setToId(propertyWalker.getToId());
parameters.setUser(propertyWalker.getCreatedByUser());
return auditService.getAuditEntriesCountByAppAndProperties(parameters);
return auditService.getAuditEntriesCountByAppAndProperties(applicationName, parameters);
}
}

View File

@@ -2,23 +2,23 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* 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
* 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%
@@ -38,11 +38,19 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.extensions.surf.util.I18NUtil;
import org.alfresco.model.ContentModel;
import org.alfresco.query.PagingRequest;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.site.SiteModel;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.People;
@@ -77,8 +85,6 @@ import org.alfresco.util.AlfrescoCollator;
import org.alfresco.util.ISO9075;
import org.alfresco.util.ParameterCheck;
import org.alfresco.util.SearchLanguageConversion;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* Queries implementation
@@ -88,43 +94,43 @@ import org.springframework.extensions.surf.util.I18NUtil;
*/
public class QueriesImpl implements Queries, InitializingBean
{
private final static Map<String,QName> NODE_SORT_PARAMS_TO_QNAMES = sortParamsToQNames(
PARAM_NAME, ContentModel.PROP_NAME,
PARAM_CREATEDAT, ContentModel.PROP_CREATED,
PARAM_MODIFIEDAT, ContentModel.PROP_MODIFIED);
private static final Log LOGGER = LogFactory.getLog(QueriesImpl.class);
private final static Map<String, QName> NODE_SORT_PARAMS_TO_QNAMES = sortParamsToQNames(
PARAM_NAME, ContentModel.PROP_NAME,
PARAM_CREATEDAT, ContentModel.PROP_CREATED,
PARAM_MODIFIEDAT, ContentModel.PROP_MODIFIED);
private final static Map<String, QName> PEOPLE_SORT_PARAMS_TO_QNAMES = sortParamsToQNames(
PARAM_PERSON_ID, ContentModel.PROP_USERNAME,
ContentModel.PROP_FIRSTNAME,
ContentModel.PROP_LASTNAME);
PARAM_PERSON_ID, ContentModel.PROP_USERNAME,
ContentModel.PROP_FIRSTNAME,
ContentModel.PROP_LASTNAME);
private final static Map<String, QName> SITE_SORT_PARAMS_TO_QNAMES = sortParamsToQNames(
PARAM_SITE_ID, ContentModel.PROP_NAME,
PARAM_SITE_TITLE, ContentModel.PROP_TITLE,
PARAM_SITE_ID, ContentModel.PROP_NAME,
PARAM_SITE_TITLE, ContentModel.PROP_TITLE,
PARAM_SITE_DESCRIPTION, ContentModel.PROP_DESCRIPTION);
/**
* Helper method to build a map of sort parameter names to QNames. This method iterates through
* the parameters. If a parameter is a String it is assumed to be a sort parameter name and will
* be followed by a QName to which it maps. If however it is a QName the local name of the OName
* is used as the sort parameter name.
* @param parameters to build up the map.
* Helper method to build a map of sort parameter names to QNames. This method iterates through the parameters. If a parameter is a String it is assumed to be a sort parameter name and will be followed by a QName to which it maps. If however it is a QName the local name of the OName is used as the sort parameter name.
*
* @param parameters
* to build up the map.
* @return the map
*/
private static Map<String, QName> sortParamsToQNames(Object... parameters)
{
Map<String, QName> map = new HashMap<>();
for (int i=0; i<parameters.length; i++)
for (int i = 0; i < parameters.length; i++)
{
map.put(
parameters[i] instanceof String
? (String)parameters[i++]
: ((QName)parameters[i]).getLocalName(),
(QName)parameters[i]);
parameters[i] instanceof String
? (String) parameters[i++]
: ((QName) parameters[i]).getLocalName(),
(QName) parameters[i]);
}
return Collections.unmodifiableMap(map);
}
private ServiceRegistry sr;
private NodeService nodeService;
private NamespaceService namespaceService;
@@ -162,7 +168,7 @@ public class QueriesImpl implements Queries, InitializingBean
ParameterCheck.mandatory("nodes", this.nodes);
ParameterCheck.mandatory("people", this.people);
ParameterCheck.mandatory("sites", this.sites);
this.nodeService = sr.getNodeService();
this.namespaceService = sr.getNamespaceService();
this.dictionaryService = sr.getDictionaryService();
@@ -173,10 +179,9 @@ public class QueriesImpl implements Queries, InitializingBean
public CollectionWithPagingInfo<Node> findNodes(Parameters parameters)
{
SearchService searchService = sr.getSearchService();
return new AbstractQuery<Node>(nodeService, searchService)
{
return new AbstractQuery<Node>(nodeService, searchService) {
private final Map<String, UserInfo> mapUserInfo = new HashMap<>(10);
@Override
protected void buildQuery(StringBuilder query, String term, SearchParameters sp, String queryTemplateName)
{
@@ -198,14 +203,14 @@ public class QueriesImpl implements Queries, InitializingBean
{
query.append(")");
}
String nodeTypeStr = parameters.getParameter(PARAM_NODE_TYPE);
if (nodeTypeStr != null)
{
QName filterNodeTypeQName = nodes.createQName(nodeTypeStr);
if (dictionaryService.getType(filterNodeTypeQName) == null)
{
throw new InvalidArgumentException("Unknown filter nodeType: "+nodeTypeStr);
throw new InvalidArgumentException("Unknown filter nodeType: " + nodeTypeStr);
}
query.append(" AND (+TYPE:\"").append(nodeTypeStr).append(("\")"));
@@ -229,7 +234,7 @@ public class QueriesImpl implements Queries, InitializingBean
Path path = null;
try
{
path = nodeService.getPath(nodeRef);
path = nodeService.getPath(nodeRef);
}
catch (InvalidNodeRefException inre)
{
@@ -248,7 +253,7 @@ public class QueriesImpl implements Queries, InitializingBean
{
// first request for this namespace prefix, get and cache result
Collection<String> prefixes = namespaceService.getPrefixes(qname.getNamespaceURI());
prefix = prefixes.size() != 0 ? prefixes.iterator().next() : "";
prefix = !prefixes.isEmpty() ? prefixes.iterator().next() : "";
cache.put(qname.getNamespaceURI(), prefix);
}
buf.append('/').append(prefix).append(':').append(ISO9075.encode(qname.getLocalName()));
@@ -262,12 +267,6 @@ public class QueriesImpl implements Queries, InitializingBean
return buf.toString();
}
@Override
protected List<Node> newList(int capacity)
{
return new ArrayList<Node>(capacity);
}
@Override
protected Node convert(NodeRef nodeRef, List<String> includeParam)
{
@@ -281,18 +280,17 @@ public class QueriesImpl implements Queries, InitializingBean
term = SearchLanguageConversion.escapeLuceneQuery(term);
return term;
}
}.find(parameters, PARAM_TERM, MIN_TERM_LENGTH_NODES, "keywords",
IN_QUERY_SORT, NODE_SORT_PARAMS_TO_QNAMES,
new SortColumn(PARAM_MODIFIEDAT, false));
IN_QUERY_SORT, NODE_SORT_PARAMS_TO_QNAMES,
new SortColumn(PARAM_MODIFIEDAT, false));
}
@Override
public CollectionWithPagingInfo<Person> findPeople(Parameters parameters)
{
SearchService searchService = sr.getSearchService();
return new AbstractQuery<Person>(nodeService, searchService)
{
return new AbstractQuery<Person>(nodeService, searchService) {
@Override
protected void buildQuery(StringBuilder query, String term, SearchParameters sp, String queryTemplateName)
{
@@ -305,33 +303,25 @@ public class QueriesImpl implements Queries, InitializingBean
query.append("*\")");
}
@Override
protected List<Person> newList(int capacity)
{
return new ArrayList<Person>(capacity);
}
@Override
protected Person convert(NodeRef nodeRef, List<String> includeParam)
{
String personId = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME);
Person person = people.getPerson(personId);
return person;
return people.getPerson(personId);
}
// TODO Do the sort in the query on day. A comment in the code for the V0 API used for live people
// search says adding sort values for this query don't work - tried it and they really don't.
// search says adding sort values for this query don't work - tried it and they really don't.
}.find(parameters, PARAM_TERM, MIN_TERM_LENGTH_PEOPLE, "_PERSON",
POST_QUERY_SORT, PEOPLE_SORT_PARAMS_TO_QNAMES,
new SortColumn(PARAM_FIRSTNAME, true), new SortColumn(PARAM_LASTNAME, true));
POST_QUERY_SORT, PEOPLE_SORT_PARAMS_TO_QNAMES,
new SortColumn(PARAM_FIRSTNAME, true), new SortColumn(PARAM_LASTNAME, true));
}
@Override
public CollectionWithPagingInfo<Site> findSites(Parameters parameters)
{
SearchService searchService = sr.getSearchService();
return new AbstractQuery<Site>(nodeService, searchService)
{
return new AbstractQuery<Site>(nodeService, searchService) {
@Override
protected void buildQuery(StringBuilder query, String term, SearchParameters sp, String queryTemplateName)
{
@@ -343,44 +333,34 @@ public class QueriesImpl implements Queries, InitializingBean
query.append(term);
query.append("*\")");
}
@Override
protected List<Site> newList(int capacity)
{
return new ArrayList<>(capacity);
}
@Override
protected Site convert(NodeRef nodeRef, List<String> includeParam)
{
return getSite(siteService.getSite(nodeRef), true);
return getSite(siteService.getSite(nodeRef));
}
// note: see also Sites.getSite
private Site getSite(SiteInfo siteInfo, boolean includeRole)
private Site getSite(SiteInfo siteInfo)
{
// set the site id to the short name (to deal with case sensitivity issues with using the siteId from the url)
String siteId = siteInfo.getShortName();
String role = null;
if(includeRole)
{
role = sites.getSiteRole(siteId);
}
String role = sites.getSiteRole(siteId);
return new Site(siteInfo, role);
}
}.find(parameters, PARAM_TERM, MIN_TERM_LENGTH_SITES, "_SITE", POST_QUERY_SORT, SITE_SORT_PARAMS_TO_QNAMES, new SortColumn(PARAM_SITE_TITLE, true));
}
public abstract static class AbstractQuery<T>
{
public enum Sort
{
IN_QUERY_SORT, POST_QUERY_SORT
}
private final NodeService nodeService;
private final SearchService searchService;
public AbstractQuery(NodeService nodeService, SearchService searchService)
{
this.nodeService = nodeService;
@@ -388,14 +368,14 @@ public class QueriesImpl implements Queries, InitializingBean
}
public CollectionWithPagingInfo<T> find(Parameters parameters,
String termName, int minTermLength, String queryTemplateName,
Sort sort, Map<String, QName> sortParamsToQNames, SortColumn... defaultSort)
String termName, int minTermLength, String queryTemplateName,
Sort sort, Map<String, QName> sortParamsToQNames, SortColumn... defaultSort)
{
SearchParameters sp = new SearchParameters();
sp.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO);
sp.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
sp.setDefaultFieldName(queryTemplateName);
String term = getTerm(parameters, termName, minTermLength);
StringBuilder query = new StringBuilder();
@@ -404,7 +384,7 @@ public class QueriesImpl implements Queries, InitializingBean
Paging paging = parameters.getPaging();
PagingRequest pagingRequest = Util.getPagingRequest(paging);
List<SortColumn> defaultSortCols = (defaultSort != null ? Arrays.asList(defaultSort) : Collections.emptyList());
if (sort == IN_QUERY_SORT)
{
@@ -413,36 +393,43 @@ public class QueriesImpl implements Queries, InitializingBean
sp.setSkipCount(pagingRequest.getSkipCount());
sp.setMaxItems(pagingRequest.getMaxItems());
}
ResultSet queryResults = null;
List<T> collection = null;
try
{
queryResults = searchService.query(sp);
List<NodeRef> nodeRefs = queryResults.getNodeRefs();
if (sort == POST_QUERY_SORT)
{
nodeRefs = postQuerySort(parameters, sortParamsToQNames, defaultSortCols, nodeRefs);
}
collection = newList(nodeRefs.size());
Map<NodeRef, T>
collection = new LinkedHashMap<>(nodeRefs.size());
List<String> includeParam = parameters.getInclude();
for (NodeRef nodeRef : nodeRefs)
{
T t = convert(nodeRef, includeParam);
collection.add(t);
try
{
T t = convert(nodeRef, includeParam);
collection.put(nodeRef, t);
}
catch (AccessDeniedException ade)
{
LOGGER.debug("Ignoring search result for nodeRef " + nodeRef + " due to access denied exception", ade);
}
}
if (sort == POST_QUERY_SORT)
{
return listPage(collection, paging);
List<T> postQuerySortedCollection = postQuerySort(parameters, sortParamsToQNames, defaultSortCols, collection.keySet())
.stream()
.map(collection::get)
.toList();
return listPage(postQuerySortedCollection, paging);
}
else
{
return CollectionWithPagingInfo.asPaged(paging, collection, queryResults.hasMore(), Long.valueOf(queryResults.getNumberFound()).intValue());
return CollectionWithPagingInfo.asPaged(paging, collection.values(), queryResults.hasMore(), Long.valueOf(queryResults.getNumberFound()).intValue());
}
}
finally
@@ -455,40 +442,39 @@ public class QueriesImpl implements Queries, InitializingBean
}
/**
* Builds up the query and is expected to call {@link SearchParameters#setDefaultFieldName(String)}
* and {@link SearchParameters#addQueryTemplate(String, String)}
* @param query StringBuilder into which the query should be built.
* @param term to be searched for
* @param sp SearchParameters
* Builds up the query and is expected to call {@link SearchParameters#setDefaultFieldName(String)} and {@link SearchParameters#addQueryTemplate(String, String)}
*
* @param query
* StringBuilder into which the query should be built.
* @param term
* to be searched for
* @param sp
* SearchParameters
* @param queryTemplateName
*/
protected abstract void buildQuery(StringBuilder query, String term, SearchParameters sp, String queryTemplateName);
/**
* Returns a list of the correct type.
* @param capacity of the list
* @return a new list.
*/
protected abstract List<T> newList(int capacity);
/**
* Converts a nodeRef into the an object of the required type.
* @param nodeRef to be converted
* @param includeParam additional fields to be included
*
* @param nodeRef
* to be converted
* @param includeParam
* additional fields to be included
* @return the object
*/
protected abstract T convert(NodeRef nodeRef, List<String> includeParam);
protected String getTerm(Parameters parameters, String termName, int minTermLength)
{
String term = parameters.getParameter(termName);
if (term == null)
{
throw new InvalidArgumentException("Query '"+termName+"' not specified");
throw new InvalidArgumentException("Query '" + termName + "' not specified");
}
term = escapeTerm(term);
int cnt = 0;
for (int i = 0; i < term.length(); i++)
{
@@ -505,7 +491,7 @@ public class QueriesImpl implements Queries, InitializingBean
if (cnt < minTermLength)
{
throw new InvalidArgumentException("Query '"+termName+"' is too short. Must have at least "+minTermLength+" alphanumeric chars");
throw new InvalidArgumentException("Query '" + termName + "' is too short. Must have at least " + minTermLength + " alphanumeric chars");
}
return term;
@@ -524,12 +510,12 @@ public class QueriesImpl implements Queries, InitializingBean
term = SearchLanguageConversion.escapeLuceneQuery(term);
return term;
}
/**
* Adds sort order to the SearchParameters.
*/
protected void addSortOrder(Parameters parameters, Map<String, QName> sortParamsToQNames,
List<SortColumn> defaultSortCols, SearchParameters sp)
List<SortColumn> defaultSortCols, SearchParameters sp)
{
List<SortColumn> sortCols = getSorting(parameters, defaultSortCols);
for (SortColumn sortCol : sortCols)
@@ -537,16 +523,16 @@ public class QueriesImpl implements Queries, InitializingBean
QName sortPropQName = sortParamsToQNames.get(sortCol.column);
if (sortPropQName == null)
{
throw new InvalidArgumentException("Invalid sort field: "+sortCol.column);
throw new InvalidArgumentException("Invalid sort field: " + sortCol.column);
}
sp.addSort("@" + sortPropQName, sortCol.asc);
sp.addSort("@" + sortPropQName, sortCol.asc);
}
}
private List<SortColumn> getSorting(Parameters parameters, List<SortColumn> defaultSortCols)
{
List<SortColumn> sortCols = parameters.getSorting();
if (sortCols == null || sortCols.size() == 0)
if (sortCols == null || sortCols.isEmpty())
{
sortCols = defaultSortCols == null ? Collections.emptyList() : defaultSortCols;
}
@@ -554,63 +540,66 @@ public class QueriesImpl implements Queries, InitializingBean
}
protected List<NodeRef> postQuerySort(Parameters parameters, Map<String, QName> sortParamsToQNames,
List<SortColumn> defaultSortCols, List<NodeRef> nodeRefs)
List<SortColumn> defaultSortCols, Set<NodeRef> unsortedNodeRefs)
{
final List<SortColumn> sortCols = getSorting(parameters, defaultSortCols);
int sortColCount = sortCols.size();
if (sortColCount > 0)
{
// make copy of nodeRefs because it can be unmodifiable list.
nodeRefs = new ArrayList<NodeRef>(nodeRefs);
List<QName> sortPropQNames = new ArrayList<>(sortColCount);
for (SortColumn sortCol : sortCols)
{
QName sortPropQName = sortParamsToQNames.get(sortCol.column);
if (sortPropQName == null)
{
throw new InvalidArgumentException("Invalid sort field: "+sortCol.column);
}
sortPropQNames.add(sortPropQName);
}
final Collator col = AlfrescoCollator.getInstance(I18NUtil.getLocale());
Collections.sort(nodeRefs, new Comparator<NodeRef>()
{
@Override
public int compare(NodeRef n1, NodeRef n2)
{
int result = 0;
for (int i=0; i<sortCols.size(); i++)
{
SortColumn sortCol = sortCols.get(i);
QName sortPropQName = sortPropQNames.get(i);
Serializable p1 = getProperty(n1, sortPropQName);
Serializable p2 = getProperty(n2, sortPropQName);
result = ((p1 instanceof Long) && (p2 instanceof Long)
? Long.compare((Long)p1, (Long)p2)
if (sortColCount == 0)
{
return new ArrayList<>(unsortedNodeRefs);
}
// make copy of nodeRefs because it can be unmodifiable list.
List<NodeRef> sortedNodeRefs = new ArrayList<>(unsortedNodeRefs);
List<QName> sortPropQNames = new ArrayList<>(sortColCount);
for (SortColumn sortCol : sortCols)
{
QName sortPropQName = sortParamsToQNames.get(sortCol.column);
if (sortPropQName == null)
{
throw new InvalidArgumentException("Invalid sort field: " + sortCol.column);
}
sortPropQNames.add(sortPropQName);
}
final Collator col = AlfrescoCollator.getInstance(I18NUtil.getLocale());
Collections.sort(sortedNodeRefs, new Comparator<NodeRef>() {
@Override
public int compare(NodeRef n1, NodeRef n2)
{
int result = 0;
for (int i = 0; i < sortCols.size(); i++)
{
SortColumn sortCol = sortCols.get(i);
QName sortPropQName = sortPropQNames.get(i);
Serializable p1 = getProperty(n1, sortPropQName);
Serializable p2 = getProperty(n2, sortPropQName);
result = ((p1 instanceof Long) && (p2 instanceof Long)
? Long.compare((Long) p1, (Long) p2)
: col.compare(p1.toString(), p2.toString()))
* (sortCol.asc ? 1 : -1);
if (result != 0)
{
break;
}
if (result != 0)
{
break;
}
return result;
}
return result;
}
private Serializable getProperty(NodeRef nodeRef, QName sortPropQName)
{
Serializable result = nodeService.getProperty(nodeRef, sortPropQName);
return result == null ? "" : result;
}
private Serializable getProperty(NodeRef nodeRef, QName sortPropQName)
{
Serializable result = nodeService.getProperty(nodeRef, sortPropQName);
return result == null ? "" : result;
}
});
}
return nodeRefs;
});
return sortedNodeRefs;
}
// note: see also AbstractNodeRelation

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* 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
@@ -39,6 +39,9 @@ import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.collections.MapUtils;
import org.apache.logging.log4j.util.Strings;
import org.alfresco.repo.action.executer.CheckOutActionExecuter;
import org.alfresco.repo.action.executer.CopyActionExecuter;
import org.alfresco.repo.action.executer.ImageTransformActionExecuter;
@@ -58,8 +61,6 @@ import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.apache.commons.collections.MapUtils;
import org.apache.logging.log4j.util.Strings;
/**
* This class provides logic for validation of permissions for action parameters which reference node.
@@ -67,15 +68,14 @@ import org.apache.logging.log4j.util.Strings;
public class ActionNodeParameterValidator implements ActionValidator
{
/**
* This list holds action parameter names which require only READ permission on a referenced node
* That means, all other parameters that reference nodes will require WRITE permission
* This list holds action parameter names which require only READ permission on a referenced node That means, all other parameters that reference nodes will require WRITE permission
*/
static final Map<String, List<String>> REQUIRE_READ_PERMISSION_PARAMS =
Map.of(LinkCategoryActionExecuter.NAME, List.of(LinkCategoryActionExecuter.PARAM_CATEGORY_VALUE));
static final Map<String, List<String>> REQUIRE_READ_PERMISSION_PARAMS = Map.of(LinkCategoryActionExecuter.NAME, List.of(LinkCategoryActionExecuter.PARAM_CATEGORY_VALUE));
static final String NO_PROPER_PERMISSIONS_FOR_NODE = "No proper permissions for node: ";
static final String NOT_A_CATEGORY = "Node is not a category ";
static final String NOT_A_FOLDER = "Node is not a folder ";
static final String NO_LONGER_EXISTS = "%s having Id: %s no longer exists. Please update your rule definition.";
private final Actions actions;
private final NamespaceService namespaceService;
@@ -83,7 +83,7 @@ public class ActionNodeParameterValidator implements ActionValidator
private final PermissionService permissionService;
public ActionNodeParameterValidator(Actions actions, NamespaceService namespaceService, Nodes nodes,
PermissionService permissionService)
PermissionService permissionService)
{
this.actions = actions;
this.namespaceService = namespaceService;
@@ -94,7 +94,8 @@ public class ActionNodeParameterValidator implements ActionValidator
/**
* Validates action parameters that reference nodes against access permissions for executing user.
*
* @param action Action to be validated
* @param action
* Action to be validated
*/
@Override
public void validate(Action action)
@@ -124,7 +125,7 @@ public class ActionNodeParameterValidator implements ActionValidator
}
private void validateNodes(final List<ActionDefinition.ParameterDefinition> nodeRefParamDefinitions,
final Action action)
final Action action)
{
if (MapUtils.isNotEmpty(action.getParams()))
{
@@ -132,7 +133,15 @@ public class ActionNodeParameterValidator implements ActionValidator
.filter(pd -> action.getParams().containsKey(pd.getName()))
.forEach(p -> {
final String nodeId = Objects.toString(action.getParams().get(p.getName()), Strings.EMPTY);
final NodeRef nodeRef = nodes.validateNode(nodeId);
NodeRef nodeRef;
try
{
nodeRef = nodes.validateNode(nodeId);
}
catch (EntityNotFoundException e)
{
throw new EntityNotFoundException(String.format(NO_LONGER_EXISTS, p.getDisplayLabel(), nodeId), e);
}
validatePermission(action.getActionDefinitionId(), p.getName(), nodeRef);
validateType(action.getActionDefinitionId(), nodeRef);
});
@@ -163,7 +172,8 @@ public class ActionNodeParameterValidator implements ActionValidator
{
throw new InvalidArgumentException(NOT_A_FOLDER + nodeRef.getId());
}
} else if (!nodes.nodeMatches(nodeRef, Set.of(TYPE_CATEGORY), Collections.emptySet()))
}
else if (!nodes.nodeMatches(nodeRef, Set.of(TYPE_CATEGORY), Collections.emptySet()))
{
throw new InvalidArgumentException(NOT_A_CATEGORY + nodeRef.getId());
}

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Admin Console
admin-console.help=Help
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Successfully saved values.
admin-console.host=Host

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Konzole pro spr\u00e1vce
admin-console.help=N\u00e1pov\u011bda
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Hodnoty byly \u00fasp\u011b\u0161n\u011b ulo\u017eeny.
admin-console.host=Hostitel

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Administrationskonsol
admin-console.help=Hj\u00e6lp
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=V\u00e6rdierne blev gemt.
admin-console.host=V\u00e6rt

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Administratorkonsole
admin-console.help=Hilfe
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Erfolgreich gespeicherte Werte.
admin-console.host=Host

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Consola de administraci\u00f3n
admin-console.help=Ayuda
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Valores guardados correctamente.
admin-console.host=Host

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Hallintakonsoli
admin-console.help=Ohje
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Arvot tallennettiin.
admin-console.host=Is\u00e4nt\u00e4

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Console d'administration
admin-console.help=Aide
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Les valeurs ont bien \u00e9t\u00e9 enregistr\u00e9es.
admin-console.host=H\u00f4te

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Console di amministrazione
admin-console.help=Aiuto
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=I valori sono stati salvati.
admin-console.host=Host

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=\u7ba1\u7406\u30b3\u30f3\u30bd\u30fc\u30eb
admin-console.help=\u30d8\u30eb\u30d7
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=\u5024\u3092\u6b63\u5e38\u306b\u4fdd\u5b58\u3057\u307e\u3057\u305f\u3002
admin-console.host=\u30db\u30b9\u30c8

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Admin-konsoll
admin-console.help=Hjelp
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Verdier som ble lagret.
admin-console.host=Vert

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Beheerconsole
admin-console.help=Help
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Waarden zijn opgeslagen.
admin-console.host=Host

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Konsola administracyjna
admin-console.help=Pomoc
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Warto\u015bci zosta\u0142y zapisane pomy\u015blnie.
admin-console.host=Host

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Console de administra\u00e7\u00e3o
admin-console.help=Ajuda
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Valores salvos com sucesso.
admin-console.host=Host

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=\u041a\u043e\u043d\u0441\u043e\u043b\u044c \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430
admin-console.help=\u0421\u043f\u0440\u0430\u0432\u043a\u0430
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f.
admin-console.host=\u0425\u043e\u0441\u0442

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Admin-konsol
admin-console.help=Hj\u00e4lp
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=V\u00e4rden sparades.
admin-console.host=V\u00e4rd

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=\u7ba1\u7406\u63a7\u5236\u53f0
admin-console.help=\u5e2e\u52a9
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=\u5df2\u6210\u529f\u4fdd\u5b58\u7684\u503c\u3002
admin-console.host=\u4e3b\u673a

View File

@@ -5,4 +5,4 @@
<authentication>guest</authentication>
<transaction allow="readonly">required</transaction>
<lifecycle>internal</lifecycle>
</webscript>
</webscript>

View File

@@ -12,9 +12,9 @@
<#macro page title readonly=false controller=DEFAULT_CONTROLLER!"/admin" params="" dialog=false>
<#assign FORM_ID="admin-jmx-form" />
<#if server.edition == "Community">
<#assign docsEdition = "community" />
<#assign docsEdition = "/Alfresco-Content-Services-Community-Edition/" + server.getVersionMajor() + "." + server.getVersionMinor() + "/Alfresco-Content-Services-Community-Edition" />
<#elseif server.edition == "Enterprise" >
<#assign docsEdition = server.getVersionMajor() + "." + server.getVersionMinor() />
<#assign docsEdition = "/Alfresco-Content-Services/" + server.getVersionMajor() + "." + server.getVersionMinor() + "/Alfresco-Content-Services" />
</#if>
<#if metadata??>
<#assign HOSTNAME>${msg("admin-console.host")}: ${metadata.hostname}</#assign>
@@ -551,7 +551,7 @@ Admin.addEventListener(window, 'load', function() {
Template for a full page view
-->
<div class="sticky-wrapper">
<div class="header">
<span><a href="${url.serviceContext}${DEFAULT_CONTROLLER!"/admin"}">${msg("admin-console.header")}</a></span><#if metadata??><span class="meta">${HOSTNAME}</span><span class="meta">${HOSTADDR}</span></#if>
<div style="float:right"><a href="${msg("admin-console.help-link", docsEdition)}" target="_blank">${msg("admin-console.help")}</a></div>
@@ -908,4 +908,4 @@ Admin.addEventListener(window, 'load', function() {
<#macro button label description="" onclick="" style="" id="" class="" disabled="false">
<input class="<#if class?has_content>${class?html}<#else>inline</#if>" <#if id?has_content>id="${id?html}"</#if> <#if style?has_content>style="${style?html}"</#if> type="button" value="${label?html}" onclick="${onclick?html}" <#if disabled="true">disabled="true"</#if> />
<#if description?has_content><span class="description">${description?html}</span></#if>
</#macro>
</#macro>

View File

@@ -214,9 +214,13 @@
<property name="authenticationListener" ref="webScriptAuthenticationListener"/>
<property name="remoteUserMapper" ref="RemoteUserMapper" />
<property name="adminConsoleAuthenticator" ref="AdminConsoleAuthenticator" />
<property name="webScriptsHomeAuthenticator" ref="WebScriptsHomeAuthenticator" />
<property name="alwaysAllowBasicAuthForAdminConsole">
<value>${authentication.alwaysAllowBasicAuthForAdminConsole.enabled}</value>
</property>
<property name="alwaysAllowBasicAuthForWebScriptsHome">
<value>${authentication.alwaysAllowBasicAuthForWebScriptsHome.enabled}</value>
</property>
<property name="getRemoteUserTimeoutMilliseconds">
<value>${authentication.getRemoteUserTimeoutMilliseconds}</value>
</property>

View File

@@ -25,6 +25,20 @@
*/
package org.alfresco.rest.api.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.rest.AbstractSingleNetworkSiteTest;
import org.alfresco.rest.api.Queries;
@@ -36,27 +50,12 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.cmr.site.SiteVisibility;
import org.alfresco.util.testing.category.LuceneTests;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* V1 REST API tests for pre-defined 'live' search Queries on Sites
* V1 REST API tests for pre-defined 'live' search Queries on Sites
*
* <ul>
* <li> {@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/queries/sites} </li>
* <li>{@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/queries/sites}</li>
* </ul>
*
* @author janv
@@ -64,7 +63,7 @@ import static org.mockito.Mockito.verify;
public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest
{
private static final String URL_QUERIES_LSS = "queries/sites";
private SiteService siteService;
@Before
@@ -72,7 +71,7 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest
public void setup() throws Exception
{
super.setup();
siteService = (SiteService)applicationContext.getBean("SiteService");
siteService = (SiteService) applicationContext.getBean("SiteService");
}
// Note expectedIds defaults to ids
@@ -86,7 +85,7 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest
}
dummySearchServiceQueryNodeRefs.clear();
for (String id: ids)
for (String id : ids)
{
NodeRef nodeRef = getNodeRef(id);
dummySearchServiceQueryNodeRefs.add(nodeRef);
@@ -98,7 +97,7 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest
if (expectedStatus == 200)
{
String termWithEscapedAsterisks = term.replaceAll("\\*", "\\\\*");
String expectedQuery = "TYPE:\"{http://www.alfresco.org/model/site/1.0}site\" AND (\"*"+ termWithEscapedAsterisks +"*\")";
String expectedQuery = "TYPE:\"{http://www.alfresco.org/model/site/1.0}site\" AND (\"*" + termWithEscapedAsterisks + "*\")";
ArgumentCaptor<SearchParameters> searchParametersCaptor = ArgumentCaptor.forClass(SearchParameters.class);
verify(mockSearchService, times(++callCountToMockSearchService)).query(searchParametersCaptor.capture());
SearchParameters parameters = searchParametersCaptor.getValue();
@@ -109,30 +108,32 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest
if (orderBy != null)
{
for (int i=0; i<expectedIds.length; i++)
for (int i = 0; i < expectedIds.length; i++)
{
String id = expectedIds[i];
String actualId = sites.get(i).getId();
assertEquals("Order "+i+":", id, actualId);
assertEquals("Order " + i + ":", id, actualId);
}
}
}
}
/**
/**
* Tests basic api for nodes live search sites - metadata (id, title, description)
*
* <p>GET:</p>
* <p>
* GET:
* </p>
* {@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/queries/sites}
*/
@Test
public void testLiveSearchSites() throws Exception
{
setRequestContext(user1);
AuthenticationUtil.setFullyAuthenticatedUser(user1);
int sCount = 5;
assertTrue(sCount > 4); // as relied on by test below
List<String> siteIds = new ArrayList<>(sCount);
try
@@ -149,14 +150,14 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest
String siteD = "siteD";
int charValue = siteI.charAt(0);
// create some some sites with site id: ab00001, abc00002, abcd00003, abcde00004, abcdef00005 (and some specific titles and descriptions)
for (int i = 1; i <= sCount; i++)
{
String num = String.format("%05d", i);
charValue = charValue+1;
siteI = siteI + String.valueOf((char)charValue);
charValue = charValue + 1;
siteI = siteI + String.valueOf((char) charValue);
String siteId = siteI + num + RUNID;
String siteTitle = siteT + num + siteT;
@@ -220,7 +221,7 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest
{
// some cleanup
setRequestContext(user1);
for (String siteId : siteIds)
{
deleteSite(siteId, true, 204);
@@ -230,14 +231,20 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest
private NodeRef getNodeRef(String createdSiteId)
{
AuthenticationUtil.setFullyAuthenticatedUser(user1);
// Created sites do not return NodeRefs to the caller so we need to get the NodeRef from the siteService.
// Temporarily as admin we will get NodeRefs to handle ACL authorization.
String userUnderTest = AuthenticationUtil.getFullyAuthenticatedUser();
AuthenticationUtil.setFullyAuthenticatedUser(DEFAULT_ADMIN);
// The following call to siteService.getSite(createdSiteId).getNodeRef() returns a NodeRef like:
// workspace://SpacesStore/9db76769-96de-4de4-bdb4-a127130af362
// workspace://SpacesStore/9db76769-96de-4de4-bdb4-a127130af362
// We call tenantService.getName(nodeRef) to get a fully qualified NodeRef as Solr returns this.
// They look like:
// workspace://@org.alfresco.rest.api.tests.queriespeopleapitest@SpacesStore/9db76769-96de-4de4-bdb4-a127130af362
// workspace://@org.alfresco.rest.api.tests.queriespeopleapitest@SpacesStore/9db76769-96de-4de4-bdb4-a127130af362
NodeRef nodeRef = siteService.getSite(createdSiteId).getNodeRef();
nodeRef = tenantService.getName(nodeRef);
AuthenticationUtil.setFullyAuthenticatedUser(userUnderTest);
return nodeRef;
}
@@ -245,7 +252,7 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest
public void testLiveSearchSites_SortPage() throws Exception
{
setRequestContext(user1);
AuthenticationUtil.setFullyAuthenticatedUser(user1);
List<String> siteIds = new ArrayList<>(5);
try
@@ -253,14 +260,14 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest
// As user 1 ...
Paging paging = getPaging(0, 100);
// create site
String s1 = createSite("siABCDEF"+RUNID, "ABCDEF DEF", "sdABCDEF", SiteVisibility.PRIVATE, 201).getId();
String s2 = createSite("siABCD"+RUNID, "ABCD DEF", "sdABCD", SiteVisibility.PRIVATE, 201).getId();
String s3 = createSite("siABCDE"+RUNID, "ABCDE DEF", "sdABCDE", SiteVisibility.PRIVATE, 201).getId();
String s4 = createSite("siAB"+RUNID, "AB DEF", "sdAB", SiteVisibility.PRIVATE, 201).getId();
String s5 = createSite("siABC"+RUNID, "ABC DEF", "sdABC", SiteVisibility.PRIVATE, 201).getId();
String s1 = createSite("siABCDEF" + RUNID, "ABCDEF DEF", "sdABCDEF", SiteVisibility.PRIVATE, 201).getId();
String s2 = createSite("siABCD" + RUNID, "ABCD DEF", "sdABCD", SiteVisibility.PRIVATE, 201).getId();
String s3 = createSite("siABCDE" + RUNID, "ABCDE DEF", "sdABCDE", SiteVisibility.PRIVATE, 201).getId();
String s4 = createSite("siAB" + RUNID, "AB DEF", "sdAB", SiteVisibility.PRIVATE, 201).getId();
String s5 = createSite("siABC" + RUNID, "ABC DEF", "sdABC", SiteVisibility.PRIVATE, 201).getId();
// test sort order
{
// default sort order - title asc
@@ -276,11 +283,11 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest
// basic paging tests
{
// sort order - title desc
checkApiCall("siAB", "title desc", getPaging(0, 2), 200, new String[] {s1, s3}, s1, s3, s2, s5, s4);
checkApiCall("siAB", "title desc", getPaging(2, 2), 200, new String[] {s2, s5}, s1, s3, s2, s5, s4);
checkApiCall("siAB", "title desc", getPaging(4, 2), 200, new String[] {s4}, s1, s3, s2, s5, s4);
checkApiCall("siAB", "title desc", getPaging(0, 2), 200, new String[]{s1, s3}, s1, s3, s2, s5, s4);
checkApiCall("siAB", "title desc", getPaging(2, 2), 200, new String[]{s2, s5}, s1, s3, s2, s5, s4);
checkApiCall("siAB", "title desc", getPaging(4, 2), 200, new String[]{s4}, s1, s3, s2, s5, s4);
}
// -ve tests
{
// -ve test - invalid sort field
@@ -304,7 +311,52 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest
}
}
}
/**
* If the search service do not support ACL filtering, then the Queries API should handle the response to exclude private sites and potential unauthorized error when building response.
*/
@Test
public void testLiveSearchExcludesPrivateSites() throws Exception
{
String publicSiteId = null;
String privateSiteId = null;
try
{
// given
setRequestContext(null, DEFAULT_ADMIN, DEFAULT_ADMIN_PWD);
createUser("bartender");
publicSiteId = createSite("samePrefixPublicSite", "samePrefixPublicSite", "Visible to all users", SiteVisibility.PUBLIC, 201).getId();
privateSiteId = createSite("samePrefixPrivateSite", "samePrefixPrivateSite", "Hidden from bartender", SiteVisibility.PRIVATE, 201).getId();
String[] searchResults = {publicSiteId, privateSiteId};
String[] expectedSites = {publicSiteId};
// when
setRequestContext(null, "bartender", "password");
AuthenticationUtil.setFullyAuthenticatedUser("bartender");
// then
checkApiCall("samePrefix", null, getPaging(0, 100), 200, expectedSites, searchResults);
}
finally
{
// cleanup
AuthenticationUtil.setFullyAuthenticatedUser(DEFAULT_ADMIN);
setRequestContext(null, DEFAULT_ADMIN, DEFAULT_ADMIN_PWD);
if (publicSiteId != null)
{
deleteSite(publicSiteId, true, 204);
}
if (privateSiteId != null)
{
deleteSite(privateSiteId, true, 204);
}
deleteUser("bartender", null);
}
}
@Override
public String getScope()
{

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* 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
@@ -212,7 +212,7 @@ public class ProcessesImplTest extends TestCase implements RecognizedParamsExtra
{
// the tests are always run on PostgreSQL only
// Dialect dialect = (Dialect) applicationContext.getBean("dialect");
// if (dialect instanceof AlfrescoSQLServerDialect)
// if (dialect instanceof SQLServerDialect)
// {
// REPO-1104: we do not run this test on MS SQL server because it will fail
// until the Activiti defect related to REPO-1104 will be fixed

View File

@@ -27,7 +27,7 @@ to integrate with a number of external Authentication providers including
* https://github.com/Alfresco/alfresco-data-model/tree/master/src/main/java/org/alfresco/repo/security/authentication
* License: LGPL
* Issue Tracker Link: https://issues.alfresco.com/jira/issues/?jql=project%3DREPO
* Documentation Link: http://docs.alfresco.com/5.2/concepts/auth-intro.html
* Documentation Link: https://support.hyland.com/r/Alfresco/Alfresco-Content-Services-Community-Edition/23.4/Alfresco-Content-Services-Community-Edition/Administer/Manage-Security/Authentication-and-sync
* Contribution Model: Alfresco Open Source
***

View File

@@ -16,7 +16,7 @@
* Source Code Link:m https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/
* License: LGPL
* Issue Tracker Link: https://issues.alfresco.com/jira/secure/RapidBoard.jspa?projectKey=REPO&useStoredSettings=true&rapidView=379
* Documentation Link: http://docs.alfresco.com/5.1/concepts/versioning.html
* Documentation Link: https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Configure/Repository/About-Versioning
* Contribution Model: Alfresco publishes the source code and will review proposed patch requests
***

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.1</version>
<version>23.6.0.4</version>
</parent>
<dependencies>

View File

@@ -2,93 +2,96 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* 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
* 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.action.executer;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
/**
* Add features action executor implementation.
*
* @author Roy Wetherall
*/
public class AddFeaturesActionExecuter extends ActionExecuterAbstractBase
{
/**
* Action constants
*/
public static final String NAME = "add-features";
public static final String PARAM_ASPECT_NAME = "aspect-name";
public static final String PARAM_CONSTRAINT = "ac-aspects";
/**
* The node service
*/
private NodeService nodeService;
/** Transaction Service, used for retrying operations */
private TransactionService transactionService;
/**
* Set the node service
*
* @param nodeService the node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Set the transaction service
*
* @param transactionService the transaction service
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* Adhoc properties are allowed for this executor
*/
@Override
protected boolean getAdhocPropertiesAllowed()
{
return true;
package org.alfresco.repo.action.executer;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.action.access.ActionAccessRestriction;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
/**
* Add features action executor implementation.
*
* @author Roy Wetherall
*/
public class AddFeaturesActionExecuter extends ActionExecuterAbstractBase
{
/**
* Action constants
*/
public static final String NAME = "add-features";
public static final String PARAM_ASPECT_NAME = "aspect-name";
public static final String PARAM_CONSTRAINT = "ac-aspects";
/**
* The node service
*/
private NodeService nodeService;
/** Transaction Service, used for retrying operations */
private TransactionService transactionService;
/**
* Set the node service
*
* @param nodeService
* the node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Set the transaction service
*
* @param transactionService
* the transaction service
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* Adhoc properties are allowed for this executor
*/
@Override
protected boolean getAdhocPropertiesAllowed()
{
return true;
}
/**
@@ -96,55 +99,61 @@ public class AddFeaturesActionExecuter extends ActionExecuterAbstractBase
*/
public void executeImpl(final Action ruleAction, final NodeRef actionedUponNodeRef)
{
if (this.nodeService.exists(actionedUponNodeRef))
{
transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
QName aspectQName = null;
if(! nodeService.exists(actionedUponNodeRef))
{
// Node has gone away, skip
return null;
}
// Build the aspect details
Map<String, Serializable> paramValues = ruleAction.getParameterValues();
for (Map.Entry<String, Serializable> entry : paramValues.entrySet())
{
if (entry.getKey().equals(PARAM_ASPECT_NAME) == true)
{
aspectQName = (QName)entry.getValue();
}
else
{
// Must be an adhoc property
QName propertyQName = QName.createQName(entry.getKey());
Serializable propertyValue = entry.getValue();
properties.put(propertyQName, propertyValue);
}
}
// Add the aspect
nodeService.addAspect(actionedUponNodeRef, aspectQName, properties);
return null;
}
}
);
}
}
/**
* @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List)
*/
@Override
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
{
paramList.add(new ParameterDefinitionImpl(PARAM_ASPECT_NAME, DataTypeDefinition.QNAME, true, getParamDisplayLabel(PARAM_ASPECT_NAME), false, "ac-aspects"));
}
}
if (this.nodeService.exists(actionedUponNodeRef))
{
transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionCallback<Void>() {
public Void execute() throws Throwable
{
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
QName aspectQName = null;
if (!nodeService.exists(actionedUponNodeRef))
{
// Node has gone away, skip
return null;
}
// Build the aspect details
Map<String, Serializable> paramValues = ruleAction.getParameterValues();
removeActionContextParameter(paramValues);
for (Map.Entry<String, Serializable> entry : paramValues.entrySet())
{
if (entry.getKey().equals(PARAM_ASPECT_NAME) == true)
{
aspectQName = (QName) entry.getValue();
}
else
{
// Must be an adhoc property
QName propertyQName = QName.createQName(entry.getKey());
Serializable propertyValue = entry.getValue();
properties.put(propertyQName, propertyValue);
}
}
// Add the aspect
nodeService.addAspect(actionedUponNodeRef, aspectQName, properties);
return null;
}
});
}
}
/**
* @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List)
*/
@Override
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
{
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);
}
}

View File

@@ -1,286 +1,282 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2024 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.audit;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.audit.model.AuditApplication;
import org.alfresco.repo.audit.model.AuditModelRegistry;
import org.alfresco.repo.audit.model._3.AuditPath;
import org.alfresco.service.cmr.audit.AuditQueryParameters;
import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback;
/**
* The audit component. Used by the AuditService and AuditMethodInterceptor to insert audit entries.
*
* @author Derek Hulley
*/
public interface AuditComponent
{
/**
* Determines whether audit is globally enabled or disabled.
*
* @return Returns <code>true</code> if audit is enabled
*
* @since 3.3
*/
public boolean isAuditEnabled();
/**
* Switch auditing on or off
*
* @param enable <tt>true</tt> to enable auditing or <tt>false</tt> to disable
*
* @since 3.4
*/
public void setAuditEnabled(boolean enable);
/**
* @param userAuditFilter UserAuditFilter
*
* @since 4.2
*/
public void setUserAuditFilter(UserAuditFilter userAuditFilter);
/**
* Get all registered audit applications, whether active or not.
*
* @return Returns a map of registered audit applications keyed by name
*
* @since 3.4
*/
public Map<String, AuditApplication> getAuditApplications();
/**
* Determine whether the audit infrastructure expects audit values to be passed in.
* This is a helper method to allow optimizations in the client code. Reasons why
* this method might return <tt>false</tt> are: auditing is disabled; no audit applications
* have been registered. Sometimes, depending on the log level, this method may always
* return <tt>true</tt>.
*
*
* @return Returns <code>true</code> if the calling code (data producers)
* should go ahead and generate the data for
* {@link #recordAuditValues(String, Map) recording}.
*
* @since 3.3
*/
public boolean areAuditValuesRequired();
/**
* Determines if audit values are required based on whether there are any audit applications
* registered to record data for the given path. This helper method gives data producers a
* shortcut in the event that nothing would be recorded in any event.
*
* @param path the audit path
* @return Returns <tt>true</tt> if there is at least one audit application
* registered to handle the given path.
*
* @since 3.4
*/
public boolean areAuditValuesRequired(String path);
/**
* Delete audit entries for the given application and time range
*
* @param applicationName the name of the application
* @param fromTime the start time of entries to remove (inclusive and optional)
* @param toTime the end time of entries to remove (exclusive and optional)
* @return Returns the number of entries deleted
*
* @since 3.2
*/
int deleteAuditEntries(String applicationName, Long fromTime, Long toTime);
/**
* Delete audit entries for the given application and id range
*
* @param applicationName the name of the application
* @param fromId the start time of entries to remove (inclusive and optional)
* @param toId the end time of entries to remove (exclusive and optional)
* @return Returns the number of entries deleted
*
* @since 5.2.2
*/
int deleteAuditEntriesByIdRange(String applicationName, Long fromId, Long toId);
/**
* Delete a discrete list of audit entries based on ID
*
* @param auditEntryIds the audit entry IDs to delete
* @return Returns the number of entries deleted
*/
int deleteAuditEntries(List<Long> auditEntryIds);
/**
* Check if an audit path is enabled. The path will be disabled if it or any higher
* path has been explicitly disabled. Any disabled path will not be processed when
* data is audited.
*
* @param applicationName the name of the application being logged to
* @param path the audit path to check or <tt>null</tt> to assume the
* application's root path
* @return Returns <tt>true</tt> if the audit path has been disabled
*
* @since 3.2
*/
boolean isAuditPathEnabled(String applicationName, String path);
/**
* Enable auditing (if it is not already enabled) for all paths that contain the given path.
* The path is the path as originally logged and
* not the path that the generated data may contain - although this would be similarly
* enabled.
* <p>
* If the enabled
*
* @param applicationName the name of the application being logged to
* @param path the audit path to check or <tt>null</tt> to assume the
* application's root path
*
* @since 3.2
*/
void enableAudit(String applicationName, String path);
/**
* Disable auditing (if it is not already disabled) for all paths that contain the given path.
* The path is the path as originally logged and
* not the path that the generated data may contain - although this would be similarly
* disabled.
* <p>
* If the path is <b>/x/y</b> then any data paths that start with <b>/x/y</b> will be stripped
* out <u>before</u> data generators and data recorders are applied. If the path represents
* the root path of the application, then auditing for that application is effectively disabled.
*
* @param applicationName the name of the application being logged to
* @param path the audit path to check or <tt>null</tt> to assume the
* application's root path
*
* @since 3.2
*/
void disableAudit(String applicationName, String path);
/**
* Remove all disabled paths i.e. enable all per-path based auditing. Auditing may still be
* disabled globally. This is primarily for test purposes; applications should know which
* paths need {@link #enableAudit(String, String) enabling} or
* {@link #disableAudit(String, String) disabled}.
*
* @param applicationName the name of the application
*
* @since 3.2
*/
void resetDisabledPaths(String applicationName);
/**
* Create an audit entry for the given map of values. The map key is a path - starting with '/'
* ({@link AuditApplication#AUDIT_PATH_SEPARATOR}) - relative to the root path provided.
* <p/>
* The root path and value keys are combined to produce a map of data keyed by full path. This
* fully-pathed map is then passed through the
* {@link AuditModelRegistry#getAuditPathMapper() audit path mapper}. The result may yield data
* destined for several different
* {@link AuditModelRegistry#getAuditApplicationByKey(String) audit applications}. depending on
* the data extraction and generation defined in the applications, values (or derived values) may
* be recorded against several audit entries (one per application represented).
* <p/>
* The return values reflect what was actually persisted and is controlled by the data extractors
* defined in the audit configuration.
* <p/>
* A new read-write transaction is started if there are values to write that there is not a viable
* transaction present.
*
* @param rootPath a base path of {@link AuditPath} key entries concatenated with the path separator
* '/' ({@link AuditApplication#AUDIT_PATH_SEPARATOR})
* @param values the values to audit mapped by {@link AuditPath} key relative to root path
* (may be <tt>null</tt>)
* @return Returns the values that were actually persisted, keyed by their full path.
* @throws IllegalStateException if the transaction state could not be determined
*
* @since 3.2
*/
Map<String, Serializable> recordAuditValues(String rootPath, Map<String, Serializable> values);
/**
* The same as {@link AuditComponent#recordAuditValues(String, Map)}, but with controlled usage of userFilter
*
* @param rootPath a base path of {@link AuditPath} key entries concatenated with the path separator
* '/' ({@link AuditApplication#AUDIT_PATH_SEPARATOR})
* @param values the values to audit mapped by {@link AuditPath} key relative to root path
* (may be <tt>null</tt>)
* @param useUserFilter if <tt>false</tt> the user filter is disabled.
* @return Returns the values that were actually persisted, keyed by their full path.
* @throws IllegalStateException if the transaction state could not be determined
*/
Map<String, Serializable> recordAuditValuesWithUserFilter(String rootPath, Map<String, Serializable> values, boolean useUserFilter);
/**
* Find audit entries using the given parameters
*
* @param callback the data callback per entry
* @param parameters the parameters for the query (may not be <tt>null</tt>)
* @param maxResults the maximum number of results to retrieve (must be greater than 0)
*
* @throws IllegalArgumentException if maxResults less or equal to zero
*
* @since 3.2
*/
void auditQuery(AuditQueryCallback callback, AuditQueryParameters parameters, int maxResults);
/**
* Issue an audit query to retrieve min / max audit record id for a given application.
*
* @param applicationName the name of the application
* @param extremes a list containing min/max or both
* @return a map containing min/max and the associated value
*/
HashMap<String, Long> getAuditMinMaxByApp(String applicationName, List<String> extremes);
/**
* Issue an audit query to retrieve count of records for a given application.
*
* @param applicationName the name of the application
* @return a map containing min/max and the associated value
*/
default int getAuditEntriesCountByApp(String applicationName)
{
return -1;
}
/**
* Issue an audit query to retrieve count of records for a given application and properties
*
* @param parameters audit parameters provided by the <code>where</code> clause on the ReST API
* @return a map containing min/max and the associated value
*/
default int getAuditEntriesCountByAppAndProperties(AuditQueryParameters parameters)
{
return -1;
}
}
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 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.audit;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.audit.model.AuditApplication;
import org.alfresco.repo.audit.model.AuditModelRegistry;
import org.alfresco.repo.audit.model._3.AuditPath;
import org.alfresco.service.cmr.audit.AuditQueryParameters;
import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback;
/**
* The audit component. Used by the AuditService and AuditMethodInterceptor to insert audit entries.
*
* @author Derek Hulley
*/
public interface AuditComponent
{
/**
* Determines whether audit is globally enabled or disabled.
*
* @return Returns <code>true</code> if audit is enabled
*
* @since 3.3
*/
public boolean isAuditEnabled();
/**
* Switch auditing on or off
*
* @param enable
* <tt>true</tt> to enable auditing or <tt>false</tt> to disable
*
* @since 3.4
*/
public void setAuditEnabled(boolean enable);
/**
* @param userAuditFilter
* UserAuditFilter
*
* @since 4.2
*/
public void setUserAuditFilter(UserAuditFilter userAuditFilter);
/**
* Get all registered audit applications, whether active or not.
*
* @return Returns a map of registered audit applications keyed by name
*
* @since 3.4
*/
public Map<String, AuditApplication> getAuditApplications();
/**
* Determine whether the audit infrastructure expects audit values to be passed in. This is a helper method to allow optimizations in the client code. Reasons why this method might return <tt>false</tt> are: auditing is disabled; no audit applications have been registered. Sometimes, depending on the log level, this method may always return <tt>true</tt>.
*
*
* @return Returns <code>true</code> if the calling code (data producers) should go ahead and generate the data for {@link #recordAuditValues(String, Map) recording}.
*
* @since 3.3
*/
public boolean areAuditValuesRequired();
/**
* Determines if audit values are required based on whether there are any audit applications registered to record data for the given path. This helper method gives data producers a shortcut in the event that nothing would be recorded in any event.
*
* @param path
* the audit path
* @return Returns <tt>true</tt> if there is at least one audit application registered to handle the given path.
*
* @since 3.4
*/
public boolean areAuditValuesRequired(String path);
/**
* Delete audit entries for the given application and time range
*
* @param applicationName
* the name of the application
* @param fromTime
* the start time of entries to remove (inclusive and optional)
* @param toTime
* the end time of entries to remove (exclusive and optional)
* @return Returns the number of entries deleted
*
* @since 3.2
*/
int deleteAuditEntries(String applicationName, Long fromTime, Long toTime);
/**
* Delete audit entries for the given application and id range
*
* @param applicationName
* the name of the application
* @param fromId
* the start time of entries to remove (inclusive and optional)
* @param toId
* the end time of entries to remove (exclusive and optional)
* @return Returns the number of entries deleted
*
* @since 5.2.2
*/
int deleteAuditEntriesByIdRange(String applicationName, Long fromId, Long toId);
/**
* Delete a discrete list of audit entries based on ID
*
* @param auditEntryIds
* the audit entry IDs to delete
* @return Returns the number of entries deleted
*/
int deleteAuditEntries(List<Long> auditEntryIds);
/**
* Check if an audit path is enabled. The path will be disabled if it or any higher path has been explicitly disabled. Any disabled path will not be processed when data is audited.
*
* @param applicationName
* the name of the application being logged to
* @param path
* the audit path to check or <tt>null</tt> to assume the application's root path
* @return Returns <tt>true</tt> if the audit path has been disabled
*
* @since 3.2
*/
boolean isAuditPathEnabled(String applicationName, String path);
/**
* Enable auditing (if it is not already enabled) for all paths that contain the given path. The path is the path as originally logged and not the path that the generated data may contain - although this would be similarly enabled.
* <p>
* If the enabled
*
* @param applicationName
* the name of the application being logged to
* @param path
* the audit path to check or <tt>null</tt> to assume the application's root path
*
* @since 3.2
*/
void enableAudit(String applicationName, String path);
/**
* Disable auditing (if it is not already disabled) for all paths that contain the given path. The path is the path as originally logged and not the path that the generated data may contain - although this would be similarly disabled.
* <p>
* If the path is <b>/x/y</b> then any data paths that start with <b>/x/y</b> will be stripped out <u>before</u> data generators and data recorders are applied. If the path represents the root path of the application, then auditing for that application is effectively disabled.
*
* @param applicationName
* the name of the application being logged to
* @param path
* the audit path to check or <tt>null</tt> to assume the application's root path
*
* @since 3.2
*/
void disableAudit(String applicationName, String path);
/**
* Remove all disabled paths i.e. enable all per-path based auditing. Auditing may still be disabled globally. This is primarily for test purposes; applications should know which paths need {@link #enableAudit(String, String) enabling} or {@link #disableAudit(String, String) disabled}.
*
* @param applicationName
* the name of the application
*
* @since 3.2
*/
void resetDisabledPaths(String applicationName);
/**
* Create an audit entry for the given map of values. The map key is a path - starting with '/' ({@link AuditApplication#AUDIT_PATH_SEPARATOR}) - relative to the root path provided.
* <p/>
* The root path and value keys are combined to produce a map of data keyed by full path. This fully-pathed map is then passed through the {@link AuditModelRegistry#getAuditPathMapper() audit path mapper}. The result may yield data destined for several different {@link AuditModelRegistry#getAuditApplicationByKey(String) audit applications}. depending on the data extraction and generation defined in the applications, values (or derived values) may be recorded against several audit entries (one per application represented).
* <p/>
* The return values reflect what was actually persisted and is controlled by the data extractors defined in the audit configuration.
* <p/>
* A new read-write transaction is started if there are values to write that there is not a viable transaction present.
*
* @param rootPath
* a base path of {@link AuditPath} key entries concatenated with the path separator '/' ({@link AuditApplication#AUDIT_PATH_SEPARATOR})
* @param values
* the values to audit mapped by {@link AuditPath} key relative to root path (may be <tt>null</tt>)
* @return Returns the values that were actually persisted, keyed by their full path.
* @throws IllegalStateException
* if the transaction state could not be determined
*
* @since 3.2
*/
Map<String, Serializable> recordAuditValues(String rootPath, Map<String, Serializable> values);
/**
* The same as {@link AuditComponent#recordAuditValues(String, Map)}, but with controlled usage of userFilter
*
* @param rootPath
* a base path of {@link AuditPath} key entries concatenated with the path separator '/' ({@link AuditApplication#AUDIT_PATH_SEPARATOR})
* @param values
* the values to audit mapped by {@link AuditPath} key relative to root path (may be <tt>null</tt>)
* @param useUserFilter
* if <tt>false</tt> the user filter is disabled.
* @return Returns the values that were actually persisted, keyed by their full path.
* @throws IllegalStateException
* if the transaction state could not be determined
*/
Map<String, Serializable> recordAuditValuesWithUserFilter(String rootPath, Map<String, Serializable> values, boolean useUserFilter);
/**
* Find audit entries using the given parameters
*
* @param callback
* the data callback per entry
* @param parameters
* the parameters for the query (may not be <tt>null</tt>)
* @param maxResults
* the maximum number of results to retrieve (must be greater than 0)
*
* @throws IllegalArgumentException
* if maxResults less or equal to zero
*
* @since 3.2
*/
void auditQuery(AuditQueryCallback callback, AuditQueryParameters parameters, int maxResults);
/**
* Issue an audit query to retrieve min / max audit record id for a given application.
*
* @param applicationName
* the name of the application
* @param extremes
* a list containing min/max or both
* @return a map containing min/max and the associated value
*/
HashMap<String, Long> getAuditMinMaxByApp(String applicationName, List<String> extremes);
/**
* Issue an audit query to retrieve count of records for a given application.
*
* @param applicationName
* the name of the application
* @return a map containing min/max and the associated value
*/
default int getAuditEntriesCountByApp(String applicationName)
{
return -1;
}
/**
* Issue an audit query to retrieve count of records for a given application and properties
*
* @param applicationName
* the name of the application
* @param parameters
* audit parameters provided by the <code>where</code> clause on the ReST API
* @return a map containing min/max and the associated value
*/
default int getAuditEntriesCountByAppAndProperties(String applicationName, AuditQueryParameters parameters)
{
return -1;
}
}

View File

@@ -1,197 +1,209 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2024 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.audit;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.alfresco.service.cmr.audit.AuditQueryParameters;
import org.alfresco.service.cmr.audit.AuditService;
/**
* The implementation of the AuditService for application auditing.
*
* @author Derek Hulley
* @since 3.2
*/
public class AuditServiceImpl implements AuditService
{
private AuditComponent auditComponent;
public AuditServiceImpl()
{
super();
}
public void setAuditComponent(AuditComponent auditComponent)
{
this.auditComponent = auditComponent;
}
/**
* {@inheritDoc}
* @since 3.4
*/
public boolean isAuditEnabled()
{
return auditComponent.isAuditEnabled();
}
/**
* {@inheritDoc}
* @since 3.4
*/
@Override
public void setAuditEnabled(boolean enable)
{
auditComponent.setAuditEnabled(enable);
}
/**
* {@inheritDoc}
* @since 3.4
*/
@Override
public Map<String, AuditApplication> getAuditApplications()
{
Map<String, org.alfresco.repo.audit.model.AuditApplication> apps = auditComponent.getAuditApplications();
Map<String, AuditApplication> ret = new TreeMap<String, AuditApplication>();
for (String app : apps.keySet())
{
String name = app;
String key = org.alfresco.repo.audit.model.AuditApplication.AUDIT_PATH_SEPARATOR + apps.get(app).getApplicationKey();
boolean enabled = auditComponent.isAuditPathEnabled(app, key);
AuditApplication auditApplication = new AuditApplication(name, key, enabled);
ret.put(name, auditApplication);
}
return ret;
}
/**
* {@inheritDoc}
* @since 3.2
*/
public boolean isAuditEnabled(String applicationName, String path)
{
return auditComponent.isAuditPathEnabled(applicationName, path);
}
/**
* {@inheritDoc}
* @since 3.2
*/
public void enableAudit(String applicationName, String path)
{
auditComponent.enableAudit(applicationName, path);
}
/**
* {@inheritDoc}
* @since 3.2
*/
public void disableAudit(String applicationName, String path)
{
auditComponent.disableAudit(applicationName, path);
}
/**
* {@inheritDoc}
* @since 3.2
*/
public int clearAudit(String applicationName)
{
Long now = Long.valueOf(System.currentTimeMillis());
return auditComponent.deleteAuditEntries(applicationName, null, now);
}
/**
* {@inheritDoc}
* @since 3.4
*/
@Override
public int clearAudit(String applicationName, Long fromTime, Long toTime)
{
toTime = (toTime == null) ? Long.valueOf(System.currentTimeMillis()) : toTime;
return auditComponent.deleteAuditEntries(applicationName, fromTime, toTime);
}
/**
* {@inheritDoc}
* @since 5.2.2
*/
@Override
public int clearAuditByIdRange(String applicationName, Long fromId, Long toId)
{
return auditComponent.deleteAuditEntriesByIdRange(applicationName, fromId, toId);
}
/**
* {@inheritDoc}
* @since 3.4
*/
@Override
public int clearAudit(List<Long> auditEntryIds)
{
return auditComponent.deleteAuditEntries(auditEntryIds);
}
/**
* {@inheritDoc}
* @since 3.3
*/
public void auditQuery(AuditQueryCallback callback, AuditQueryParameters parameters, int maxResults)
{
auditComponent.auditQuery(callback, parameters, maxResults);
}
/**
* {@inheritDoc}
*/
public HashMap<String, Long> getAuditMinMaxByApp(String applicationName, List<String> extremes)
{
return auditComponent.getAuditMinMaxByApp(applicationName, extremes);
}
/**
* {@inheritDoc}
*/
@Override
public int getAuditEntriesCountByApp(String applicationName)
{
return auditComponent.getAuditEntriesCountByApp(applicationName);
}
/**
* {@inheritDoc}
*/
@Override public int getAuditEntriesCountByAppAndProperties(AuditQueryParameters parameters)
{
return auditComponent.getAuditEntriesCountByAppAndProperties(parameters);
}
}
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 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.audit;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.alfresco.service.cmr.audit.AuditQueryParameters;
import org.alfresco.service.cmr.audit.AuditService;
/**
* The implementation of the AuditService for application auditing.
*
* @author Derek Hulley
* @since 3.2
*/
public class AuditServiceImpl implements AuditService
{
private AuditComponent auditComponent;
public AuditServiceImpl()
{
super();
}
public void setAuditComponent(AuditComponent auditComponent)
{
this.auditComponent = auditComponent;
}
/**
* {@inheritDoc}
*
* @since 3.4
*/
public boolean isAuditEnabled()
{
return auditComponent.isAuditEnabled();
}
/**
* {@inheritDoc}
*
* @since 3.4
*/
@Override
public void setAuditEnabled(boolean enable)
{
auditComponent.setAuditEnabled(enable);
}
/**
* {@inheritDoc}
*
* @since 3.4
*/
@Override
public Map<String, AuditApplication> getAuditApplications()
{
Map<String, org.alfresco.repo.audit.model.AuditApplication> apps = auditComponent.getAuditApplications();
Map<String, AuditApplication> ret = new TreeMap<String, AuditApplication>();
for (String app : apps.keySet())
{
String name = app;
String key = org.alfresco.repo.audit.model.AuditApplication.AUDIT_PATH_SEPARATOR + apps.get(app).getApplicationKey();
boolean enabled = auditComponent.isAuditPathEnabled(app, key);
AuditApplication auditApplication = new AuditApplication(name, key, enabled);
ret.put(name, auditApplication);
}
return ret;
}
/**
* {@inheritDoc}
*
* @since 3.2
*/
public boolean isAuditEnabled(String applicationName, String path)
{
return auditComponent.isAuditPathEnabled(applicationName, path);
}
/**
* {@inheritDoc}
*
* @since 3.2
*/
public void enableAudit(String applicationName, String path)
{
auditComponent.enableAudit(applicationName, path);
}
/**
* {@inheritDoc}
*
* @since 3.2
*/
public void disableAudit(String applicationName, String path)
{
auditComponent.disableAudit(applicationName, path);
}
/**
* {@inheritDoc}
*
* @since 3.2
*/
public int clearAudit(String applicationName)
{
Long now = Long.valueOf(System.currentTimeMillis());
return auditComponent.deleteAuditEntries(applicationName, null, now);
}
/**
* {@inheritDoc}
*
* @since 3.4
*/
@Override
public int clearAudit(String applicationName, Long fromTime, Long toTime)
{
toTime = (toTime == null) ? Long.valueOf(System.currentTimeMillis()) : toTime;
return auditComponent.deleteAuditEntries(applicationName, fromTime, toTime);
}
/**
* {@inheritDoc}
*
* @since 5.2.2
*/
@Override
public int clearAuditByIdRange(String applicationName, Long fromId, Long toId)
{
return auditComponent.deleteAuditEntriesByIdRange(applicationName, fromId, toId);
}
/**
* {@inheritDoc}
*
* @since 3.4
*/
@Override
public int clearAudit(List<Long> auditEntryIds)
{
return auditComponent.deleteAuditEntries(auditEntryIds);
}
/**
* {@inheritDoc}
*
* @since 3.3
*/
public void auditQuery(AuditQueryCallback callback, AuditQueryParameters parameters, int maxResults)
{
auditComponent.auditQuery(callback, parameters, maxResults);
}
/**
* {@inheritDoc}
*/
public HashMap<String, Long> getAuditMinMaxByApp(String applicationName, List<String> extremes)
{
return auditComponent.getAuditMinMaxByApp(applicationName, extremes);
}
/**
* {@inheritDoc}
*/
@Override
public int getAuditEntriesCountByApp(String applicationName)
{
return auditComponent.getAuditEntriesCountByApp(applicationName);
}
/**
* {@inheritDoc}
*/
@Override
public int getAuditEntriesCountByAppAndProperties(String applicationName, AuditQueryParameters parameters)
{
return auditComponent.getAuditEntriesCountByAppAndProperties(applicationName, parameters);
}
}

View File

@@ -1,123 +1,116 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2024 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.domain;
import java.io.UnsupportedEncodingException;
import java.util.zip.CRC32;
import org.alfresco.util.Pair;
/**
* Helper class to calculate CRC values for string persistence.
*
* @author Derek Hulley
* @since 3.2
*/
public class CrcHelper
{
public static final String EMPTY_STRING = ".empty";
/**
* Calculate a persistable, unique pair of values that can be persisted in a database unique
* key and guarantee correct case-sensitivity.
* <p>
* While the short-string version of the value is always lowercase, the CRC is
* calculated from the virgin string if case-sensitivity is enforced; in the case-insensitive
* case, the CRC is calculated from a lowercase version of the string.
* <p>
* If the value is an empty string, then {@link #EMPTY_STRING} is used instead. This ensures
* that persisted values don't fall foul of the Oracle empty string comparison "behaviour" i.e
* you should never persist an empty string in Oracle as it equates to a SQL <b>NULL</b>.
*
* @param value the raw value that will be persisted
* @param dataLength the maximum number of characters that can be persisted
* @param useCharsFromStart <tt>true</tt> if the shortened string value must be made from
* the first characters of the string or <tt>false</tt> to use
* characters from the end of the string.
* @param caseSensitive <tt>true</tt> if the resulting pair must be case-sensitive or
* <tt>false</tt> if the pair must be case-insensitive.
* @return Return the persistable pair. The result will never be <tt>null</tt>,
* but the individual pair values will be <tt>null</tt> if the
* value given is <tt>null</tt>
*/
public static Pair<String, Long> getStringCrcPair(
String value,
int dataLength,
boolean useCharsFromStart,
boolean caseSensitive)
{
String valueLowerCase;
if (value == null)
{
return new Pair<String, Long>(null, null);
}
else if (value.length() == 0)
{
value = CrcHelper.EMPTY_STRING;
valueLowerCase = value;
}
else
{
valueLowerCase = value.toLowerCase();
}
Long valueCrc;
try
{
CRC32 crc = new CRC32();
if (caseSensitive)
{
crc.update(value.getBytes("UTF-8"));
}
else
{
crc.update(valueLowerCase.getBytes("UTF-8"));
}
valueCrc = crc.getValue();
}
catch (UnsupportedEncodingException e)
{
throw new RuntimeException("UTF-8 encoding is not supported");
}
// Crc Value will change based on the case-sensitive, So we need to get the short value based on case-sensitive
String valueShort = null;
String currentValue = caseSensitive ? value : valueLowerCase;
int valueLen = currentValue.length();
if (valueLen < dataLength)
{
valueShort = currentValue;
}
else if (useCharsFromStart)
{
valueShort = currentValue.substring(0, dataLength - 1);
}
else
{
valueShort = currentValue.substring(valueLen - dataLength);
}
return new Pair<String, Long>(valueShort, valueCrc);
}
}
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 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.domain;
import java.io.UnsupportedEncodingException;
import java.util.zip.CRC32;
import org.alfresco.util.Pair;
/**
* Helper class to calculate CRC values for string persistence.
*
* @author Derek Hulley
* @since 3.2
*/
public class CrcHelper
{
public static final String EMPTY_STRING = ".empty";
/**
* Calculate a persistable, unique pair of values that can be persisted in a database unique key and guarantee correct case-sensitivity.
* <p>
* While the short-string version of the value is always lowercase, the CRC is calculated from the virgin string if case-sensitivity is enforced; in the case-insensitive case, the CRC is calculated from a lowercase version of the string.
* <p>
* If the value is an empty string, then {@link #EMPTY_STRING} is used instead. This ensures that persisted values don't fall foul of the Oracle empty string comparison "behaviour" i.e you should never persist an empty string in Oracle as it equates to a SQL <b>NULL</b>.
*
* @param value
* the raw value that will be persisted
* @param dataLength
* the maximum number of characters that can be persisted
* @param useCharsFromStart
* <tt>true</tt> if the shortened string value must be made from the first characters of the string or <tt>false</tt> to use characters from the end of the string.
* @param caseSensitive
* <tt>true</tt> if the resulting pair must be case-sensitive or <tt>false</tt> if the pair must be case-insensitive.
* @return Return the persistable pair. The result will never be <tt>null</tt>, but the individual pair values will be <tt>null</tt> if the value given is <tt>null</tt>
*/
public static Pair<String, Long> getStringCrcPair(
String value,
int dataLength,
boolean useCharsFromStart,
boolean caseSensitive)
{
String valueLowerCase;
if (value == null)
{
return new Pair<String, Long>(null, null);
}
else if (value.length() == 0)
{
value = CrcHelper.EMPTY_STRING;
valueLowerCase = value;
}
else
{
valueLowerCase = value.toLowerCase();
}
Long valueCrc;
try
{
CRC32 crc = new CRC32();
if (caseSensitive)
{
crc.update(value.getBytes("UTF-8"));
}
else
{
crc.update(valueLowerCase.getBytes("UTF-8"));
}
valueCrc = crc.getValue();
}
catch (UnsupportedEncodingException e)
{
throw new RuntimeException("UTF-8 encoding is not supported");
}
// Get the short value (case-sensitive or not)
String valueShort = null;
int valueLen = valueLowerCase.length();
if (valueLen < dataLength)
{
valueShort = valueLowerCase;
}
else if (useCharsFromStart)
{
valueShort = valueLowerCase.substring(0, dataLength - 1);
}
else
{
valueShort = valueLowerCase.substring(valueLen - dataLength);
}
return new Pair<String, Long>(valueShort, valueCrc);
}
}

View File

@@ -1,258 +1,285 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2024 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.domain.audit;
import java.io.Serializable;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.util.Pair;
/**
* DAO services for <b>alf_audit_XXX</b> tables.
* <p>
* The older methods are supported by a different implementation and will eventually
* be deprecated and phased out.
*
* @author Derek Hulley
* @since 3.2
*/
public interface AuditDAO
{
/*
* V3.2 methods after here only, please
*/
/**
* Information about the audit application to be passed in an out of the interface.
*
* @author Derek Hulley
* @since 3.2
*/
public static class AuditApplicationInfo
{
private Long id;
private String name;
private Long modelId;
private Long disabledPathsId;
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("AuditApplicationInfo ")
.append("[ id=").append(id)
.append(", name=").append(name)
.append(", modelId=").append(modelId)
.append(", disabledPathsId=").append(disabledPathsId)
.append("]");
return sb.toString();
}
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setname(String name)
{
this.name = name;
}
public Long getModelId()
{
return modelId;
}
public void setModelId(Long modelId)
{
this.modelId = modelId;
}
public Long getDisabledPathsId()
{
return disabledPathsId;
}
public void setDisabledPathsId(Long disabledPathsId)
{
this.disabledPathsId = disabledPathsId;
}
}
/**
* Creates a new audit model entry or finds an existing one
*
* @param url the URL of the configuration
* @return Returns the ID of the config matching the input stream and the
* content storage details
* @since 3.2
*/
Pair<Long, ContentData> getOrCreateAuditModel(URL url);
/**
* Get the audit application details.
*
* @param applicationName the name of the application
* @return Returns details of an existing application or <tt>null</tt> if it doesn't exist
*
* @since 3.2
*/
AuditApplicationInfo getAuditApplication(String applicationName);
/**
* Creates a new audit application. The application name must be unique.
*
* @param application the name of the application
* @param modelId the ID of the model configuration
*
* @since 3.2
*/
AuditApplicationInfo createAuditApplication(String application, Long modelId);
/**
* Update the audit application to refer to a new model.
* If the model did not change, then nothing will be done.
*
* @param id the ID of the audit application
* @param modelId the ID of the new model
*
* @since 3.2
*/
void updateAuditApplicationModel(Long id, Long modelId);
/**
* Update the audit application to hold a new set of disabled paths.
* If the value did not change, then nothing will be done.
*
* @param id the ID of the audit application
* @param disabledPaths the new disabled paths
*
* @since 3.2
*/
void updateAuditApplicationDisabledPaths(Long id, Set<String> disabledPaths);
/**
* Delete audit entries for the application, possibly limiting the time range.
*
* @param applicationId an existing audit application ID
* @param fromTime the minimum entry time (inclusive, optional)
* @param toTime the maximum entry time (exclusive, optional)
* @return Returns the number of entries deleted
*
* @since 3.2
*/
int deleteAuditEntries(Long applicationId, Long fromTime, Long toTime);
/**
* Delete audit entries for the application for given id range.
*
* @param applicationId an existing audit application ID
* @param fromId the minimum fromId (inclusive, optional)
* @param toId the maximum toId (exclusive, optional)
* @return Returns the number of entries deleted
*
* @since 5.2.2
*/
int deleteAuditEntriesByIdRange(Long applicationId, Long fromId, Long toId);
/**
* Delete a discrete list of audit entries. Duplicate entries are collapsed
* and the number of entries deleted will match the count of unique IDs in
* the list; otherwise a concurrency condition has occured and an exception
* will be generated.
*
* @param auditEntryIds the IDs of all audit entries to delete
* @return Returns the number of entries deleted
*/
int deleteAuditEntries(List<Long> auditEntryIds);
/**
* Create a new audit entry with the given map of values.
*
* @param applicationId an existing audit application ID
* @param time the time (ms since epoch) to log the entry against
* @param username the authenticated user (<tt>null</tt> if not present)
* @param values the values to record
* @return Returns the unique entry ID
*
* @since 3.2
*/
Long createAuditEntry(Long applicationId, long time, String username, Map<String, Serializable> values);
/**
* Find audit entries using the given parameters, any of which may be null
*
* @param callback the data callback per entry
* @param parameters the parameters for the query (may not be <tt>null</tt>)
* @param maxResults the maximum number of results to retrieve (must be greater than 0)
*
* @throws IllegalArgumentException if maxResults less or equal to zero
*/
void findAuditEntries(
AuditQueryCallback callback,
org.alfresco.service.cmr.audit.AuditQueryParameters parameters,
int maxResults);
/**
* Issue an audit query to retrieve min / max audit record id for a given application.
*
* @param appId the database id of the application
* @param extremes a list containing min/max or both
* @return a map containing min/max and the associated value
*/
HashMap<String, Long> getAuditMinMaxByApp(long appId, List<String> extremes);
/**
* Issue an audit query to retrieve count of records for a given application.
*
* @param applicationId the database id of the application
* @return a map containing min/max and the associated value
*/
default int getAuditEntriesCountByApp(long applicationId)
{
return -1;
}
/**
* Issue an audit query to retrieve count of records for a given application and properties
*
* @param parameters audit parameters provided by the <code>where</code> clause on the ReST API
* @return a map containing min/max and the associated value
*/
default int getAuditEntriesCountByAppAndProperties(org.alfresco.service.cmr.audit.AuditQueryParameters parameters)
{
return -1;
}
}
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 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.domain.audit;
import java.io.Serializable;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.util.Pair;
/**
* DAO services for <b>alf_audit_XXX</b> tables.
* <p>
* The older methods are supported by a different implementation and will eventually be deprecated and phased out.
*
* @author Derek Hulley
* @since 3.2
*/
public interface AuditDAO
{
/* V3.2 methods after here only, please */
/**
* Information about the audit application to be passed in an out of the interface.
*
* @author Derek Hulley
* @since 3.2
*/
public static class AuditApplicationInfo
{
private Long id;
private String name;
private Long modelId;
private Long disabledPathsId;
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("AuditApplicationInfo ")
.append("[ id=").append(id)
.append(", name=").append(name)
.append(", modelId=").append(modelId)
.append(", disabledPathsId=").append(disabledPathsId)
.append("]");
return sb.toString();
}
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setname(String name)
{
this.name = name;
}
public Long getModelId()
{
return modelId;
}
public void setModelId(Long modelId)
{
this.modelId = modelId;
}
public Long getDisabledPathsId()
{
return disabledPathsId;
}
public void setDisabledPathsId(Long disabledPathsId)
{
this.disabledPathsId = disabledPathsId;
}
}
/**
* Creates a new audit model entry or finds an existing one
*
* @param url
* the URL of the configuration
* @return Returns the ID of the config matching the input stream and the content storage details
* @since 3.2
*/
Pair<Long, ContentData> getOrCreateAuditModel(URL url);
/**
* Get the audit application details.
*
* @param applicationName
* the name of the application
* @return Returns details of an existing application or <tt>null</tt> if it doesn't exist
*
* @since 3.2
*/
AuditApplicationInfo getAuditApplication(String applicationName);
/**
* Creates a new audit application. The application name must be unique.
*
* @param application
* the name of the application
* @param modelId
* the ID of the model configuration
*
* @since 3.2
*/
AuditApplicationInfo createAuditApplication(String application, Long modelId);
/**
* Update the audit application to refer to a new model. If the model did not change, then nothing will be done.
*
* @param id
* the ID of the audit application
* @param modelId
* the ID of the new model
*
* @since 3.2
*/
void updateAuditApplicationModel(Long id, Long modelId);
/**
* Update the audit application to hold a new set of disabled paths. If the value did not change, then nothing will be done.
*
* @param id
* the ID of the audit application
* @param disabledPaths
* the new disabled paths
*
* @since 3.2
*/
void updateAuditApplicationDisabledPaths(Long id, Set<String> disabledPaths);
/**
* Delete audit entries for the application, possibly limiting the time range.
*
* @param applicationId
* an existing audit application ID
* @param fromTime
* the minimum entry time (inclusive, optional)
* @param toTime
* the maximum entry time (exclusive, optional)
* @return Returns the number of entries deleted
*
* @since 3.2
*/
int deleteAuditEntries(Long applicationId, Long fromTime, Long toTime);
/**
* Delete audit entries for the application for given id range.
*
* @param applicationId
* an existing audit application ID
* @param fromId
* the minimum fromId (inclusive, optional)
* @param toId
* the maximum toId (exclusive, optional)
* @return Returns the number of entries deleted
*
* @since 5.2.2
*/
int deleteAuditEntriesByIdRange(Long applicationId, Long fromId, Long toId);
/**
* Delete a discrete list of audit entries. Duplicate entries are collapsed and the number of entries deleted will match the count of unique IDs in the list; otherwise a concurrency condition has occured and an exception will be generated.
*
* @param auditEntryIds
* the IDs of all audit entries to delete
* @return Returns the number of entries deleted
*/
int deleteAuditEntries(List<Long> auditEntryIds);
/**
* Create a new audit entry with the given map of values.
*
* @param applicationId
* an existing audit application ID
* @param time
* the time (ms since epoch) to log the entry against
* @param username
* the authenticated user (<tt>null</tt> if not present)
* @param values
* the values to record
* @return Returns the unique entry ID
*
* @since 3.2
*/
Long createAuditEntry(Long applicationId, long time, String username, Map<String, Serializable> values);
/**
* Find audit entries using the given parameters, any of which may be null
*
* @param callback
* the data callback per entry
* @param parameters
* the parameters for the query (may not be <tt>null</tt>)
* @param maxResults
* the maximum number of results to retrieve (must be greater than 0)
*
* @throws IllegalArgumentException
* if maxResults less or equal to zero
*/
void findAuditEntries(
AuditQueryCallback callback,
org.alfresco.service.cmr.audit.AuditQueryParameters parameters,
int maxResults);
/**
* Issue an audit query to retrieve min / max audit record id for a given application.
*
* @param appId
* the database id of the application
* @param extremes
* a list containing min/max or both
* @return a map containing min/max and the associated value
*/
HashMap<String, Long> getAuditMinMaxByApp(long appId, List<String> extremes);
/**
* Issue an audit query to retrieve count of records for a given application.
*
* @param applicationId
* the database id of the application
* @return a map containing min/max and the associated value
*/
default int getAuditEntriesCountByApp(long applicationId)
{
return -1;
}
/**
* Issue an audit query to retrieve count of records for a given application and properties
*
* @param applicationName
* name of the application to be queried
* @param parameters
* audit parameters provided by the <code>where</code> clause on the ReST API
* @return a map containing min/max and the associated value
*/
default int getAuditEntriesCountByAppAndProperties(String applicationName, org.alfresco.service.cmr.audit.AuditQueryParameters parameters)
{
return -1;
}
}

View File

@@ -1,316 +1,315 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2024 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.domain.audit.ibatis;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.alfresco.repo.domain.audit.AbstractAuditDAOImpl;
import org.alfresco.repo.domain.audit.AuditApplicationEntity;
import org.alfresco.repo.domain.audit.AuditDeleteParameters;
import org.alfresco.repo.domain.audit.AuditEntryEntity;
import org.alfresco.repo.domain.audit.AuditModelEntity;
import org.alfresco.repo.domain.audit.AuditQueryParameters;
import org.alfresco.repo.domain.audit.AuditQueryResult;
import org.alfresco.repo.domain.propval.PropertyValueDAO.PropertyFinderCallback;
import org.alfresco.util.Pair;
import org.apache.ibatis.session.RowBounds;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.dao.ConcurrencyFailureException;
/**
* iBatis-specific implementation of the DAO for <b>alf_audit_XXX</b> tables.
*
* @author Derek Hulley
* @since 3.2
*/
public class AuditDAOImpl extends AbstractAuditDAOImpl
{
private static final String SELECT_MODEL_BY_CRC = "alfresco.audit.select_AuditModelByCrc";
private static final String INSERT_MODEL = "alfresco.audit.insert.insert_AuditModel";
private static final String SELECT_APPLICATION_BY_ID = "alfresco.audit.select_AuditApplicationById";
private static final String SELECT_APPLICATION_BY_NAME_ID = "alfresco.audit.select_AuditApplicationByNameId";
private static final String INSERT_APPLICATION = "alfresco.audit.insert.insert_AuditApplication";
private static final String UPDATE_APPLICATION = "alfresco.audit.update_AuditApplication";
private static final String DELETE_ENTRIES = "alfresco.audit.delete_AuditEntries";
private static final String DELETE_ENTRIES_BY_ID = "alfresco.audit.delete_AuditEntriesById";
private static final String INSERT_ENTRY = "alfresco.audit.insert.insert_AuditEntry";
private static final String SELECT_MINMAX_ENTRY_FOR_APP = "alfresco.audit.select_MinMaxAuditEntryId";
private static final String SELECT_COUNT_ENTRIES_FOR_APP = "alfresco.audit.select_CountAuditEntryId";
private static final String SELECT_COUNT_ENTRIES_FOR_APP_WITH_PROPERTIES = "select_CountAuditEntryIdWithWhereClause";
@SuppressWarnings("unused")
private static final String SELECT_ENTRIES_SIMPLE = "alfresco.audit.select_AuditEntriesSimple";
private static final String SELECT_ENTRIES_WITH_VALUES = "alfresco.audit.select_AuditEntriesWithValues";
private static final String SELECT_ENTRIES_WITHOUT_VALUES = "alfresco.audit.select_AuditEntriesWithoutValues";
private SqlSessionTemplate template;
public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate)
{
this.template = sqlSessionTemplate;
}
@Override
protected AuditModelEntity getAuditModelByCrc(long crc)
{
AuditModelEntity entity = new AuditModelEntity();
entity.setContentCrc(crc);
entity = template.selectOne(
SELECT_MODEL_BY_CRC,
entity);
// Done
return entity;
}
@Override
protected AuditModelEntity createAuditModel(Long contentDataId, long crc)
{
AuditModelEntity entity = new AuditModelEntity();
entity.setContentDataId(contentDataId);
entity.setContentCrc(crc);
template.insert(INSERT_MODEL, entity);
return entity;
}
@Override
protected AuditApplicationEntity getAuditApplicationById(Long id)
{
Map<String, Object> params = new HashMap<String, Object>(11);
params.put("id", id);
AuditApplicationEntity entity = template.selectOne(
SELECT_APPLICATION_BY_ID,
params);
// Done
if (logger.isDebugEnabled())
{
logger.debug("Searched for audit application ID " + id + " and found: " + entity);
}
return entity;
}
@Override
protected AuditApplicationEntity getAuditApplicationByName(String appName)
{
// Resolve the name as a property ID
Pair<Long, Serializable> appNamePair = propertyValueDAO.getPropertyValue(appName);
if (appNamePair == null)
{
// There will be no results
return null;
}
Map<String, Object> params = new HashMap<String, Object>(11);
params.put("id", appNamePair.getFirst());
AuditApplicationEntity entity = template.selectOne(
SELECT_APPLICATION_BY_NAME_ID,
params);
// Done
if (logger.isDebugEnabled())
{
logger.debug("Searched for audit application '" + appName + "' and found: " + entity);
}
return entity;
}
@Override
protected AuditApplicationEntity createAuditApplication(Long appNameId, Long modelId, Long disabledPathsId)
{
AuditApplicationEntity entity = new AuditApplicationEntity();
entity.setVersion((short)0);
entity.setApplicationNameId(appNameId);
entity.setAuditModelId(modelId);
entity.setDisabledPathsId(disabledPathsId);
template.insert(INSERT_APPLICATION, entity);
return entity;
}
@Override
protected AuditApplicationEntity updateAuditApplication(AuditApplicationEntity entity)
{
AuditApplicationEntity updateEntity = new AuditApplicationEntity();
updateEntity.setId(entity.getId());
updateEntity.setVersion(entity.getVersion());
updateEntity.incrementVersion();
updateEntity.setApplicationNameId(entity.getApplicationNameId());
updateEntity.setAuditModelId(entity.getAuditModelId());
updateEntity.setDisabledPathsId(entity.getDisabledPathsId());
int updated = template.update(UPDATE_APPLICATION, updateEntity);
if (updated != 1)
{
// unexpected number of rows affected
throw new ConcurrencyFailureException("Incorrect number of rows affected for updateAuditApplication: " + updateEntity + ": expected 1, actual " + updated);
}
// Done
return updateEntity;
}
public int deleteAuditEntries(Long applicationId, Long from, Long to)
{
AuditDeleteParameters params = new AuditDeleteParameters();
params.setAuditApplicationId(applicationId);
params.setAuditFromTime(from);
params.setAuditToTime(to);
return template.delete(DELETE_ENTRIES, params);
}
public int deleteAuditEntriesByIdRange(Long applicationId, Long fromId, Long toId)
{
AuditDeleteParameters params = new AuditDeleteParameters();
params.setAuditApplicationId(applicationId);
params.setAuditFromId(fromId);
params.setAuditToId(toId);
return template.delete(DELETE_ENTRIES, params);
}
@Override
protected int deleteAuditEntriesImpl(List<Long> auditEntryIds)
{
AuditDeleteParameters params = new AuditDeleteParameters();
params.setAuditEntryIds(auditEntryIds);
return template.delete(DELETE_ENTRIES_BY_ID, params);
}
@Override
protected AuditEntryEntity createAuditEntry(Long applicationId, long time, Long usernameId, Long valuesId)
{
AuditEntryEntity entity = new AuditEntryEntity();
entity.setAuditApplicationId(applicationId);
entity.setAuditTime(time);
entity.setAuditUserId(usernameId);
entity.setAuditValuesId(valuesId);
template.insert(INSERT_ENTRY, entity);
return entity;
}
public HashMap<String, Long> getAuditMinMaxByApp(long appId, List<String> extremes)
{
// Build parameters to be used in the query. Filter the duplicates when inserting into map
Map<String, Object> params = extremes.stream().collect(Collectors.toMap(s -> s, s -> Boolean.TRUE, (s1, s2) -> s1));
params.put("auditAppId", appId);
HashMap<String, Long> result = template.selectOne(SELECT_MINMAX_ENTRY_FOR_APP, params);
return result;
}
@Override
public int getAuditEntriesCountByApp(long applicationId)
{
Map<String, Object> params = new HashMap<>();
params.put("auditAppId", applicationId);
int result = template.selectOne(SELECT_COUNT_ENTRIES_FOR_APP, params);
return result;
}
@Override
public int getAuditEntriesCountByAppAndProperties(org.alfresco.service.cmr.audit.AuditQueryParameters parameters)
{
AuditQueryParameters dbParameters = convertFromRestAuditQueryParameters(parameters);
int result = template.selectOne(SELECT_COUNT_ENTRIES_FOR_APP_WITH_PROPERTIES, dbParameters);
return result;
}
@SuppressWarnings("unchecked")
@Override
protected void findAuditEntries(
final AuditQueryRowHandler rowHandler,
int maxResults,
org.alfresco.service.cmr.audit.AuditQueryParameters restParameters)
{
AuditQueryParameters params = convertFromRestAuditQueryParameters(restParameters);
if (params==null)
{
return;
}
if (maxResults > 0)
{
// Query without getting the values. We gather all the results and batch-fetch the audited
// values afterwards.
final Map<Long, AuditQueryResult> resultsByValueId = new HashMap<Long, AuditQueryResult>(173);
PropertyFinderCallback propertyFinderCallback = new PropertyFinderCallback()
{
public void handleProperty(Long id, Serializable value)
{
// get the row
AuditQueryResult row = resultsByValueId.get(id);
try
{
row.setAuditValue((Map<String, Serializable>) value);
}
catch (ClassCastException e)
{
// The handler will deal with the entry
}
}
};
List<AuditQueryResult> rows = template.selectList(SELECT_ENTRIES_WITHOUT_VALUES, params, new RowBounds(0, maxResults));
for (AuditQueryResult row : rows)
{
resultsByValueId.put(row.getAuditValuesId(), row);
if (resultsByValueId.size() >= 100)
{
// Fetch values for the results. The treemap is ordered.
List<Long> valueIds = new ArrayList<Long>(resultsByValueId.keySet());
propertyValueDAO.getPropertiesByIds(valueIds, propertyFinderCallback);
// Clear and continue
resultsByValueId.clear();
}
}
// Process any remaining results
if (resultsByValueId.size() > 0)
{
// Fetch values for the results. The treemap is ordered.
List<Long> valueIds = new ArrayList<Long>(resultsByValueId.keySet());
propertyValueDAO.getPropertiesByIds(valueIds, propertyFinderCallback);
}
// Now pass the filled-out results to the row handler (order-preserved)
for (AuditQueryResult row : rows)
{
rowHandler.processResult(row);
}
}
else
{
throw new IllegalArgumentException("maxResults must be greater than 0");
}
}
}
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 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.domain.audit.ibatis;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.ibatis.session.RowBounds;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.dao.ConcurrencyFailureException;
import org.alfresco.repo.domain.audit.AbstractAuditDAOImpl;
import org.alfresco.repo.domain.audit.AuditApplicationEntity;
import org.alfresco.repo.domain.audit.AuditDeleteParameters;
import org.alfresco.repo.domain.audit.AuditEntryEntity;
import org.alfresco.repo.domain.audit.AuditModelEntity;
import org.alfresco.repo.domain.audit.AuditQueryParameters;
import org.alfresco.repo.domain.audit.AuditQueryResult;
import org.alfresco.repo.domain.propval.PropertyValueDAO.PropertyFinderCallback;
import org.alfresco.util.Pair;
/**
* iBatis-specific implementation of the DAO for <b>alf_audit_XXX</b> tables.
*
* @author Derek Hulley
* @since 3.2
*/
public class AuditDAOImpl extends AbstractAuditDAOImpl
{
private static final String SELECT_MODEL_BY_CRC = "alfresco.audit.select_AuditModelByCrc";
private static final String INSERT_MODEL = "alfresco.audit.insert.insert_AuditModel";
private static final String SELECT_APPLICATION_BY_ID = "alfresco.audit.select_AuditApplicationById";
private static final String SELECT_APPLICATION_BY_NAME_ID = "alfresco.audit.select_AuditApplicationByNameId";
private static final String INSERT_APPLICATION = "alfresco.audit.insert.insert_AuditApplication";
private static final String UPDATE_APPLICATION = "alfresco.audit.update_AuditApplication";
private static final String DELETE_ENTRIES = "alfresco.audit.delete_AuditEntries";
private static final String DELETE_ENTRIES_BY_ID = "alfresco.audit.delete_AuditEntriesById";
private static final String INSERT_ENTRY = "alfresco.audit.insert.insert_AuditEntry";
private static final String SELECT_MINMAX_ENTRY_FOR_APP = "alfresco.audit.select_MinMaxAuditEntryId";
private static final String SELECT_COUNT_ENTRIES_FOR_APP = "alfresco.audit.select_CountAuditEntryId";
private static final String SELECT_COUNT_ENTRIES_FOR_APP_WITH_PROPERTIES = "select_CountAuditEntryIdWithWhereClause";
@SuppressWarnings("unused")
private static final String SELECT_ENTRIES_SIMPLE = "alfresco.audit.select_AuditEntriesSimple";
private static final String SELECT_ENTRIES_WITH_VALUES = "alfresco.audit.select_AuditEntriesWithValues";
private static final String SELECT_ENTRIES_WITHOUT_VALUES = "alfresco.audit.select_AuditEntriesWithoutValues";
private SqlSessionTemplate template;
public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate)
{
this.template = sqlSessionTemplate;
}
@Override
protected AuditModelEntity getAuditModelByCrc(long crc)
{
AuditModelEntity entity = new AuditModelEntity();
entity.setContentCrc(crc);
entity = template.selectOne(
SELECT_MODEL_BY_CRC,
entity);
// Done
return entity;
}
@Override
protected AuditModelEntity createAuditModel(Long contentDataId, long crc)
{
AuditModelEntity entity = new AuditModelEntity();
entity.setContentDataId(contentDataId);
entity.setContentCrc(crc);
template.insert(INSERT_MODEL, entity);
return entity;
}
@Override
protected AuditApplicationEntity getAuditApplicationById(Long id)
{
Map<String, Object> params = new HashMap<String, Object>(11);
params.put("id", id);
AuditApplicationEntity entity = template.selectOne(
SELECT_APPLICATION_BY_ID,
params);
// Done
if (logger.isDebugEnabled())
{
logger.debug("Searched for audit application ID " + id + " and found: " + entity);
}
return entity;
}
@Override
protected AuditApplicationEntity getAuditApplicationByName(String appName)
{
// Resolve the name as a property ID
Pair<Long, Serializable> appNamePair = propertyValueDAO.getPropertyValue(appName);
if (appNamePair == null)
{
// There will be no results
return null;
}
Map<String, Object> params = new HashMap<String, Object>(11);
params.put("id", appNamePair.getFirst());
AuditApplicationEntity entity = template.selectOne(
SELECT_APPLICATION_BY_NAME_ID,
params);
// Done
if (logger.isDebugEnabled())
{
logger.debug("Searched for audit application '" + appName + "' and found: " + entity);
}
return entity;
}
@Override
protected AuditApplicationEntity createAuditApplication(Long appNameId, Long modelId, Long disabledPathsId)
{
AuditApplicationEntity entity = new AuditApplicationEntity();
entity.setVersion((short) 0);
entity.setApplicationNameId(appNameId);
entity.setAuditModelId(modelId);
entity.setDisabledPathsId(disabledPathsId);
template.insert(INSERT_APPLICATION, entity);
return entity;
}
@Override
protected AuditApplicationEntity updateAuditApplication(AuditApplicationEntity entity)
{
AuditApplicationEntity updateEntity = new AuditApplicationEntity();
updateEntity.setId(entity.getId());
updateEntity.setVersion(entity.getVersion());
updateEntity.incrementVersion();
updateEntity.setApplicationNameId(entity.getApplicationNameId());
updateEntity.setAuditModelId(entity.getAuditModelId());
updateEntity.setDisabledPathsId(entity.getDisabledPathsId());
int updated = template.update(UPDATE_APPLICATION, updateEntity);
if (updated != 1)
{
// unexpected number of rows affected
throw new ConcurrencyFailureException("Incorrect number of rows affected for updateAuditApplication: " + updateEntity + ": expected 1, actual " + updated);
}
// Done
return updateEntity;
}
public int deleteAuditEntries(Long applicationId, Long from, Long to)
{
AuditDeleteParameters params = new AuditDeleteParameters();
params.setAuditApplicationId(applicationId);
params.setAuditFromTime(from);
params.setAuditToTime(to);
return template.delete(DELETE_ENTRIES, params);
}
public int deleteAuditEntriesByIdRange(Long applicationId, Long fromId, Long toId)
{
AuditDeleteParameters params = new AuditDeleteParameters();
params.setAuditApplicationId(applicationId);
params.setAuditFromId(fromId);
params.setAuditToId(toId);
return template.delete(DELETE_ENTRIES, params);
}
@Override
protected int deleteAuditEntriesImpl(List<Long> auditEntryIds)
{
AuditDeleteParameters params = new AuditDeleteParameters();
params.setAuditEntryIds(auditEntryIds);
return template.delete(DELETE_ENTRIES_BY_ID, params);
}
@Override
protected AuditEntryEntity createAuditEntry(Long applicationId, long time, Long usernameId, Long valuesId)
{
AuditEntryEntity entity = new AuditEntryEntity();
entity.setAuditApplicationId(applicationId);
entity.setAuditTime(time);
entity.setAuditUserId(usernameId);
entity.setAuditValuesId(valuesId);
template.insert(INSERT_ENTRY, entity);
return entity;
}
public HashMap<String, Long> getAuditMinMaxByApp(long appId, List<String> extremes)
{
// Build parameters to be used in the query. Filter the duplicates when inserting into map
Map<String, Object> params = extremes.stream().collect(Collectors.toMap(s -> s, s -> Boolean.TRUE, (s1, s2) -> s1));
params.put("auditAppId", appId);
HashMap<String, Long> result = template.selectOne(SELECT_MINMAX_ENTRY_FOR_APP, params);
return result;
}
@Override
public int getAuditEntriesCountByApp(long applicationId)
{
Map<String, Object> params = new HashMap<>();
params.put("auditAppId", applicationId);
int result = template.selectOne(SELECT_COUNT_ENTRIES_FOR_APP, params);
return result;
}
@Override
public int getAuditEntriesCountByAppAndProperties(String applicationName, org.alfresco.service.cmr.audit.AuditQueryParameters parameters)
{
AuditQueryParameters dbParameters = convertFromRestAuditQueryParameters(parameters);
int result = template.selectOne(SELECT_COUNT_ENTRIES_FOR_APP_WITH_PROPERTIES, dbParameters);
return result;
}
@SuppressWarnings("unchecked")
@Override
protected void findAuditEntries(
final AuditQueryRowHandler rowHandler,
int maxResults,
org.alfresco.service.cmr.audit.AuditQueryParameters restParameters)
{
AuditQueryParameters params = convertFromRestAuditQueryParameters(restParameters);
if (params == null)
{
return;
}
if (maxResults > 0)
{
// Query without getting the values. We gather all the results and batch-fetch the audited
// values afterwards.
final Map<Long, AuditQueryResult> resultsByValueId = new HashMap<Long, AuditQueryResult>(173);
PropertyFinderCallback propertyFinderCallback = new PropertyFinderCallback() {
public void handleProperty(Long id, Serializable value)
{
// get the row
AuditQueryResult row = resultsByValueId.get(id);
try
{
row.setAuditValue((Map<String, Serializable>) value);
}
catch (ClassCastException e)
{
// The handler will deal with the entry
}
}
};
List<AuditQueryResult> rows = template.selectList(SELECT_ENTRIES_WITHOUT_VALUES, params, new RowBounds(0, maxResults));
for (AuditQueryResult row : rows)
{
resultsByValueId.put(row.getAuditValuesId(), row);
if (resultsByValueId.size() >= 100)
{
// Fetch values for the results. The treemap is ordered.
List<Long> valueIds = new ArrayList<Long>(resultsByValueId.keySet());
propertyValueDAO.getPropertiesByIds(valueIds, propertyFinderCallback);
// Clear and continue
resultsByValueId.clear();
}
}
// Process any remaining results
if (resultsByValueId.size() > 0)
{
// Fetch values for the results. The treemap is ordered.
List<Long> valueIds = new ArrayList<Long>(resultsByValueId.keySet());
propertyValueDAO.getPropertiesByIds(valueIds, propertyFinderCallback);
}
// Now pass the filled-out results to the row handler (order-preserved)
for (AuditQueryResult row : rows)
{
rowHandler.processResult(row);
}
}
else
{
throw new IllegalArgumentException("maxResults must be greater than 0");
}
}
}

View File

@@ -1,173 +1,158 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2024 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.domain.propval;
import org.alfresco.repo.domain.CrcHelper;
import org.alfresco.util.EqualsHelper;
import org.alfresco.util.Pair;
/**
* Entity bean for <b>alf_prop_string_value</b> table.
*
* @author Derek Hulley
* @since 3.2
*/
public class PropertyStringValueEntity
{
public static final String EMPTY_STRING = "";
public static final String EMPTY_STRING_REPLACEMENT = ".empty";
private Long id;
private String stringValue;
private String stringEndLower;
private Long stringCrc;
private String stringLower;
public PropertyStringValueEntity()
{
}
@Override
public int hashCode()
{
return (stringValue == null ? 0 : stringValue.hashCode());
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
else if (obj != null && obj instanceof PropertyStringValueEntity)
{
PropertyStringValueEntity that = (PropertyStringValueEntity) obj;
return EqualsHelper.nullSafeEquals(this.stringValue, that.stringValue);
}
else
{
return false;
}
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder(512);
sb.append("PropertyStringValueEntity")
.append("[ ID=").append(id)
.append(", stringValue=").append(stringValue)
.append("]");
return sb.toString();
}
public Pair<Long, String> getEntityPair()
{
if (stringValue != null && stringValue.equals(PropertyStringValueEntity.EMPTY_STRING_REPLACEMENT))
{
return new Pair<Long, String>(id, PropertyStringValueEntity.EMPTY_STRING);
}
else
{
return new Pair<Long, String>(id, stringValue);
}
}
/**
* Set the string and string-end values
*/
public void setValue(String value)
{
if (value == null)
{
throw new IllegalArgumentException("Null strings cannot be persisted");
}
if (value != null && value.equals(PropertyStringValueEntity.EMPTY_STRING))
{
// Oracle: We can't insert empty strings into the column.
value = PropertyStringValueEntity.EMPTY_STRING_REPLACEMENT;
}
stringValue = value;
// Calculate the crc value from the original value
Pair<String, Long> crcPair = CrcHelper.getStringCrcPair(value, 16, false, true);
stringEndLower = crcPair.getFirst();
stringCrc = crcPair.getSecond();
// Calculate the crc value with case-insensitive
Pair<String, Long> crcPairWithCaseInSensitive = CrcHelper.getStringCrcPair(value, 16, false, false);
stringLower = crcPairWithCaseInSensitive.getFirst();
}
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getStringValue()
{
return stringValue;
}
public void setStringValue(String stringValue)
{
this.stringValue = stringValue;
}
public String getStringEndLower()
{
return stringEndLower;
}
public void setStringEndLower(String stringEndLower)
{
this.stringEndLower = stringEndLower;
}
public Long getStringCrc()
{
return stringCrc;
}
public void setStringCrc(Long stringCrc)
{
this.stringCrc = stringCrc;
}
public String getStringLower()
{
return stringLower;
}
public void setStringLower(String stringLower)
{
this.stringLower = stringLower;
}
}
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 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.domain.propval;
import org.alfresco.repo.domain.CrcHelper;
import org.alfresco.util.EqualsHelper;
import org.alfresco.util.Pair;
/**
* Entity bean for <b>alf_prop_string_value</b> table.
*
* @author Derek Hulley
* @since 3.2
*/
public class PropertyStringValueEntity
{
public static final String EMPTY_STRING = "";
public static final String EMPTY_STRING_REPLACEMENT = ".empty";
private Long id;
private String stringValue;
private String stringEndLower;
private Long stringCrc;
public PropertyStringValueEntity()
{}
@Override
public int hashCode()
{
return (stringValue == null ? 0 : stringValue.hashCode());
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
else if (obj != null && obj instanceof PropertyStringValueEntity)
{
PropertyStringValueEntity that = (PropertyStringValueEntity) obj;
return EqualsHelper.nullSafeEquals(this.stringValue, that.stringValue);
}
else
{
return false;
}
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder(512);
sb.append("PropertyStringValueEntity")
.append("[ ID=").append(id)
.append(", stringValue=").append(stringValue)
.append("]");
return sb.toString();
}
public Pair<Long, String> getEntityPair()
{
if (stringValue != null && stringValue.equals(PropertyStringValueEntity.EMPTY_STRING_REPLACEMENT))
{
return new Pair<Long, String>(id, PropertyStringValueEntity.EMPTY_STRING);
}
else
{
return new Pair<Long, String>(id, stringValue);
}
}
/**
* Set the string and string-end values
*/
public void setValue(String value)
{
if (value == null)
{
throw new IllegalArgumentException("Null strings cannot be persisted");
}
if (value != null && value.equals(PropertyStringValueEntity.EMPTY_STRING))
{
// Oracle: We can't insert empty strings into the column.
value = PropertyStringValueEntity.EMPTY_STRING_REPLACEMENT;
}
stringValue = value;
// Calculate the crc value from the original value
Pair<String, Long> crcPair = CrcHelper.getStringCrcPair(value, 16, false, true);
stringEndLower = crcPair.getFirst();
stringCrc = crcPair.getSecond();
}
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getStringValue()
{
return stringValue;
}
public void setStringValue(String stringValue)
{
this.stringValue = stringValue;
}
public String getStringEndLower()
{
return stringEndLower;
}
public void setStringEndLower(String stringEndLower)
{
this.stringEndLower = stringEndLower;
}
public Long getStringCrc()
{
return stringCrc;
}
public void setStringCrc(Long stringCrc)
{
this.stringCrc = stringCrc;
}
}

View File

@@ -542,10 +542,7 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
@Override
protected void onShutdown(ApplicationEvent applicationEvent)
{
if (eventSender != null)
{
eventSender.destroy();
}
// NOOP
}
protected class EventTransactionListener extends TransactionListenerAdapter

View File

@@ -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()
{

View File

@@ -25,15 +25,16 @@
*/
package org.alfresco.repo.event2;
import java.util.Optional;
import java.util.concurrent.Executor;
import jakarta.annotation.Nonnull;
import org.alfresco.util.PropertyCheck;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.core.env.PropertyResolver;
import java.util.Optional;
import java.util.concurrent.Executor;
import org.alfresco.util.PropertyCheck;
public class EventSenderFactoryBean extends AbstractFactoryBean<EventSender>
{
@@ -51,7 +52,7 @@ public class EventSenderFactoryBean extends AbstractFactoryBean<EventSender>
private boolean legacySkipQueueConfig;
public EventSenderFactoryBean(@Autowired PropertyResolver propertyResolver, Event2MessageProducer event2MessageProducer,
Executor enqueueThreadPoolExecutor, Executor dequeueThreadPoolExecutor)
Executor enqueueThreadPoolExecutor, Executor dequeueThreadPoolExecutor)
{
super();
PropertyCheck.mandatory(this, "propertyResolver", propertyResolver);
@@ -155,4 +156,13 @@ public class EventSenderFactoryBean extends AbstractFactoryBean<EventSender>
{
return event2MessageProducer;
}
@Override
protected void destroyInstance(EventSender eventSender)
{
if (eventSender != null)
{
eventSender.destroy();
}
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* 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
@@ -42,6 +42,9 @@ import java.util.Set;
import java.util.stream.Collectors;
import com.google.common.collect.Sets;
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.repo.event.v1.model.ContentInfo;
@@ -50,6 +53,7 @@ import org.alfresco.repo.event.v1.model.UserInfo;
import org.alfresco.repo.event2.filter.EventFilterRegistry;
import org.alfresco.repo.event2.filter.NodeAspectFilter;
import org.alfresco.repo.event2.filter.NodePropertyFilter;
import org.alfresco.repo.event2.mapper.PropertyMapper;
import org.alfresco.repo.node.MLPropertyInterceptor;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.permissions.AccessDeniedException;
@@ -68,9 +72,6 @@ import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.PathUtil;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
/**
* Helper for {@link NodeResource} objects.
@@ -81,14 +82,15 @@ public class NodeResourceHelper implements InitializingBean
{
private static final Log LOGGER = LogFactory.getLog(NodeResourceHelper.class);
protected NodeService nodeService;
protected DictionaryService dictionaryService;
protected PersonService personService;
protected NodeService nodeService;
protected DictionaryService dictionaryService;
protected PersonService personService;
protected EventFilterRegistry eventFilterRegistry;
protected NamespaceService namespaceService;
protected PermissionService permissionService;
protected NamespaceService namespaceService;
protected PermissionService permissionService;
protected PropertyMapper propertyMapper;
private NodeAspectFilter nodeAspectFilter;
private NodeAspectFilter nodeAspectFilter;
private NodePropertyFilter nodePropertyFilter;
@Override
@@ -100,6 +102,7 @@ public class NodeResourceHelper implements InitializingBean
PropertyCheck.mandatory(this, "eventFilterRegistry", eventFilterRegistry);
PropertyCheck.mandatory(this, "namespaceService", namespaceService);
PropertyCheck.mandatory(this, "permissionService", permissionService);
PropertyCheck.mandatory(this, "propertyMapper", propertyMapper);
this.nodeAspectFilter = eventFilterRegistry.getNodeAspectFilter();
this.nodePropertyFilter = eventFilterRegistry.getNodePropertyFilter();
@@ -124,7 +127,7 @@ public class NodeResourceHelper implements InitializingBean
{
this.permissionService = permissionService;
}
// To make IntelliJ stop complaining about unused method!
@SuppressWarnings("unused")
public void setEventFilterRegistry(EventFilterRegistry eventFilterRegistry)
@@ -137,6 +140,11 @@ public class NodeResourceHelper implements InitializingBean
this.namespaceService = namespaceService;
}
public void setPropertyMapper(PropertyMapper propertyMapper)
{
this.propertyMapper = propertyMapper;
}
public NodeResource.Builder createNodeResourceBuilder(NodeRef nodeRef)
{
final QName type = nodeService.getType(nodeRef);
@@ -148,22 +156,22 @@ public class NodeResourceHelper implements InitializingBean
Map<String, UserInfo> mapUserCache = new HashMap<>(2);
return NodeResource.builder()
.setId(nodeRef.getId())
.setName((String) properties.get(ContentModel.PROP_NAME))
.setNodeType(getQNamePrefixString(type))
.setIsFile(isSubClass(type, ContentModel.TYPE_CONTENT))
.setIsFolder(isSubClass(type, ContentModel.TYPE_FOLDER))
.setCreatedByUser(getUserInfo((String) properties.get(ContentModel.PROP_CREATOR), mapUserCache))
.setCreatedAt(getZonedDateTime((Date)properties.get(ContentModel.PROP_CREATED)))
.setModifiedByUser(getUserInfo((String) properties.get(ContentModel.PROP_MODIFIER), mapUserCache))
.setModifiedAt(getZonedDateTime((Date)properties.get(ContentModel.PROP_MODIFIED)))
.setContent(getContentInfo(properties))
.setPrimaryAssocQName(getPrimaryAssocQName(nodeRef))
.setPrimaryHierarchy(PathUtil.getNodeIdsInReverse(path, false))
.setProperties(mapToNodeProperties(properties))
.setLocalizedProperties(mapToNodeLocalizedProperties(properties))
.setAspectNames(getMappedAspects(nodeRef))
.setSecondaryParents(getSecondaryParents(nodeRef));
.setId(nodeRef.getId())
.setName((String) properties.get(ContentModel.PROP_NAME))
.setNodeType(getQNamePrefixString(type))
.setIsFile(isSubClass(type, ContentModel.TYPE_CONTENT))
.setIsFolder(isSubClass(type, ContentModel.TYPE_FOLDER))
.setCreatedByUser(getUserInfo((String) properties.get(ContentModel.PROP_CREATOR), mapUserCache))
.setCreatedAt(getZonedDateTime((Date) properties.get(ContentModel.PROP_CREATED)))
.setModifiedByUser(getUserInfo((String) properties.get(ContentModel.PROP_MODIFIER), mapUserCache))
.setModifiedAt(getZonedDateTime((Date) properties.get(ContentModel.PROP_MODIFIED)))
.setContent(getContentInfo(properties))
.setPrimaryAssocQName(getPrimaryAssocQName(nodeRef))
.setPrimaryHierarchy(PathUtil.getNodeIdsInReverse(path, false))
.setProperties(mapToNodeProperties(properties))
.setLocalizedProperties(mapToNodeLocalizedProperties(properties))
.setAspectNames(getMappedAspects(nodeRef))
.setSecondaryParents(getSecondaryParents(nodeRef));
}
private boolean isSubClass(QName className, QName ofClassQName)
@@ -171,17 +179,18 @@ public class NodeResourceHelper implements InitializingBean
return dictionaryService.isSubClass(className, ofClassQName);
}
private String getPrimaryAssocQName(NodeRef nodeRef)
private String getPrimaryAssocQName(NodeRef nodeRef)
{
String result = null;
try
try
{
ChildAssociationRef primaryParent = nodeService.getPrimaryParent(nodeRef);
if(primaryParent != null && primaryParent.getQName() != null)
if (primaryParent != null && primaryParent.getQName() != null)
{
result = primaryParent.getQName().getPrefixedQName(namespaceService).getPrefixString();
}
} catch (NamespaceException namespaceException)
}
catch (NamespaceException namespaceException)
{
LOGGER.error("Cannot return a valid primary association QName: " + namespaceException.getMessage());
}
@@ -215,8 +224,8 @@ public class NodeResourceHelper implements InitializingBean
{
v = ((MLText) v).getDefaultValue();
}
filteredProps.put(getQNamePrefixString(k), v);
Serializable mappedValue = propertyMapper.map(k, v);
filteredProps.put(getQNamePrefixString(k), mappedValue);
}
});
@@ -232,7 +241,10 @@ public class NodeResourceHelper implements InitializingBean
{
final MLText mlTextValue = (MLText) v;
final HashMap<String, String> localizedValues = new HashMap<>(mlTextValue.size());
mlTextValue.forEach((locale, text) -> localizedValues.put(locale.toString(), text));
mlTextValue.forEach((locale, text) -> {
Serializable mappedValue = propertyMapper.map(k, text);
localizedValues.put(locale.toString(), (String) mappedValue);
});
filteredProps.put(getQNamePrefixString(k), localizedValues);
}
});
@@ -259,7 +271,7 @@ public class NodeResourceHelper implements InitializingBean
{
String sysUserName = AuthenticationUtil.getSystemUserName();
if (userName.equals(sysUserName) || (AuthenticationUtil.isMtEnabled()
&& userName.startsWith(sysUserName + "@")))
&& userName.startsWith(sysUserName + "@")))
{
userInfo = new UserInfo(userName, userName, "");
}
@@ -306,11 +318,11 @@ public class NodeResourceHelper implements InitializingBean
}
/**
* Returns the QName in the format prefix:local, but in the exceptional case where there is no registered prefix
* returns it in the form {uri}local.
* Returns the QName in the format prefix:local, but in the exceptional case where there is no registered prefix returns it in the form {uri}local.
*
* @param k QName
* @return a String representing the QName in the format prefix:local or {uri}local.
* @param k
* QName
* @return a String representing the QName in the format prefix:local or {uri}local.
*/
public String getQNamePrefixString(QName k)
{
@@ -342,7 +354,7 @@ public class NodeResourceHelper implements InitializingBean
public QName getNodeType(NodeRef nodeRef)
{
return nodeService.getType(nodeRef);
return nodeService.getType(nodeRef);
}
public Serializable getProperty(NodeRef nodeRef, QName qName)
@@ -352,13 +364,14 @@ public class NodeResourceHelper implements InitializingBean
public Map<QName, Serializable> getProperties(NodeRef nodeRef)
{
//We need to have full MLText properties here. This is why we are marking the current thread as MLAware
// We need to have full MLText properties here. This is why we are marking the current thread as MLAware
final boolean toRestore = MLPropertyInterceptor.isMLAware();
MLPropertyInterceptor.setMLAware(true);
try
{
return nodeService.getProperties(nodeRef);
} finally
}
finally
{
MLPropertyInterceptor.setMLAware(toRestore);
}
@@ -377,7 +390,7 @@ public class NodeResourceHelper implements InitializingBean
}
static Map<String, Map<String, String>> getLocalizedPropertiesBefore(Map<String, Map<String, String>> locPropsBefore,
Map<String, Map<String, String>> locPropsAfter)
Map<String, Map<String, String>> locPropsAfter)
{
final Map<String, Map<String, String>> result = new HashMap<>(locPropsBefore.size());
@@ -410,7 +423,7 @@ public class NodeResourceHelper implements InitializingBean
{
return mapToNodeAspects(nodeService.getAspects(nodeRef));
}
public List<String> getPrimaryHierarchy(NodeRef nodeRef, boolean showLeaf)
{
final Path path = nodeService.getPath(nodeRef);
@@ -420,16 +433,17 @@ public class NodeResourceHelper implements InitializingBean
/**
* Gathers node's secondary parents.
*
* @param nodeRef - node reference
* @param nodeRef
* - node reference
* @return a list of node's secondary parents.
*/
public List<String> getSecondaryParents(final NodeRef nodeRef)
{
return nodeService.getParentAssocs(nodeRef).stream()
.filter(not(ChildAssociationRef::isPrimary))
.map(ChildAssociationRef::getParentRef)
.map(NodeRef::getId)
.collect(Collectors.toList());
.filter(not(ChildAssociationRef::isPrimary))
.map(ChildAssociationRef::getParentRef)
.map(NodeRef::getId)
.collect(Collectors.toList());
}
public PermissionService getPermissionService()

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* 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
@@ -26,160 +26,50 @@
package org.alfresco.repo.event2.filter;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.repo.event2.shared.CSVStringToListParser;
import org.alfresco.repo.event2.shared.QNameMatcher;
import org.alfresco.repo.event2.shared.TypeDefExpander;
import org.alfresco.service.namespace.QName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract {@link EventFilter} implementation, containing common event filtering
* functionality for the {@link QName} type.
* Abstract {@link EventFilter} implementation, containing common event filtering functionality for the {@link QName} type.
*
* @author Jamal Kaabi-Mofrad
*/
public abstract class AbstractNodeEventFilter implements EventFilter<QName>
{
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractNodeEventFilter.class);
protected TypeDefExpander typeDefExpander;
private static final String MARKER_INCLUDE_SUBTYPES = "include_subtypes";
private static final String WILDCARD = "*";
protected DictionaryService dictionaryService;
protected NamespaceService namespaceService;
private Set<QName> excludedTypes;
private Set<String> excludedNamespaceURI;
public AbstractNodeEventFilter()
{
this.excludedTypes = new HashSet<>();
this.excludedNamespaceURI = new HashSet<>();
}
private QNameMatcher qNameMatcher;
public final void init()
{
preprocessExcludedTypes(getExcludedTypes());
qNameMatcher = new QNameMatcher(getExcludedTypes());
}
public void setDictionaryService(DictionaryService dictionaryService)
public void setTypeDefExpander(TypeDefExpander typeDefExpander)
{
this.dictionaryService = dictionaryService;
}
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
this.typeDefExpander = typeDefExpander;
}
@Override
public boolean isExcluded(QName qName)
{
if (qName != null)
{
return excludedTypes.contains(qName) || excludedNamespaceURI.contains(qName.getNamespaceURI());
}
return false;
return qNameMatcher.isMatching(qName);
}
protected abstract Set<QName> getExcludedTypes();
protected List<String> parseFilterList(String unparsedFilterList)
{
List<String> list = new LinkedList<>();
StringTokenizer st = new StringTokenizer(unparsedFilterList, ",");
while (st.hasMoreTokens())
{
String entry = st.nextToken().trim();
if (!entry.isEmpty())
{
if (!entry.equals("none") && !entry.contains("${"))
{
list.add(entry);
}
}
}
return list;
}
/**
* Processes the user-defined list of types into valid QNames. It
* validates them against the dictionary and also supports wildcards
*/
private void preprocessExcludedTypes(Set<QName> excluded)
{
excluded.forEach(qName -> {
if (WILDCARD.equals(qName.getLocalName()))
{
//excludedPrefixes.add(getPrefix(qName));
excludedNamespaceURI.add(qName.getNamespaceURI());
}
else
{
excludedTypes.add(qName);
}
});
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Excluded namespace URIs:" + excludedNamespaceURI);
LOGGER.debug("Excluded types:" + excludedTypes);
}
}
private QName getQName(String type)
{
return QName.createQName(type, namespaceService);
return CSVStringToListParser.parse(unparsedFilterList);
}
protected Collection<QName> expandTypeDef(String typeDef)
{
if ((typeDef == null) || typeDef.isEmpty() || typeDef.equals("none") || typeDef.contains("${"))
{
return Collections.emptyList();
}
if (typeDef.indexOf(' ') < 0)
{
return Collections.singleton(getQName(typeDef));
}
String[] typeDefParts = typeDef.split(" ");
if (typeDefParts.length != 2)
{
LOGGER.warn("Ignoring invalid blacklist type pattern: " + typeDef);
return Collections.emptyList();
}
if (typeDefParts[1].equals(MARKER_INCLUDE_SUBTYPES))
{
if (typeDefParts[0].indexOf('*') >= 0)
{
LOGGER.warn("Ignoring invalid blacklist type pattern: " + typeDef);
return Collections.emptyList();
}
QName baseType;
try
{
baseType = getQName(typeDefParts[0]);
}
catch (NamespaceException ne)
{
return Collections.emptyList();
}
return dictionaryService.getSubTypes(baseType, true);
}
LOGGER.warn("Ignoring invalid blacklist type pattern: " + typeDef);
return Collections.emptyList();
return typeDefExpander.expand(typeDef);
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* 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
@@ -25,6 +25,7 @@
*/
package org.alfresco.repo.event2.filter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -43,34 +44,37 @@ public class NodePropertyFilter extends AbstractNodeEventFilter
// These properties are included as top-level info,
// so exclude them from the properties object
private static final Set<QName> EXCLUDED_TOP_LEVEL_PROPS = Set.of(ContentModel.PROP_NAME,
ContentModel.PROP_MODIFIER,
ContentModel.PROP_MODIFIED,
ContentModel.PROP_CREATOR,
ContentModel.PROP_CREATED,
ContentModel.PROP_CONTENT);
ContentModel.PROP_MODIFIER,
ContentModel.PROP_MODIFIED,
ContentModel.PROP_CREATOR,
ContentModel.PROP_CREATED,
ContentModel.PROP_CONTENT);
// These properties should not be excluded from the properties object
private static final Set<QName> ALLOWED_PROPERTIES = Set.of(ContentModel.PROP_CASCADE_TX,
ContentModel.PROP_CASCADE_CRC);
ContentModel.PROP_CASCADE_CRC);
private final List<String> nodePropertiesBlackList;
private final List<String> nodePropertiesBlackList = new ArrayList<>();
public NodePropertyFilter()
public NodePropertyFilter(String userConfiguredProperties)
{
this.nodePropertiesBlackList = parseFilterList(FILTERED_PROPERTIES);
super();
nodePropertiesBlackList.addAll(parseFilterList(FILTERED_PROPERTIES));
nodePropertiesBlackList.addAll(parseFilterList(userConfiguredProperties));
}
@Override
public Set<QName> getExcludedTypes()
{
Set<QName> result = new HashSet<>(EXCLUDED_TOP_LEVEL_PROPS);
nodePropertiesBlackList.forEach(nodeProperty-> result.addAll(expandTypeDef(nodeProperty)));
nodePropertiesBlackList.forEach(nodeProperty -> result.addAll(expandTypeDef(nodeProperty)));
return result;
}
@Override
public boolean isExcluded(QName qName)
{
if(qName != null && ALLOWED_PROPERTIES.contains(qName)){
if (qName != null && ALLOWED_PROPERTIES.contains(qName))
{
return false;
}
return super.isExcluded(qName);

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* 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
@@ -31,6 +31,7 @@ import java.util.List;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.namespace.QName;
/**
@@ -41,10 +42,13 @@ import org.alfresco.service.namespace.QName;
public class NodeTypeFilter extends AbstractNodeEventFilter
{
private final List<String> nodeTypesBlackList;
private final DictionaryService dictionaryService;
public NodeTypeFilter(String filteredNodeTypes)
public NodeTypeFilter(String filteredNodeTypes, DictionaryService dictionaryService)
{
super();
this.nodeTypesBlackList = parseFilterList(filteredNodeTypes);
this.dictionaryService = dictionaryService;
}
@Override

View File

@@ -0,0 +1,37 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2025 - 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.event2.mapper;
import java.io.Serializable;
import org.alfresco.service.namespace.QName;
public interface PropertyMapper
{
PropertyMapper NO_OP = (propertyQName, value) -> value;
Serializable map(QName propertyQName, Serializable value);
}

View File

@@ -0,0 +1,58 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2025 - 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.event2.mapper;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import org.alfresco.repo.event2.shared.CSVStringToListParser;
import org.alfresco.repo.event2.shared.TypeDefExpander;
import org.alfresco.service.namespace.QName;
public class PropertyMapperFactory
{
private final TypeDefExpander typeDefExpander;
public PropertyMapperFactory(TypeDefExpander typeDefExpander)
{
this.typeDefExpander = typeDefExpander;
}
public PropertyMapper createPropertyMapper(String enabled, String userConfiguredSensitiveProperties, String userConfiguredReplacementText)
{
if ("false".equalsIgnoreCase(enabled))
{
return PropertyMapper.NO_OP;
}
Set<QName> sensitiveProperties = Optional.ofNullable(userConfiguredSensitiveProperties)
.filter(Predicate.not(String::isEmpty))
.map(CSVStringToListParser::parse)
.map(typeDefExpander::expand)
.orElse(Set.of());
return new ReplaceSensitivePropertyWithTextMapper(sensitiveProperties, userConfiguredReplacementText);
}
}

View File

@@ -0,0 +1,72 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2025 - 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.event2.mapper;
import java.io.Serializable;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.event2.shared.QNameMatcher;
import org.alfresco.repo.transfer.TransferModel;
import org.alfresco.service.namespace.QName;
public class ReplaceSensitivePropertyWithTextMapper implements PropertyMapper
{
private static final Set<QName> DEFAULT_SENSITIVE_PROPERTIES = Set.of(
ContentModel.PROP_PASSWORD,
ContentModel.PROP_SALT,
ContentModel.PROP_PASSWORD_HASH,
TransferModel.PROP_PASSWORD);
private static final String DEFAULT_REPLACEMENT_TEXT = "SENSITIVE_DATA_REMOVED";
private final QNameMatcher qNameMatcher;
private final String replacementText;
public ReplaceSensitivePropertyWithTextMapper(Set<QName> userConfiguredSensitiveProperties, String userConfiguredReplacementText)
{
Set<QName> sensitiveProperties = Optional.ofNullable(userConfiguredSensitiveProperties)
.filter(Predicate.not(Collection::isEmpty))
.orElse(DEFAULT_SENSITIVE_PROPERTIES);
qNameMatcher = new QNameMatcher(sensitiveProperties);
replacementText = Optional.ofNullable(userConfiguredReplacementText)
.filter(Predicate.not(String::isEmpty))
.filter(userInput -> !userInput.contains("${"))
.orElse(DEFAULT_REPLACEMENT_TEXT);
}
@Override
public Serializable map(QName propertyQName, Serializable value)
{
if (qNameMatcher.isMatching(propertyQName))
{
return replacementText;
}
return value;
}
}

View File

@@ -0,0 +1,52 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2025 - 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.event2.shared;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
public final class CSVStringToListParser
{
private CSVStringToListParser()
{}
public static List<String> parse(String userInputCSV)
{
List<String> list = new LinkedList<>();
StringTokenizer st = new StringTokenizer(userInputCSV, ",");
while (st.hasMoreTokens())
{
String entry = st.nextToken().trim();
if (!entry.isEmpty() && !entry.equals("none") && !entry.contains("${"))
{
list.add(entry);
}
}
return list;
}
}

View File

@@ -0,0 +1,75 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2025 - 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.event2.shared;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.alfresco.service.namespace.QName;
public class QNameMatcher
{
private static final Logger LOGGER = LoggerFactory.getLogger(QNameMatcher.class);
private static final String WILDCARD = "*";
private final Set<QName> matchingTypes;
private final Set<String> matchingNamespaceURIs;
public QNameMatcher(Set<QName> qNamesToMatch)
{
matchingTypes = new HashSet<>();
matchingNamespaceURIs = new HashSet<>();
qNamesToMatch.forEach(qName -> {
if (WILDCARD.equals(qName.getLocalName()))
{
matchingNamespaceURIs.add(qName.getNamespaceURI());
}
else
{
matchingTypes.add(qName);
}
});
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Matching namespace URIs:" + matchingNamespaceURIs);
LOGGER.debug("Matching types:" + matchingTypes);
}
}
public boolean isMatching(QName qName)
{
if (qName != null)
{
return matchingTypes.contains(qName) || matchingNamespaceURIs.contains(qName.getNamespaceURI());
}
return false;
}
}

View File

@@ -0,0 +1,108 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2025 - 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.event2.shared;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
public class TypeDefExpander
{
private static final Logger LOGGER = LoggerFactory.getLogger(TypeDefExpander.class);
private static final String MARKER_INCLUDE_SUBTYPES = "include_subtypes";
private final DictionaryService dictionaryService;
private final NamespaceService namespaceService;
public TypeDefExpander(DictionaryService dictionaryService, NamespaceService namespaceService)
{
this.dictionaryService = dictionaryService;
this.namespaceService = namespaceService;
}
public Set<QName> expand(Collection<String> types)
{
Set<QName> result = new HashSet<>();
types.forEach(type -> result.addAll(expand(type)));
return result;
}
public Collection<QName> expand(String typeDef)
{
if ((typeDef == null) || typeDef.isEmpty() || typeDef.equals("none") || typeDef.contains("${"))
{
return Collections.emptyList();
}
if (typeDef.indexOf(' ') < 0)
{
return Collections.singleton(getQName(typeDef));
}
String[] typeDefParts = typeDef.split(" ");
if (typeDefParts.length != 2)
{
LOGGER.warn("Ignoring invalid type pattern: " + typeDef);
return Collections.emptyList();
}
if (typeDefParts[1].equals(MARKER_INCLUDE_SUBTYPES))
{
if (typeDefParts[0].indexOf('*') >= 0)
{
LOGGER.warn("Ignoring invalid type pattern: " + typeDef);
return Collections.emptyList();
}
QName baseType;
try
{
baseType = getQName(typeDefParts[0]);
}
catch (NamespaceException ne)
{
return Collections.emptyList();
}
return dictionaryService.getSubTypes(baseType, true);
}
LOGGER.warn("Ignoring invalid type pattern: " + typeDef);
return Collections.emptyList();
}
private QName getQName(String type)
{
return QName.createQName(type, namespaceService);
}
}

View File

@@ -37,6 +37,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.transform.config.CoreFunction;
import org.alfresco.util.PropertyCheck;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
@@ -46,6 +47,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import static org.alfresco.model.ContentModel.PROP_CONTENT;
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL;
@@ -68,6 +70,7 @@ public class LocalTransformClient implements TransformClient, InitializingBean
private ContentService contentService;
private RenditionService2Impl renditionService2;
private boolean directAccessUrlEnabled;
private int threadPoolSize;
private ExecutorService executorService;
private ThreadLocal<LocalTransform> transform = new ThreadLocal<>();
@@ -97,6 +100,11 @@ public class LocalTransformClient implements TransformClient, InitializingBean
this.directAccessUrlEnabled = directAccessUrlEnabled;
}
public void setThreadPoolSize(int threadPoolSize)
{
this.threadPoolSize = threadPoolSize;
}
public void setExecutorService(ExecutorService executorService)
{
this.executorService = executorService;
@@ -110,9 +118,11 @@ public class LocalTransformClient implements TransformClient, InitializingBean
PropertyCheck.mandatory(this, "contentService", contentService);
PropertyCheck.mandatory(this, "renditionService2", renditionService2);
PropertyCheck.mandatory(this, "directAccessUrlEnabled", directAccessUrlEnabled);
PropertyCheck.mandatory(this, "threadPoolSize", threadPoolSize);
if (executorService == null)
{
executorService = Executors.newCachedThreadPool();
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("local-transform-%d").build();
executorService = Executors.newFixedThreadPool(threadPoolSize, threadFactory);
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* 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
@@ -25,35 +25,27 @@
*/
package org.alfresco.repo.rendition2;
import java.util.List;
import org.alfresco.service.NotAuditable;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import java.util.List;
/**
* The Async Rendition service. Replaces the original rendition services which included synchronous renditions and
* asynchronous methods with Java call backs.<p/>
* The Async Rendition service. Replaces the original rendition services which included synchronous renditions and asynchronous methods with Java call backs.
* <p/>
*
* Renditions are defined as {@link RenditionDefinition2}s and may be registered and looked by the associated
* {@link RenditionDefinitionRegistry2}.<p/>
* Renditions are defined as {@link RenditionDefinition2}s and may be registered and looked by the associated {@link RenditionDefinitionRegistry2}.
* <p/>
*
* Unlike the original RenditionService this service, it:
* <ul>
* <li>Performs async renditions without a Java callback, as another node in the cluster may complete the rendition.
* The current node requests a transform, but another node might take the resulting transform and turn it into a
* rendition if the external Transform Service is used.</li>
* <li>Reduces the configurable options to do with with the associations of rendition nodes, their type. They
* are identical to 'hidden' (not normally seen as nodes in their own right in a
* UI) renditions produced by the original service. So, they are always directly under the source node connected by a
* {@code}rn:rendition{@code} association with the name of the rendition.</li>
* <li>The rendition nodes additionally have a {@code}rn:rendition2{@code} aspect and a {@code}contentUrlHashCode{@code}
* property. This property contains a value that allows the service to work out if it holds a rendition of the
* source node's current content.</li>
* <li>Failures are handled by setting the rendition node's content to null.</li>
* <li>When a rendition is requested via the REST API, only the newer service is used.</li>
* <li>Where possible old service renditions migrate automatically over to the new service when content on a
* source node is updated.</li>
* <li>Performs async renditions without a Java callback, as another node in the cluster may complete the rendition. The current node requests a transform, but another node might take the resulting transform and turn it into a rendition if the external Transform Service is used.</li>
* <li>Reduces the configurable options to do with with the associations of rendition nodes, their type. They are identical to 'hidden' (not normally seen as nodes in their own right in a UI) renditions produced by the original service. So, they are always directly under the source node connected by a {@code}rn:rendition{@code} association with the name of the rendition.</li>
* <li>The rendition nodes additionally have a {@code}rn:rendition2{@code} aspect and a {@code}contentUrlHashCode{@code} property. This property contains a value that allows the service to work out if it holds a rendition of the source node's current content.</li>
* <li>Failures are handled by setting the rendition node's content to null.</li>
* <li>When a rendition is requested via the REST API, only the newer service is used.</li>
* <li>Where possible old service renditions migrate automatically over to the new service when content on a source node is updated.</li>
* </ul>
*
* @author adavis
@@ -66,29 +58,30 @@ public interface RenditionService2
RenditionDefinitionRegistry2 getRenditionDefinitionRegistry2();
/**
* This method asynchronously transforms content to a target mimetype with transform options supplied in the
* {@code transformDefinition}. A response is set on a message queue once the transform is complete or fails,
* together with some client supplied data. The response queue and client data are also included in the
* transformDefinition.<p>
* This method asynchronously transforms content to a target mimetype with transform options supplied in the {@code transformDefinition}. A response is set on a message queue once the transform is complete or fails, together with some client supplied data. The response queue and client data are also included in the transformDefinition.
* <p>
*
* This method does not create a rendition node, but uses the same code as renditions to perform the transform. The
* {@code transformDefinition} extends {@link RenditionDefinition2}, but is not stored in a
* {@link RenditionDefinitionRegistry2}, as it is transient in nature.
* This method does not create a rendition node, but uses the same code as renditions to perform the transform. The {@code transformDefinition} extends {@link RenditionDefinition2}, but is not stored in a {@link RenditionDefinitionRegistry2}, as it is transient in nature.
*
* @param sourceNodeRef the node from which the content is retrieved.
* @param transformDefinition which defines the transform, where to sent the response and some client specified data.
* @throws UnsupportedOperationException if the transform is not supported.
* @param sourceNodeRef
* the node from which the content is retrieved.
* @param transformDefinition
* which defines the transform, where to sent the response and some client specified data.
* @throws UnsupportedOperationException
* if the transform is not supported.
*/
@NotAuditable
public void transform(NodeRef sourceNodeRef, TransformDefinition transformDefinition);
/**
* This method asynchronously renders content as specified by the {@code renditionName}. The content to be
* rendered is provided by {@code sourceNodeRef}.
* This method asynchronously renders content as specified by the {@code renditionName}. The content to be rendered is provided by {@code sourceNodeRef}.
*
* @param sourceNodeRef the node from which the content is retrieved.
* @param renditionName the rendition to be performed.
* @throws UnsupportedOperationException if the transform is not supported AND the rendition has not been created before.
* @param sourceNodeRef
* the node from which the content is retrieved.
* @param renditionName
* the rendition to be performed.
* @throws UnsupportedOperationException
* if the transform is not supported AND the rendition has not been created before.
*/
@NotAuditable
public void render(NodeRef sourceNodeRef, String renditionName);
@@ -104,10 +97,11 @@ public interface RenditionService2
/**
* This method gets the rendition of the {@code sourceNodeRef} identified by its name.
*
* @param sourceNodeRef the source node for the renditions
* @param renditionName the renditionName used to identify a rendition.
* @return the {@link ChildAssociationRef} which links the source node to the
* rendition or <code>null</code> if there is no rendition or it is not up to date.
* @param sourceNodeRef
* the source node for the renditions
* @param renditionName
* the renditionName used to identify a rendition.
* @return the {@link ChildAssociationRef} which links the source node to the rendition or <code>null</code> if there is no rendition or it is not up to date.
*/
@NotAuditable
ChildAssociationRef getRenditionByName(NodeRef sourceNodeRef, String renditionName);
@@ -115,7 +109,8 @@ public interface RenditionService2
/**
* This method clears source nodeRef rendition content and content hash code using supplied rendition name.
*
* @param renditionNode the rendition node
* @param renditionNode
* the rendition node
*/
@NotAuditable
void clearRenditionContentDataInTransaction(NodeRef renditionNode);
@@ -124,4 +119,13 @@ public interface RenditionService2
* Indicates if renditions are enabled. Set using the {@code system.thumbnail.generate} value.
*/
boolean isEnabled();
}
/**
* This method forces the content hash code for every {@code sourceNodeRef} renditions.
*
* @param sourceNodeRef
* the source node to update renditions hash code
*/
@NotAuditable
void forceRenditionsContentHashCode(NodeRef sourceNodeRef);
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* 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
@@ -25,6 +25,24 @@
*/
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.RenditionModel;
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.transaction.TransactionService;
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.
@@ -95,12 +96,10 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
abstract RenditionDefinition2 getRenditionDefinition();
void handleUnsupported(UnsupportedOperationException e)
{
}
{}
void throwIllegalStateExceptionIfAlreadyDone(int sourceContentHashCode)
{
}
{}
}
private TransactionService transactionService;
@@ -217,8 +216,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
@Override
public void transform(NodeRef sourceNodeRef, TransformDefinition transformDefinition)
{
requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack()
{
requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack() {
@Override
public String getName()
{
@@ -237,8 +235,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
@Override
public void render(NodeRef sourceNodeRef, String renditionName)
{
requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack()
{
requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack() {
@Override
public String getName()
{
@@ -261,7 +258,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
@Override
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);
if (renditionNode == null)
{
@@ -277,7 +274,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
if (logger.isDebugEnabled())
{
logger.debug(getName() + ": Source " + sourceContentHashCode + " rendition " + renditionContentHashCode+ " hashCodes");
logger.debug(getName() + ": Source " + sourceContentHashCode + " rendition " + renditionContentHashCode + " hashCodes");
}
if (renditionContentHashCode == sourceContentHashCode)
{
@@ -299,14 +296,14 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
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();
if (logger.isDebugEnabled())
{
logger.debug(renderOrTransform.getName()+ ": transform " +sourceNodeRef);
logger.debug(renderOrTransform.getName() + ": transform " + sourceNodeRef);
}
AtomicBoolean supported = new AtomicBoolean(true);
@@ -328,14 +325,13 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
String user = AuthenticationUtil.getRunAsUser();
RetryingTransactionHelper.RetryingTransactionCallback callback = () ->
{
RetryingTransactionHelper.RetryingTransactionCallback callback = () -> {
int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
if (!supported.get())
{
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.");
}
failure(sourceNodeRef, renditionDefinition, sourceContentHashCode);
@@ -372,26 +368,24 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
public void failure(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, int transformContentHashCode)
{
// The original transaction may have already have failed
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () ->
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{
consume(sourceNodeRef, null, renditionDefinition, transformContentHashCode);
return null;
}, false, true));
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
consume(sourceNodeRef, null, renditionDefinition, transformContentHashCode);
return null;
}, false, true));
}
public void consume(NodeRef sourceNodeRef, InputStream transformInputStream, RenditionDefinition2 renditionDefinition,
int transformContentHashCode)
int transformContentHashCode)
{
int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
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)
{
TransformDefinition transformDefinition = (TransformDefinition)renditionDefinition;
TransformDefinition transformDefinition = (TransformDefinition) renditionDefinition;
String targetMimetype = transformDefinition.getTargetMimetype();
if (AsynchronousExtractor.isMetadataExtractMimetype(targetMimetype))
{
@@ -413,7 +407,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
private void consumeExtractedMetadata(NodeRef nodeRef, int sourceContentHashCode, InputStream transformInputStream,
TransformDefinition transformDefinition, int transformContentHashCode)
TransformDefinition transformDefinition, int transformContentHashCode)
{
if (transformInputStream == null)
{
@@ -440,7 +434,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
private void consumeEmbeddedMetadata(NodeRef nodeRef, int sourceContentHashCode, InputStream transformInputStream,
TransformDefinition transformDefinition, int transformContentHashCode)
TransformDefinition transformDefinition, int transformContentHashCode)
{
if (transformInputStream == null)
{
@@ -468,7 +462,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
private void consumeTransformReply(NodeRef sourceNodeRef, InputStream transformInputStream,
TransformDefinition transformDefinition, int transformContentHashCode)
TransformDefinition transformDefinition, int transformContentHashCode)
{
if (logger.isDebugEnabled())
{
@@ -484,12 +478,10 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
/**
* 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.
* 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.
*/
private void consumeRendition(NodeRef sourceNodeRef, int sourceContentHashCode, InputStream transformInputStream,
RenditionDefinition2 renditionDefinition, int transformContentHashCode)
RenditionDefinition2 renditionDefinition, int transformContentHashCode)
{
String renditionName = renditionDefinition.getRenditionName();
if (transformContentHashCode != sourceContentHashCode)
@@ -507,93 +499,92 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
(transformInputStream == null ? " to null as the transform failed" : " to the transform result"));
}
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () ->
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () -> 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
{
ruleService.disableRuleType(RuleType.UPDATE);
behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE);
behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE);
// 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);
// If they do not exist create the rendition association and the rendition node.
if (createRenditionNode)
ContentReader contentReader = renditionWriter.getReader();
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())
{
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)
{
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;
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);
}
if (!sourceHasAspectRenditioned)
{
nodeService.addAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED, null);
}
}
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
{
behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE);
behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE);
ruleService.enableRuleType(RuleType.UPDATE);
}
return null;
}, false, true));
}
else
{
clearRenditionContentData(renditionNode);
}
if (!sourceHasAspectRenditioned)
{
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 +625,14 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
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))
{
List<String> thumbnailMods = (List<String>) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA);
String target = null;
for (String currThumbnailMod: thumbnailMods)
for (String currThumbnailMod : thumbnailMods)
{
if (currThumbnailMod.startsWith(prefix))
{
@@ -665,8 +656,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
* sequences to which they were requested, this is used work out if a rendition should be replaced.
* 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.
*/
private int getSourceContentHashCode(NodeRef sourceNodeRef)
{
@@ -675,7 +665,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
if (contentData != null)
{
// 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)
{
hashCode = contentString.hashCode();
@@ -685,13 +675,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.
* 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.
* 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.
*/
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;
}
@@ -699,7 +687,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
Serializable hashCode = nodeService.getProperty(renditionNode, PROP_RENDITION_CONTENT_HASH_CODE);
return hashCode == null
? SOURCE_HAS_NO_CONTENT
: (int)hashCode;
: (int) hashCode;
}
private NodeRef getRenditionNode(NodeRef sourceNodeRef, String renditionName)
@@ -773,11 +761,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
* rendition prevention.
* This method checks whether the specified source node is of a content class which has been registered for rendition prevention.
*
* @param sourceNode the node to check.
* @throws RenditionService2PreventedException if the source node is configured for rendition prevention.
* @param sourceNode
* the node to check.
* @throws RenditionService2PreventedException
* if the source node is configured for rendition prevention.
*/
// This code is based on the old RenditionServiceImpl.checkSourceNodeForPreventionClass(...)
private void checkSourceNodeForPreventionClass(NodeRef sourceNode)
@@ -823,7 +812,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
for (ChildAssociationRef childAssoc : childAsocs)
{
NodeRef renditionNode = childAssoc.getChildRef();
NodeRef renditionNode = childAssoc.getChildRef();
if (isRenditionAvailable(sourceNodeRef, renditionNode))
{
result.add(childAssoc);
@@ -833,8 +822,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
/**
* 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.
* 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.
*/
public boolean isRenditionAvailable(NodeRef sourceNodeRef, NodeRef renditionNode)
{
@@ -852,7 +840,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
if (logger.isDebugEnabled())
{
logger.debug("isRenditionAvailable source " + sourceContentHashCode + " and rendition " + renditionContentHashCode+" hashcodes");
logger.debug("isRenditionAvailable source " + sourceContentHashCode + " and rendition " + renditionContentHashCode + " hashcodes");
}
if (sourceContentHashCode != renditionContentHashCode)
{
@@ -892,19 +880,17 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
ChildAssociationRef childAssoc = renditions.get(0);
NodeRef renditionNode = childAssoc.getChildRef();
return !isRenditionAvailable(sourceNodeRef, renditionNode) ? null: childAssoc;
return !isRenditionAvailable(sourceNodeRef, renditionNode) ? null : childAssoc;
}
}
@Override
public void clearRenditionContentDataInTransaction(NodeRef renditionNode)
{
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () ->
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{
clearRenditionContentData(renditionNode);
return null;
}, false, true));
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
clearRenditionContentData(renditionNode);
return null;
}, false, true));
}
@Override
@@ -950,4 +936,35 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
}
@Override
public void forceRenditionsContentHashCode(NodeRef sourceNodeRef)
{
if (sourceNodeRef != null && nodeService.exists(sourceNodeRef))
{
List<ChildAssociationRef> renditions = getRenditionChildAssociations(sourceNodeRef);
if (renditions != null)
{
int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
for (ChildAssociationRef rendition : renditions)
{
NodeRef renditionNode = rendition.getChildRef();
if (nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
{
int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
String renditionName = rendition.getQName().getLocalName();
if (sourceContentHashCode != renditionContentHashCode)
{
if (logger.isDebugEnabled())
{
logger.debug("Update content hash code for rendition " + renditionName + " of node "
+ sourceNodeRef);
}
nodeService.setProperty(renditionNode, PROP_RENDITION_CONTENT_HASH_CODE,
sourceContentHashCode);
}
}
}
}
}
}
}

View File

@@ -30,12 +30,12 @@ import jakarta.servlet.http.HttpServletResponse;
import org.alfresco.repo.management.subsystems.ActivateableBean;
/**
* A default {@link AdminConsoleAuthenticator} implementation. Returns null to request a basic auth challenge.
* A default {@link ExternalUserAuthenticator} implementation. Returns null to request a basic auth challenge.
*/
public class DefaultAdminConsoleAuthenticator implements AdminConsoleAuthenticator, ActivateableBean
public class DefaultAdminConsoleAuthenticator implements ExternalUserAuthenticator, ActivateableBean
{
@Override
public String getAdminConsoleUser(HttpServletRequest request, HttpServletResponse response)
public String getUserId(HttpServletRequest request, HttpServletResponse response)
{
return null;
}

View File

@@ -0,0 +1,55 @@
/*
* #%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.external;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.alfresco.repo.management.subsystems.ActivateableBean;
/**
* A default {@link ExternalUserAuthenticator} implementation. Returns null to request a basic auth challenge.
*/
public class DefaultWebScriptsHomeAuthenticator implements ExternalUserAuthenticator, ActivateableBean
{
@Override
public String getUserId(HttpServletRequest request, HttpServletResponse response)
{
return null;
}
@Override
public void requestAuthentication(HttpServletRequest request, HttpServletResponse response)
{
// No implementation
}
@Override
public boolean isActive()
{
return false;
}
}

View File

@@ -29,28 +29,17 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* An interface for objects capable of extracting an externally authenticated user ID from the HTTP Admin Console webscript request.
* An interface for objects capable of extracting an externally authenticated user ID from the HTTP request.
*/
public interface AdminConsoleAuthenticator
public interface ExternalUserAuthenticator
{
/**
* Gets an externally authenticated user ID from the HTTP Admin Console webscript request.
* Gets an externally authenticated user ID from the HTTP request.
*
* @param request
* the request
* @param response
* the response
* @return the user ID or <code>null</code> if the user is unauthenticated
*/
String getAdminConsoleUser(HttpServletRequest request, HttpServletResponse response);
String getUserId(HttpServletRequest request, HttpServletResponse response);
/**
* Requests an authentication.
*
* @param request
* the request
* @param response
* the response
*/
/* Sends redirect to external site to initiate the OIDC authorization code flow. */
void requestAuthentication(HttpServletRequest request, HttpServletResponse response);
}

Some files were not shown because too many files have changed in this diff Show More