mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	Compare commits
	
		
			58 Commits
		
	
	
		
			feature/AC
			...
			dependabot
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 39bb92585a | ||
|  | 0f3e2dc4cc | ||
|  | 4e7d0ccae3 | ||
|  | 1b5636a339 | ||
|  | 164ce720af | ||
|  | 258738e3dd | ||
|  | fefd937c89 | ||
|  | 91f9467a99 | ||
|  | 97b1515f7c | ||
|  | 7f235f1e2b | ||
|  | 109bdeee0f | ||
|  | 7c97f49574 | ||
|  | 2088b8b553 | ||
|  | 280a873cb6 | ||
|  | 9683c18448 | ||
|  | 21b36a7100 | ||
|  | 96481daae1 | ||
|  | 7ef573699b | ||
|  | a000df7ceb | ||
|  | 4a22735120 | ||
|  | 94d84799be | ||
|  | 754776e30c | ||
|  | 28b8bb85e4 | ||
|  | 4910028d51 | ||
|  | 75d0825295 | ||
|  | 964cedaebd | ||
|  | 2bda7d7231 | ||
|  | 82d316d802 | ||
|  | 1840d1056d | ||
|  | 334e8c84df | ||
|  | 24309cf4b6 | ||
|  | 2d28742a94 | ||
|  | d6503ac1de | ||
|  | 73ef1ed9ff | ||
|  | d3bc9e2b60 | ||
|  | 174186d1ff | ||
|  | 57331afe8f | ||
|  | 7c87595b0c | ||
|  | b7191b175e | ||
|  | 39b19d1ceb | ||
|  | 9e23b99078 | ||
|  | ac36ac07e8 | ||
|  | cc10339577 | ||
|  | 8aa975fbc3 | ||
|  | 01620b75ff | ||
|  | 9327218f17 | ||
|  | b57373fbe3 | ||
|  | 613fb458b9 | ||
|  | 31cd97b9d2 | ||
|  | 255fe46c8e | ||
|  | 88d32748b1 | ||
|  | 90e1522a56 | ||
|  | 7e61befc21 | ||
|  | be6dc14330 | ||
|  | ab4bc1af9f | ||
|  | eaec23ae7a | ||
|  | 8ff727c95d | ||
|  | 48475fdfaa | 
							
								
								
									
										205
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										205
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -15,7 +15,6 @@ on: | ||||
|   workflow_dispatch: | ||||
|  | ||||
| env: | ||||
|   JAVA_VERSION: '21' | ||||
|   DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} | ||||
|   DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USERNAME }} | ||||
|   GITHUB_ACTIONS_DEPLOY_TIMEOUT: 60 | ||||
| @@ -39,15 +38,13 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: actions/checkout@v5 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/pre-commit@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/pre-commit@v9.0.1 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Prepare maven cache and check compilation" | ||||
| @@ -64,15 +61,13 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/veracode@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/veracode@v9.0.1 | ||||
|         continue-on-error: true | ||||
|         with: | ||||
|           srcclr-api-token: ${{ secrets.SRCCLR_API_TOKEN }} | ||||
| @@ -89,13 +84,11 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/github-download-file@v8.24.1 | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/github-download-file@v9.0.1 | ||||
|         with: | ||||
|           token: ${{ secrets.BOT_GITHUB_TOKEN }} | ||||
|           repository: "Alfresco/veracode-baseline-archive" | ||||
| @@ -148,11 +141,9 @@ 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@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - uses: Alfresco/ya-pmd-scan@v4.3.0 | ||||
|         with: | ||||
|           classpath-build-command: "mvn test-compile -ntp -Pags -pl \"-:alfresco-community-repo-docker\"" | ||||
| @@ -182,12 +173,10 @@ jobs: | ||||
|             testModule: mmt | ||||
|             testAttributes: "-Dtest=AllMmtUnitTestSuite" | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run tests" | ||||
| @@ -221,12 +210,10 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_INSTALLED_ARTIFACTS: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Build" | ||||
|         timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} | ||||
|         run: | | ||||
| @@ -258,12 +245,10 @@ jobs: | ||||
|       matrix: | ||||
|         version: ['10.5', '10.6'] | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: Run MariaDB ${{ matrix.version }} database | ||||
| @@ -287,12 +272,10 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run MariaDB 10.11 database" | ||||
| @@ -316,12 +299,10 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run MySQL 8 database" | ||||
| @@ -344,12 +325,10 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run PostgreSQL 14.15 database" | ||||
| @@ -372,12 +351,10 @@ jobs: | ||||
|             !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|             !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run PostgreSQL 15.10 database" | ||||
| @@ -400,12 +377,10 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run PostgreSQL 16.6 database" | ||||
| @@ -426,12 +401,10 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run ActiveMQ" | ||||
| @@ -484,12 +457,10 @@ jobs: | ||||
|             disabledHostnameVerification: false | ||||
|             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@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Set transformers tag" | ||||
| @@ -556,12 +527,10 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_LOCAL_IMAGES: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Build" | ||||
|         timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} | ||||
|         run: | | ||||
| @@ -597,12 +566,10 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run Postgres 16.6 database" | ||||
| @@ -629,12 +596,10 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_INSTALLED_ARTIFACTS: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Build" | ||||
|         timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} | ||||
|         run: | | ||||
| @@ -663,12 +628,10 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_INSTALLED_ARTIFACTS: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Build" | ||||
|         timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} | ||||
|         run: | | ||||
| @@ -693,12 +656,10 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_LOCAL_IMAGES: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Build" | ||||
|         timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} | ||||
|         run: | | ||||
| @@ -741,12 +702,10 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force]') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: actions/checkout@v5 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Build" | ||||
|         timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} | ||||
|         run: | | ||||
|   | ||||
							
								
								
									
										25
									
								
								.github/workflows/master_release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								.github/workflows/master_release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -7,7 +7,6 @@ on: | ||||
|       - release/** | ||||
|  | ||||
| env: | ||||
|   JAVA_VERSION: '21' | ||||
|   GIT_USERNAME: ${{ secrets.BOT_GITHUB_USERNAME }} | ||||
|   GIT_EMAIL: ${{ secrets.BOT_GITHUB_EMAIL }} | ||||
|   GIT_PASSWORD: ${{ secrets.BOT_GITHUB_TOKEN }} | ||||
| @@ -32,17 +31,15 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[no release]') && | ||||
|       github.event_name != 'pull_request' | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: actions/checkout@v5 | ||||
|         with: | ||||
|           persist-credentials: false | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v9.0.1 | ||||
|         with: | ||||
|           username: ${{ env.GIT_USERNAME }} | ||||
|           email: ${{ env.GIT_EMAIL }} | ||||
| @@ -63,17 +60,15 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[no downstream]') && | ||||
|       github.event_name != 'pull_request' | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: actions/checkout@v5 | ||||
|         with: | ||||
|           persist-credentials: false | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.24.1 | ||||
|         with: | ||||
|           java-version: ${{ env.JAVA_VERSION }} | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v9.0.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v9.0.1 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v9.0.1 | ||||
|         with: | ||||
|           username: ${{ env.GIT_USERNAME }} | ||||
|           email: ${{ env.GIT_EMAIL }} | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/precommit_formatter.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/precommit_formatter.yml
									
									
									
									
										vendored
									
									
								
							| @@ -11,7 +11,7 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     if: contains(github.event.head_commit.message, '[reformat code]') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: actions/checkout@v5 | ||||
|       - name: Set up Python ${{ inputs.python-version }} | ||||
|         uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 | ||||
|         with: | ||||
| @@ -22,7 +22,7 @@ jobs: | ||||
|           extra_args: --all-files | ||||
|       - name: Update secrets baseline | ||||
|         run: pip install detect-secrets && detect-secrets scan --baseline .secrets.baseline | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/git-commit-changes@v8.24.1 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/git-commit-changes@v9.0.1 | ||||
|         with: | ||||
|           username: ${{ secrets.BOT_GITHUB_USERNAME }} | ||||
|           add-options: -u | ||||
|   | ||||
| @@ -133,7 +133,7 @@ | ||||
|         "filename": ".github/workflows/master_release.yml", | ||||
|         "hashed_secret": "3e26d6750975d678acb8fa35a0f69237881576b0", | ||||
|         "is_verified": false, | ||||
|         "line_number": 25, | ||||
|         "line_number": 24, | ||||
|         "is_secret": false | ||||
|       } | ||||
|     ], | ||||
| @@ -1242,7 +1242,7 @@ | ||||
|         "filename": "repository/src/main/resources/alfresco/repository.properties", | ||||
|         "hashed_secret": "1459a56410378e4d3ab470eff570e5eae1742762", | ||||
|         "is_verified": false, | ||||
|         "line_number": 312, | ||||
|         "line_number": 314, | ||||
|         "is_secret": false | ||||
|       }, | ||||
|       { | ||||
| @@ -1250,7 +1250,7 @@ | ||||
|         "filename": "repository/src/main/resources/alfresco/repository.properties", | ||||
|         "hashed_secret": "84551ae5442affc9f1a2d3b4c86ae8b24860149d", | ||||
|         "is_verified": false, | ||||
|         "line_number": 771, | ||||
|         "line_number": 773, | ||||
|         "is_secret": false | ||||
|       } | ||||
|     ], | ||||
| @@ -1845,5 +1845,5 @@ | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   "generated_at": "2025-09-04T08:27:43Z" | ||||
|   "generated_at": "2025-07-23T08:25:11Z" | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-community-repo-amps</artifactId> | ||||
|       <version>25.3.0.33-SNAPSHOT</version> | ||||
|       <version>25.3.0.51-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-parent</artifactId> | ||||
|       <version>25.3.0.33-SNAPSHOT</version> | ||||
|       <version>25.3.0.51-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-automation-community-repo</artifactId> | ||||
|       <version>25.3.0.33-SNAPSHOT</version> | ||||
|       <version>25.3.0.51-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <build> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-parent</artifactId> | ||||
|       <version>25.3.0.33-SNAPSHOT</version> | ||||
|       <version>25.3.0.51-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -15,6 +15,13 @@ | ||||
|       <parameter property="end" jdbcType="BIGINT" javaType="java.lang.Long"/> | ||||
|    </parameterMap> | ||||
|  | ||||
|    <parameterMap id="parameter_NodeIdsWhichReferenceContentUrl" type="map"> | ||||
|       <parameter property="contentUrlShort" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|       <parameter property="contentUrlCrc" jdbcType="BIGINT" javaType="java.lang.Long"/> | ||||
|       <parameter property="localName" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|       <parameter property="uri" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|    </parameterMap> | ||||
|  | ||||
|    <resultMap id="result_NodeRefEntity" type="org.alfresco.module.org_alfresco_module_rm.query.NodeRefEntity"> | ||||
|       <result property="row" column="row" jdbcType="BIGINT" javaType="java.lang.Long"/> | ||||
|       <result property="protocol" column="protocol" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
| @@ -55,18 +62,21 @@ | ||||
|  | ||||
|    <!-- Get list of node ids which reference given content url --> | ||||
|    <select id="select_NodeIdsWhichReferenceContentUrl" | ||||
|            parameterType="ContentUrl" | ||||
|            parameterMap="parameter_NodeIdsWhichReferenceContentUrl" | ||||
|            resultMap="result_NodeIds"> | ||||
|       select | ||||
|          p.node_id | ||||
|       from | ||||
|          alf_content_url cu | ||||
|       LEFT OUTER JOIN alf_content_data cd ON (cd.content_url_id = cu.id) | ||||
|       LEFT OUTER JOIN alf_node_properties p ON (p.long_value = cd.id) | ||||
|       WHERE | ||||
|          content_url_short = #{contentUrlShort} and | ||||
|          content_url_crc = #{contentUrlCrc} | ||||
|  | ||||
|          left outer join alf_content_data cd ON (cd.content_url_id = cu.id) | ||||
|          left outer join alf_node_properties p ON (p.long_value = cd.id) | ||||
|          left outer join alf_qname q ON (q.id = p.qname_id) | ||||
|          left outer join alf_namespace n ON (n.id = q.ns_id) | ||||
|       where | ||||
|          cu.content_url_short = ? and | ||||
|          cu.content_url_crc = ? and | ||||
|          q.local_name = ? and | ||||
|          n.uri = ? | ||||
|    </select> | ||||
|  | ||||
|    <select id="select_RecordFoldersWithSchedules" | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-repo-parent</artifactId> | ||||
|       <version>25.3.0.33-SNAPSHOT</version> | ||||
|       <version>25.3.0.51-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <properties> | ||||
|   | ||||
| @@ -39,6 +39,7 @@ import org.apache.commons.logging.Log; | ||||
| import org.apache.commons.logging.LogFactory; | ||||
| import org.mybatis.spring.SqlSessionTemplate; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; | ||||
| import org.alfresco.repo.domain.contentdata.ContentUrlEntity; | ||||
| import org.alfresco.repo.domain.node.NodeDAO; | ||||
| @@ -191,13 +192,19 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, | ||||
|         ContentUrlEntity contentUrlEntity = new ContentUrlEntity(); | ||||
|         contentUrlEntity.setContentUrl(contentUrl.toLowerCase()); | ||||
|  | ||||
|         Map<String, Object> params = new HashMap<>(4); | ||||
|         params.put("contentUrlShort", contentUrlEntity.getContentUrlShort()); | ||||
|         params.put("contentUrlCrc", contentUrlEntity.getContentUrlCrc()); | ||||
|         params.put("localName", ContentModel.PROP_CONTENT.getLocalName()); | ||||
|         params.put("uri", ContentModel.PROP_CONTENT.getNamespaceURI()); | ||||
|  | ||||
|         if (logger.isDebugEnabled()) | ||||
|         { | ||||
|             logger.debug("Executing query " + SELECT_NODE_IDS_WHICH_REFERENCE_CONTENT_URL); | ||||
|         } | ||||
|  | ||||
|         // Get all the node ids which reference the given content url | ||||
|         List<Long> nodeIds = template.selectList(SELECT_NODE_IDS_WHICH_REFERENCE_CONTENT_URL, contentUrlEntity); | ||||
|         List<Long> nodeIds = template.selectList(SELECT_NODE_IDS_WHICH_REFERENCE_CONTENT_URL, params); | ||||
|  | ||||
|         if (logger.isDebugEnabled()) | ||||
|         { | ||||
|   | ||||
| @@ -36,6 +36,7 @@ import java.util.Set; | ||||
|  | ||||
| import org.springframework.context.ApplicationListener; | ||||
| import org.springframework.context.event.ContextRefreshedEvent; | ||||
| import org.springframework.dao.ConcurrencyFailureException; | ||||
| import org.springframework.extensions.webscripts.ui.common.StringUtils; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| @@ -649,8 +650,8 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|         } | ||||
|         catch (DuplicateChildNodeNameException ex) | ||||
|         { | ||||
|             // the group was concurrently created | ||||
|             group = authorityService.getName(AuthorityType.GROUP, groupShortName); | ||||
|             // Rethrow as ConcurrencyFailureException so that is can be retried and linked to the group created by the concurrent transaction | ||||
|             throw new ConcurrencyFailureException("IPR group creation failed due to concurrent duplicate group name creation: " + groupShortName); | ||||
|         } | ||||
|  | ||||
|         return group; | ||||
|   | ||||
| @@ -29,14 +29,23 @@ package org.alfresco.module.org_alfresco_module_rm.test.legacy.service; | ||||
|  | ||||
| import java.util.HashSet; | ||||
| import java.util.Set; | ||||
| import java.util.concurrent.CompletableFuture; | ||||
|  | ||||
| import org.junit.Assert; | ||||
| import org.springframework.dao.ConcurrencyFailureException; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; | ||||
| import org.alfresco.query.PagingRequest; | ||||
| import org.alfresco.query.PagingResults; | ||||
| import org.alfresco.repo.security.authentication.AuthenticationUtil; | ||||
| import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; | ||||
| import org.alfresco.repo.site.SiteModel; | ||||
| import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.security.AccessPermission; | ||||
| import org.alfresco.service.cmr.security.AccessStatus; | ||||
| import org.alfresco.service.cmr.security.AuthorityType; | ||||
| import org.alfresco.service.cmr.site.SiteService; | ||||
| import org.alfresco.service.cmr.site.SiteVisibility; | ||||
| import org.alfresco.util.GUID; | ||||
| @@ -206,7 +215,8 @@ public class ExtendedSecurityServiceImplTest extends BaseRMTestCase | ||||
|         final NodeRef record = doTestInTransaction(new Test<NodeRef>() { | ||||
|             public NodeRef run() throws Exception | ||||
|             { | ||||
|                 NodeRef record = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef(); | ||||
|                 NodeRef record = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_CONTENT) | ||||
|                         .getNodeRef(); | ||||
|                 recordService.createRecord(filePlan, record); | ||||
|                 return record; | ||||
|             } | ||||
| @@ -279,4 +289,238 @@ public class ExtendedSecurityServiceImplTest extends BaseRMTestCase | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public void testConcurrentSetWithRetry() | ||||
|     { | ||||
|         Set<String> extendedReaders = new HashSet<>(2); | ||||
|         Set<String> extendedWriters = new HashSet<>(2); | ||||
|  | ||||
|         Set<NodeRef> documents = setupConcurrentTestCase(10, extendedReaders, extendedWriters); | ||||
|  | ||||
|         // For each record created previously, spawn a thread to set extended security so we cause concurrency | ||||
|         // failure trying to create IPR groups with the same name | ||||
|         fireParallelExecutionOfSetExtendedSecurity(documents, extendedReaders, extendedWriters, true); | ||||
|  | ||||
|         // Look for duplicated IPR groups and verify all documents have the same groups assigned | ||||
|         verifyCreatedGroups(documents, false); | ||||
|  | ||||
|         AuthenticationUtil.clearCurrentSecurityContext(); | ||||
|     } | ||||
|  | ||||
|     public void testConcurrentSetWithoutRetry() | ||||
|     { | ||||
|         Set<String> extendedReaders = new HashSet<>(2); | ||||
|         Set<String> extendedWriters = new HashSet<>(2); | ||||
|  | ||||
|         Set<NodeRef> documents = setupConcurrentTestCase(10, extendedReaders, extendedWriters); | ||||
|  | ||||
|         // For each record created previously, spawn a thread to set extended security so we cause concurrency | ||||
|         // failure trying to create IPR groups with the same name. | ||||
|         // Since there is no retry, we expect to get a ConcurrencyFailureException | ||||
|         Assert.assertThrows(ConcurrencyFailureException.class, () -> { | ||||
|             fireParallelExecutionOfSetExtendedSecurity(documents, extendedReaders, extendedWriters, false); | ||||
|         }); | ||||
|  | ||||
|         // Look for duplicated IPR groups and verify all documents have the same groups assigned | ||||
|         // Since there was a ConcurrencyFailureException some threads failed to set extended security so some | ||||
|         // documents may not have IPR groups created. | ||||
|         verifyCreatedGroups(documents, true); | ||||
|  | ||||
|         AuthenticationUtil.clearCurrentSecurityContext(); | ||||
|     } | ||||
|  | ||||
|     private Set<NodeRef> setupConcurrentTestCase(int concurrentThreads, Set<String> extendedReaders, Set<String> extendedWriters) | ||||
|     { | ||||
|         final String usera = createTestUser(); | ||||
|         final String userb = createTestUser(); | ||||
|         final String owner = createTestUser(); | ||||
|  | ||||
|         extendedReaders.add(usera); | ||||
|         extendedReaders.add(userb); | ||||
|         extendedWriters.add(usera); | ||||
|         extendedWriters.add(userb); | ||||
|  | ||||
|         AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); | ||||
|  | ||||
|         // Create a site | ||||
|         NodeRef documentLib = createSite(new HashSet<>(), new HashSet<>()); | ||||
|  | ||||
|         // Create records in the site document library | ||||
|         return createRecords(concurrentThreads, documentLib, owner); | ||||
|     } | ||||
|  | ||||
|     private NodeRef createSite(Set<String> readers, Set<String> writers) | ||||
|     { | ||||
|         return retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<NodeRef>() { | ||||
|             @Override | ||||
|             public NodeRef execute() throws Throwable | ||||
|             { | ||||
|                 final String siteShortName = GUID.generate(); | ||||
|                 siteService.createSite(null, siteShortName, "test", "test", SiteVisibility.PRIVATE); | ||||
|                 readers.forEach(reader -> siteService.setMembership(siteShortName, reader, SiteModel.SITE_CONSUMER)); | ||||
|                 writers.forEach(writer -> siteService.setMembership(siteShortName, writer, SiteModel.SITE_COLLABORATOR)); | ||||
|                 return siteService.createContainer(siteShortName, SiteService.DOCUMENT_LIBRARY, null, null); | ||||
|             } | ||||
|         }, false, true); | ||||
|     } | ||||
|  | ||||
|     private Set<NodeRef> createRecords(int numRecords, NodeRef parent, String owner) | ||||
|     { | ||||
|         return retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Set<NodeRef>>() { | ||||
|             @Override | ||||
|             public Set<NodeRef> execute() throws Throwable | ||||
|             { | ||||
|                 int createdRecords = 0; | ||||
|                 Set<NodeRef> documents = new HashSet<>(); | ||||
|                 while (createdRecords < numRecords) | ||||
|                 { | ||||
|                     final NodeRef doc = fileFolderService.create(parent, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef(); | ||||
|                     ownableService.setOwner(doc, owner); | ||||
|                     recordService.createRecord(filePlan, doc, rmFolder, true); | ||||
|                     recordService.file(doc); | ||||
|                     recordService.complete(doc); | ||||
|                     documents.add(doc); | ||||
|                     createdRecords++; | ||||
|                 } | ||||
|                 return documents; | ||||
|             } | ||||
|         }, false, true); | ||||
|     } | ||||
|  | ||||
|     private void setExtendedSecurity(NodeRef doc, Set<String> readers, Set<String> writers, boolean useRetry) | ||||
|     { | ||||
|         if (!useRetry) | ||||
|         { | ||||
|             setExtendedSecurity(doc, readers, writers); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Void>() { | ||||
|             @Override | ||||
|             public Void execute() throws Throwable | ||||
|             { | ||||
|                 setExtendedSecurity(doc, readers, writers); | ||||
|                 return null; | ||||
|             } | ||||
|         }, false, true); | ||||
|     } | ||||
|  | ||||
|     private void setExtendedSecurity(NodeRef doc, Set<String> readers, Set<String> writers) | ||||
|     { | ||||
|         AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); | ||||
|         extendedSecurityService.set(doc, readers, writers); | ||||
|     } | ||||
|  | ||||
|     private void fireParallelExecutionOfSetExtendedSecurity(Set<NodeRef> documents, Set<String> extendedReaders, Set<String> extendedWriters, boolean useRetry) | ||||
|     { | ||||
|         CompletableFuture<?>[] futures = documents.stream() | ||||
|                 .map(doc -> CompletableFuture.runAsync(() -> setExtendedSecurity(doc, extendedReaders, extendedWriters, useRetry))) | ||||
|                 .toArray(CompletableFuture[]::new); | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             CompletableFuture.allOf(futures).join(); | ||||
|         } | ||||
|         catch (Exception e) | ||||
|         { | ||||
|             Throwable cause = e.getCause(); | ||||
|             if (cause instanceof ConcurrencyFailureException) | ||||
|             { | ||||
|                 throw (ConcurrencyFailureException) cause; | ||||
|             } | ||||
|             throw new RuntimeException("Error during parallel execution", e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void verifyCreatedGroups(Set<NodeRef> documents, boolean onlyDuplicatesValidation) | ||||
|     { | ||||
|         retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Void>() { | ||||
|             @Override | ||||
|             public Void execute() throws Throwable | ||||
|             { | ||||
|                 Set<String> expectedAuthorities = null; | ||||
|                 Set<Set<String>> errors = new HashSet<>(); | ||||
|                 for (NodeRef doc : documents) | ||||
|                 { | ||||
|                     Set<AccessPermission> permissions = permissionService.getAllSetPermissions(doc); | ||||
|                     Set<String> authorities = getDocumentAuthorities(permissions); | ||||
|                     Set<String> authoritiesById = getAuthorityIds(authorities); | ||||
|  | ||||
|                     verifyIPRGroups(authorities, onlyDuplicatesValidation); | ||||
|  | ||||
|                     if (onlyDuplicatesValidation) | ||||
|                     { | ||||
|                         // Some documents may not have IPR groups created if there was a ConcurrencyFailureException | ||||
|                         continue; | ||||
|                     } | ||||
|  | ||||
|                     // All documents should have the same exact set of groups assigned | ||||
|                     if (expectedAuthorities == null) | ||||
|                     { | ||||
|                         expectedAuthorities = authoritiesById; | ||||
|                     } | ||||
|  | ||||
|                     if (!expectedAuthorities.equals(authoritiesById)) | ||||
|                     { | ||||
|                         errors.add(authoritiesById); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 assertTrue("Unexpected authorities linked to document", errors.isEmpty()); | ||||
|  | ||||
|                 return null; | ||||
|             } | ||||
|         }, false, true); | ||||
|     } | ||||
|  | ||||
|     private Set<String> getDocumentAuthorities(Set<AccessPermission> permissions) | ||||
|     { | ||||
|         Set<String> authorities = new HashSet<>(); | ||||
|  | ||||
|         for (AccessPermission accessPermission : permissions) | ||||
|         { | ||||
|             String authority = accessPermission.getAuthority(); | ||||
|             String authName = authorityService.getName(AuthorityType.GROUP, authority); | ||||
|             authorities.add(authName); | ||||
|  | ||||
|         } | ||||
|         return authorities; | ||||
|     } | ||||
|  | ||||
|     private Set<String> getAuthorityIds(Set<String> authorities) | ||||
|     { | ||||
|         Set<String> authorityIds = new HashSet<>(); | ||||
|         for (String authority : authorities) | ||||
|         { | ||||
|             String authId = authorityService.getAuthorityNodeRef(authority) != null | ||||
|                     ? authorityService.getAuthorityNodeRef(authority).getId() | ||||
|                     : null; | ||||
|             authorityIds.add(authId); | ||||
|         } | ||||
|         return authorityIds; | ||||
|     } | ||||
|  | ||||
|     private void verifyIPRGroups(Set<String> authorities, boolean onlyDuplicatesValidation) | ||||
|     { | ||||
|         boolean hasGroupIPR = false; | ||||
|  | ||||
|         for (String authorityName : authorities) | ||||
|         { | ||||
|             String shortName = authorityService.getShortName(authorityName); | ||||
|  | ||||
|             if (authorityName.startsWith("GROUP_IPR")) | ||||
|             { | ||||
|                 hasGroupIPR = true; | ||||
|                 PagingResults<String> results = authorityService.getAuthorities(AuthorityType.GROUP, null, shortName, false, | ||||
|                         false, new PagingRequest(0, 10)); | ||||
|  | ||||
|                 assertEquals("No duplicated IPR group expected", 1, results.getPage().size()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!onlyDuplicatesValidation) | ||||
|         { | ||||
|             assertTrue("No IPR Groups created", hasGroupIPR); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-governance-services-community-repo-parent</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <build> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-amps</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
| @@ -134,6 +134,25 @@ function doclist_main() | ||||
|       logger.log("doclist.lib.js - NodeRef: " + parsedArgs.nodeRef + " Query: " + query); | ||||
|  | ||||
|    favourites = sanitizeJunkFavouriteKeys(favourites); | ||||
|    if(query === null) | ||||
|    { | ||||
|       return { | ||||
|          luceneQuery: "", | ||||
|          paging: { | ||||
|             totalRecords: 0, | ||||
|             startIndex: 0 | ||||
|          }, | ||||
|          container: parsedArgs.rootNode, | ||||
|          parent: null, | ||||
|          onlineEditing: utils.moduleInstalled("org.alfresco.module.vti"), | ||||
|          itemCount: { | ||||
|             folders: 0, | ||||
|             documents: 0 | ||||
|          }, | ||||
|          items: [], | ||||
|          customJSON: slingshotDocLib.getJSON() | ||||
|       }; | ||||
|    } | ||||
|  | ||||
|    if(Object.keys(favourites).length === 0 && query === null) | ||||
|    { | ||||
|   | ||||
| @@ -233,15 +233,15 @@ var Filters = | ||||
|             filterParams.query = "+ID:\"" + parsedArgs.nodeRef + "\""; | ||||
|             break; | ||||
|  | ||||
|          case "tag": | ||||
|             // Remove any trailing "/" character | ||||
|             if (filterData.charAt(filterData.length - 1) == "/") | ||||
|             { | ||||
|                filterData = filterData.slice(0, -1); | ||||
|             } | ||||
|             filterQuery = this.constructPathQuery(parsedArgs); | ||||
|             filterParams.query = filterQuery + " +PATH:\"/cm:taggable/cm:" + search.ISO9075Encode(filterData) + "/member\""; | ||||
|             break; | ||||
|           case "tag": | ||||
|               // Remove any trailing "/" character | ||||
|               if (filterData.charAt(filterData.length - 1) == "/") | ||||
|               { | ||||
|                   filterData = filterData.slice(0, -1); | ||||
|               } | ||||
|               filterQuery = this.constructPathQuery(parsedArgs); | ||||
|               filterParams.query = filterQuery + " +TAG:\"" + search.ISO9075Encode(filterData) + "\""; | ||||
|               break; | ||||
|  | ||||
|          case "category": | ||||
|             // Remove any trailing "/" character | ||||
| @@ -249,8 +249,15 @@ var Filters = | ||||
|             { | ||||
|                filterData = filterData.slice(0, -1); | ||||
|             } | ||||
|             filterQuery = this.constructPathQuery(parsedArgs); | ||||
|             filterParams.query = filterQuery + " +PATH:\"/cm:categoryRoot/cm:generalclassifiable" + Filters.iso9075EncodePath(filterData) + "/member\""; | ||||
|  | ||||
|             var categoryNodeRef = this.getCategoryNodeRef(filterData); | ||||
|  | ||||
|             if (categoryNodeRef && search.findNode(categoryNodeRef) != null) { | ||||
|                filterParams.query = filterQuery + ' +@cm\\:categories:"' + categoryNodeRef + '"'; | ||||
|             } else { | ||||
|                logger.warn("category filter: skipping invalid category node : " + categoryNodeRef); | ||||
|             } | ||||
|             filterParams.language = "fts-alfresco"; | ||||
|             break; | ||||
|  | ||||
|          case "aspect": | ||||
| @@ -271,11 +278,24 @@ var Filters = | ||||
|       { | ||||
|          filterParams.query += " " + (Filters.TYPE_MAP[parsedArgs.type] || ""); | ||||
|       } | ||||
|  | ||||
|       logger.warn("Final Query : " + filterParams.query); | ||||
|       return filterParams; | ||||
|    }, | ||||
|     | ||||
|    constructPathQuery: function constructPathQuery(parsedArgs) | ||||
|  | ||||
|     getCategoryNodeRef: function(categoryName) { | ||||
|         var results = search.luceneSearch( | ||||
|             'PATH:"/cm:categoryRoot/cm:generalclassifiable//*" AND @cm\\:name:"' + categoryName + '"' | ||||
|         ); | ||||
|  | ||||
|         if (results && results.length > 0) { | ||||
|             return results[0].nodeRef.toString(); | ||||
|         } | ||||
|  | ||||
|         logger.warn("Category not found: " + categoryName); | ||||
|         return null; | ||||
|     }, | ||||
|  | ||||
|    constructPathQuery: function(parsedArgs) | ||||
|    { | ||||
|       var pathQuery = ""; | ||||
|       if (parsedArgs.libraryRoot != companyhome || parsedArgs.nodeRef != "alfresco://company/home") | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-community-repo</artifactId> | ||||
|       <version>25.3.0.33-SNAPSHOT</version> | ||||
|       <version>25.3.0.51-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <dependencies> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
|   | ||||
| @@ -9,6 +9,6 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
| </project> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| # More infos about this image: https://github.com/Alfresco/alfresco-docker-base-tomcat | ||||
| FROM alfresco/alfresco-base-tomcat:tomcat10-jre21-rockylinux9@sha256:ed568167f4c28efc9db4c5bc44a882ee117c475463b526b21ada99e1b6d568dd | ||||
| FROM alfresco/alfresco-base-tomcat:tomcat10-jre17-rockylinux9@sha256:00d89fb84bda7bb37c17b0117adb2cfe4f7cbddcd6c1e42b0a67ea8dbb41a734 | ||||
| # Set default docker_context. | ||||
| ARG resource_path=target | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <organization> | ||||
| @@ -16,11 +16,11 @@ | ||||
|     </organization> | ||||
|  | ||||
|     <properties> | ||||
|         <maven.build.sourceVersion>21</maven.build.sourceVersion> | ||||
|         <maven.build.sourceVersion>17</maven.build.sourceVersion> | ||||
|         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||||
|         <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version> | ||||
|         <maven-release.version>2.5.3</maven-release.version> | ||||
|         <java.version>21</java.version> | ||||
|         <java.version>17</java.version> | ||||
|         <suiteXmlFile>${project.basedir}/src/test/resources/cmis-suite.xml</suiteXmlFile> | ||||
|         <cmis.binding /> | ||||
|         <cmis.basePath /> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -8,18 +8,18 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|         <suiteXmlFile>${project.basedir}/src/test/resources/restapi-suite.xml</suiteXmlFile> | ||||
|         <maven.build.sourceVersion>21</maven.build.sourceVersion> | ||||
|         <maven.build.sourceVersion>17</maven.build.sourceVersion> | ||||
|         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||||
|         <rest.api.explorer.branch>master</rest.api.explorer.branch> | ||||
|         <httpclient-osgi-version>4.5.6</httpclient-osgi-version> | ||||
|         <commons-lang3.version>3.18.0</commons-lang3.version> | ||||
|         <scribejava-apis.version>8.3.3</scribejava-apis.version> | ||||
|         <java.version>21</java.version> | ||||
|         <java.version>17</java.version> | ||||
|     </properties> | ||||
|  | ||||
|     <profiles> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
							
								
								
									
										30
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ | ||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
|     <artifactId>alfresco-community-repo</artifactId> | ||||
|     <version>25.3.0.33-SNAPSHOT</version> | ||||
|     <version>25.3.0.51-SNAPSHOT</version> | ||||
|     <packaging>pom</packaging> | ||||
|     <name>Alfresco Community Repo Parent</name> | ||||
|  | ||||
| @@ -38,7 +38,7 @@ | ||||
|         <builder.name>entitled-builder</builder.name> | ||||
|         <local.registry>127.0.0.1:5000</local.registry> | ||||
|  | ||||
|         <java.version>21</java.version> | ||||
|         <java.version>17</java.version> | ||||
|         <maven.compiler.source>${java.version}</maven.compiler.source> | ||||
|         <maven.compiler.target>${java.version}</maven.compiler.target> | ||||
|         <maven.build.sourceVersion>${java.version}</maven.build.sourceVersion> | ||||
| @@ -51,14 +51,14 @@ | ||||
|         <dependency.alfresco-server-root.version>7.0.2</dependency.alfresco-server-root.version> | ||||
|         <dependency.activiti-engine.version>5.23.0</dependency.activiti-engine.version> | ||||
|         <dependency.activiti.version>5.23.0</dependency.activiti.version> | ||||
|         <dependency.alfresco-transform-core.version>5.2.1</dependency.alfresco-transform-core.version> | ||||
|         <dependency.alfresco-transform-service.version>4.2.1</dependency.alfresco-transform-service.version> | ||||
|         <dependency.alfresco-transform-core.version>5.2.2-A.4</dependency.alfresco-transform-core.version> | ||||
|         <dependency.alfresco-transform-service.version>4.2.2-A.2</dependency.alfresco-transform-service.version> | ||||
|         <dependency.alfresco-greenmail.version>7.1</dependency.alfresco-greenmail.version> | ||||
|         <dependency.acs-event-model.version>1.0.5</dependency.acs-event-model.version> | ||||
|         <dependency.acs-event-model.version>1.0.11</dependency.acs-event-model.version> | ||||
|  | ||||
|         <dependency.aspectj.version>1.9.22.1</dependency.aspectj.version> | ||||
|         <dependency.spring.version>6.2.8</dependency.spring.version> | ||||
|         <dependency.spring-security.version>6.3.9</dependency.spring-security.version> | ||||
|         <dependency.spring.version>6.2.11</dependency.spring.version> | ||||
|         <dependency.spring-security.version>6.4.11</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.1.2</dependency.cxf.version> | ||||
| @@ -82,7 +82,7 @@ | ||||
|         <dependency.slf4j.version>2.0.16</dependency.slf4j.version> | ||||
|         <dependency.log4j.version>2.25.1</dependency.log4j.version> | ||||
|         <dependency.groovy.version>3.0.25</dependency.groovy.version> | ||||
|         <dependency.tika.version>2.9.2</dependency.tika.version> | ||||
|         <dependency.tika.version>3.2.3</dependency.tika.version> | ||||
|         <dependency.truezip.version>7.7.10</dependency.truezip.version> | ||||
|         <dependency.poi.version>5.4.0</dependency.poi.version> | ||||
|         <dependency.jboss.logging.version>3.5.0.Final</dependency.jboss.logging.version> | ||||
| @@ -170,6 +170,12 @@ | ||||
|  | ||||
|     <dependencyManagement> | ||||
|         <dependencies> | ||||
|             <!-- v1.10 has 0BSD license it must be consulted with Legal --> | ||||
|             <dependency> | ||||
|                 <groupId>org.tukaani</groupId> | ||||
|                 <artifactId>xz</artifactId> | ||||
|                 <version>1.9</version> | ||||
|             </dependency> | ||||
|             <!-- Jakarta... --> | ||||
|             <dependency> | ||||
|                 <groupId>jakarta.xml.bind</groupId> | ||||
| @@ -1125,16 +1131,10 @@ | ||||
|                                             <exclude>jakarta.xml.soap:jakarta.xml.soap-api:(, 2.0.1)</exclude> | ||||
|                                             <exclude>jakarta.jws:jakarta.jws-api:(, 3.0.0)</exclude> | ||||
| <!--                                            Enforce ban bouncycastle dependencies other than specified under <includes> section--> | ||||
|                                             <exclude>org.bouncycastle</exclude> | ||||
|                                             <exclude>org.bouncycastle:(,1.81)</exclude> | ||||
| <!--                                            Enforce one version of Jaxb--> | ||||
|                                             <exclude>com.sun.xml.bind</exclude> | ||||
|                                         </excludes> | ||||
|                                         <includes> | ||||
|                                             <include>org.bouncycastle:bcprov-jdk18on:[1.78.1,)</include> | ||||
|                                             <include>org.bouncycastle:bcmail-jdk18on:[1.78.1,)</include> | ||||
|                                             <include>org.bouncycastle:bcpkix-jdk18on:[1.78.1,)</include> | ||||
|                                             <include>org.bouncycastle:bcutil-jdk18on:[1.78.1,)</include> | ||||
|                                         </includes> | ||||
|                                     </bannedDependencies> | ||||
|                                 </rules> | ||||
|                                 <fail>true</fail> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
|   | ||||
| @@ -40,7 +40,6 @@ | ||||
| 		"items": | ||||
| 		[ | ||||
| 		<#list results as row> | ||||
| 			<#if row.item.hasPermission("Read")> | ||||
| 			{ | ||||
| 				"type": "${row.item.typeShort}", | ||||
| 				"parentType": "${row.item.parentTypeShort!""}", | ||||
| @@ -76,7 +75,6 @@ | ||||
| 				"nodeRef": "${row.item.nodeRef}"<#if row.selectable?exists>, | ||||
| 				"selectable" : ${row.selectable?string}</#if> | ||||
| 			}<#if row_has_next>,</#if> | ||||
| 			</#if> | ||||
| 		</#list> | ||||
| 		] | ||||
| 	} | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>25.3.0.33-SNAPSHOT</version> | ||||
|         <version>25.3.0.51-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
| @@ -840,12 +840,12 @@ | ||||
|                     </execution> | ||||
|                 </executions> | ||||
|                 <configuration> | ||||
|                     <complianceLevel>${java.version}</complianceLevel> | ||||
|                     <complianceLevel>17</complianceLevel> | ||||
|                     <outxml>false</outxml> | ||||
|                     <verbose>true</verbose> | ||||
|                     <showWeaveInfo>true</showWeaveInfo> | ||||
|                     <source>${java.version}</source> | ||||
|                     <target>${java.version}</target> | ||||
|                     <source>17</source> | ||||
|                     <target>17</target> | ||||
|                     <additionalCompilerArgs> | ||||
|                         <arg>-parameters</arg> | ||||
|                     </additionalCompilerArgs> | ||||
|   | ||||
| @@ -70,6 +70,13 @@ public interface AuditComponent | ||||
|      */ | ||||
|     public void setUserAuditFilter(UserAuditFilter userAuditFilter); | ||||
|  | ||||
|     /** | ||||
|      * @param auditRecordReporter | ||||
|      *            AuditRecordReporter | ||||
|      * @since 25.3 | ||||
|      */ | ||||
|     public void setAuditRecordReporter(AuditRecordReporter auditRecordReporter); | ||||
|  | ||||
|     /** | ||||
|      * Get all registered audit applications, whether active or not. | ||||
|      * | ||||
|   | ||||
| @@ -48,7 +48,6 @@ import org.alfresco.repo.audit.model.AuditModelRegistryImpl; | ||||
| import org.alfresco.repo.domain.audit.AuditDAO; | ||||
| import org.alfresco.repo.domain.propval.PropertyValueDAO; | ||||
| import org.alfresco.repo.security.authentication.AuthenticationUtil; | ||||
| import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; | ||||
| import org.alfresco.repo.transaction.AlfrescoTransactionSupport; | ||||
| import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; | ||||
| import org.alfresco.repo.transaction.RetryingTransactionHelper; | ||||
| @@ -73,8 +72,8 @@ public class AuditComponentImpl implements AuditComponent | ||||
| { | ||||
|     private static final String INBOUND_LOGGER = "org.alfresco.repo.audit.inbound"; | ||||
|  | ||||
|     private static Log logger = LogFactory.getLog(AuditComponentImpl.class); | ||||
|     private static Log loggerInbound = LogFactory.getLog(INBOUND_LOGGER); | ||||
|     private static final Log logger = LogFactory.getLog(AuditComponentImpl.class); | ||||
|     private static final Log loggerInbound = LogFactory.getLog(INBOUND_LOGGER); | ||||
|  | ||||
|     private AuditModelRegistryImpl auditModelRegistry; | ||||
|     private PropertyValueDAO propertyValueDAO; | ||||
| @@ -82,6 +81,7 @@ public class AuditComponentImpl implements AuditComponent | ||||
|     private TransactionService transactionService; | ||||
|     private AuditFilter auditFilter; | ||||
|     private UserAuditFilter userAuditFilter; | ||||
|     private AuditRecordReporter auditRecordReporter; | ||||
|  | ||||
|     /** | ||||
|      * Default constructor | ||||
| @@ -140,6 +140,11 @@ public class AuditComponentImpl implements AuditComponent | ||||
|         this.userAuditFilter = userAuditFilter; | ||||
|     } | ||||
|  | ||||
|     public void setAuditRecordReporter(AuditRecordReporter auditRecordReporter) | ||||
|     { | ||||
|         this.auditRecordReporter = auditRecordReporter; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * {@inheritDoc} | ||||
|      * | ||||
| @@ -215,7 +220,7 @@ public class AuditComponentImpl implements AuditComponent | ||||
|     public int deleteAuditEntries(List<Long> auditEntryIds) | ||||
|     { | ||||
|         // Shortcut, if necessary | ||||
|         if (auditEntryIds.size() == 0) | ||||
|         if (auditEntryIds.isEmpty()) | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
| @@ -234,7 +239,7 @@ public class AuditComponentImpl implements AuditComponent | ||||
|         { | ||||
|             Long disabledPathsId = application.getDisabledPathsId(); | ||||
|             Set<String> disabledPaths = (Set<String>) propertyValueDAO.getPropertyById(disabledPathsId); | ||||
|             return new HashSet<String>(disabledPaths); | ||||
|             return new HashSet<>(disabledPaths); | ||||
|         } | ||||
|         catch (Throwable e) | ||||
|         { | ||||
| @@ -254,6 +259,16 @@ public class AuditComponentImpl implements AuditComponent | ||||
|         return auditModelRegistry.isAuditEnabled(); | ||||
|     } | ||||
|  | ||||
|     public boolean isAuditingToDatabaseEnabled() | ||||
|     { | ||||
|         return auditModelRegistry.isAuditingToDatabaseEnabled(); | ||||
|     } | ||||
|  | ||||
|     public boolean isAuditingToAuditStorageEnabled() | ||||
|     { | ||||
|         return auditModelRegistry.isAuditingToAuditStorageEnabled(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * {@inheritDoc} | ||||
|      * | ||||
| @@ -309,7 +324,7 @@ public class AuditComponentImpl implements AuditComponent | ||||
|     { | ||||
|         PathMapper pathMapper = auditModelRegistry.getAuditPathMapper(); | ||||
|         Set<String> mappedPaths = pathMapper.getMappedPathsWithPartialMatch(path); | ||||
|         return loggerInbound.isDebugEnabled() || mappedPaths.size() > 0; | ||||
|         return loggerInbound.isDebugEnabled() || !mappedPaths.isEmpty(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -346,7 +361,7 @@ public class AuditComponentImpl implements AuditComponent | ||||
|  | ||||
|         // Check if there are any entries that match or supercede the given path | ||||
|         String disablingPath = null; | ||||
|         ; | ||||
|  | ||||
|         for (String disabledPath : disabledPaths) | ||||
|         { | ||||
|             if (path.startsWith(disabledPath)) | ||||
| @@ -573,7 +588,7 @@ public class AuditComponentImpl implements AuditComponent | ||||
|         } | ||||
|  | ||||
|         // Build the key paths using the session root path | ||||
|         Map<String, Serializable> pathedValues = new HashMap<String, Serializable>(values.size() * 2); | ||||
|         Map<String, Serializable> pathedValues = new HashMap<>(values.size() * 2); | ||||
|         for (Map.Entry<String, Serializable> entry : values.entrySet()) | ||||
|         { | ||||
|             String pathElement = entry.getKey(); | ||||
| @@ -596,12 +611,7 @@ public class AuditComponentImpl implements AuditComponent | ||||
|         case TXN_NONE: | ||||
|         case TXN_READ_ONLY: | ||||
|             // New transaction | ||||
|             RetryingTransactionCallback<Map<String, Serializable>> callback = new RetryingTransactionCallback<Map<String, Serializable>>() { | ||||
|                 public Map<String, Serializable> execute() throws Throwable | ||||
|                 { | ||||
|                     return recordAuditValuesImpl(mappedValues); | ||||
|                 } | ||||
|             }; | ||||
|             RetryingTransactionCallback<Map<String, Serializable>> callback = () -> recordAuditValuesImpl(mappedValues); | ||||
|             RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); | ||||
|             txnHelper.setForceWritable(true); | ||||
|             return txnHelper.doInTransaction(callback, false, true); | ||||
| @@ -618,21 +628,16 @@ public class AuditComponentImpl implements AuditComponent | ||||
|     public Map<String, Serializable> recordAuditValuesImpl(Map<String, Serializable> mappedValues) | ||||
|     { | ||||
|         // Group the values by root path | ||||
|         Map<String, Map<String, Serializable>> mappedValuesByRootKey = new HashMap<String, Map<String, Serializable>>(); | ||||
|         Map<String, Map<String, Serializable>> mappedValuesByRootKey = new HashMap<>(); | ||||
|         for (Map.Entry<String, Serializable> entry : mappedValues.entrySet()) | ||||
|         { | ||||
|             String path = entry.getKey(); | ||||
|             String rootKey = AuditApplication.getRootKey(path); | ||||
|             Map<String, Serializable> rootKeyMappedValues = mappedValuesByRootKey.get(rootKey); | ||||
|             if (rootKeyMappedValues == null) | ||||
|             { | ||||
|                 rootKeyMappedValues = new HashMap<String, Serializable>(7); | ||||
|                 mappedValuesByRootKey.put(rootKey, rootKeyMappedValues); | ||||
|             } | ||||
|             Map<String, Serializable> rootKeyMappedValues = mappedValuesByRootKey.computeIfAbsent(rootKey, k -> new HashMap<>(7)); | ||||
|             rootKeyMappedValues.put(path, entry.getValue()); | ||||
|         } | ||||
|  | ||||
|         Map<String, Serializable> allAuditedValues = new HashMap<String, Serializable>(mappedValues.size() * 2 + 1); | ||||
|         Map<String, Serializable> allAuditedValues = new HashMap<>(mappedValues.size() * 2 + 1); | ||||
|         // Now audit for each of the root keys | ||||
|         for (Map.Entry<String, Map<String, Serializable>> entry : mappedValuesByRootKey.entrySet()) | ||||
|         { | ||||
| @@ -694,7 +699,7 @@ public class AuditComponentImpl implements AuditComponent | ||||
|         } | ||||
|  | ||||
|         // Check if there is anything to audit | ||||
|         if (values.size() == 0) | ||||
|         if (values.isEmpty()) | ||||
|         { | ||||
|             if (logger.isDebugEnabled()) | ||||
|             { | ||||
| @@ -727,12 +732,7 @@ public class AuditComponentImpl implements AuditComponent | ||||
|         Map<String, Serializable> auditData = generateData(generators); | ||||
|  | ||||
|         // Now extract values | ||||
|         Map<String, Serializable> extractedData = AuthenticationUtil.runAs(new RunAsWork<Map<String, Serializable>>() { | ||||
|             public Map<String, Serializable> doWork() throws Exception | ||||
|             { | ||||
|                 return extractData(application, values); | ||||
|             } | ||||
|         }, AuthenticationUtil.getSystemUserName()); | ||||
|         Map<String, Serializable> extractedData = AuthenticationUtil.runAs(() -> extractData(application, values), AuthenticationUtil.getSystemUserName()); | ||||
|  | ||||
|         // Combine extracted and generated values (extracted data takes precedence) | ||||
|         auditData.putAll(extractedData); | ||||
| @@ -743,8 +743,8 @@ public class AuditComponentImpl implements AuditComponent | ||||
|         { | ||||
|             String root = value.getKey(); | ||||
|             int index = root.lastIndexOf("/"); | ||||
|             Map<String, Serializable> argc = new HashMap<String, Serializable>(1); | ||||
|             argc.put(root.substring(index, root.length()).substring(1), value.getValue()); | ||||
|             Map<String, Serializable> argc = new HashMap<>(1); | ||||
|             argc.put(root.substring(index).substring(1), value.getValue()); | ||||
|             if (!auditFilter.accept(root.substring(0, index), argc)) | ||||
|             { | ||||
|                 return Collections.emptyMap(); | ||||
| @@ -760,10 +760,15 @@ public class AuditComponentImpl implements AuditComponent | ||||
|         { | ||||
|             // Persist the values (if not just gathering data in a pre call for use in a post call) | ||||
|             boolean justGatherPreCallData = application.isApplicationJustGeneratingPreCallData(); | ||||
|             if (!justGatherPreCallData) | ||||
|             if (!justGatherPreCallData && isAuditingToDatabaseEnabled()) | ||||
|             { | ||||
|                 entryId = auditDAO.createAuditEntry(applicationId, time, username, auditData); | ||||
|             } | ||||
|             if (isAuditingToAuditStorageEnabled()) | ||||
|             { | ||||
|                 auditRecordReporter.reportAuditRecord(createAuditRecord(auditData, true, username, entryId, application.getApplicationName())); | ||||
|             } | ||||
|  | ||||
|             // Done | ||||
|             if (logger.isDebugEnabled()) | ||||
|             { | ||||
| @@ -822,7 +827,7 @@ public class AuditComponentImpl implements AuditComponent | ||||
|             AuditApplication application, | ||||
|             Map<String, Serializable> values) | ||||
|     { | ||||
|         Map<String, Serializable> newData = new HashMap<String, Serializable>(values.size()); | ||||
|         Map<String, Serializable> newData = new HashMap<>(values.size()); | ||||
|  | ||||
|         List<DataExtractorDefinition> extractors = application.getDataExtractors(); | ||||
|         for (DataExtractorDefinition extractorDef : extractors) | ||||
| @@ -900,7 +905,7 @@ public class AuditComponentImpl implements AuditComponent | ||||
|      */ | ||||
|     private Map<String, Serializable> generateData(Map<String, DataGenerator> generators) | ||||
|     { | ||||
|         Map<String, Serializable> newData = new HashMap<String, Serializable>(generators.size() + 5); | ||||
|         Map<String, Serializable> newData = new HashMap<>(generators.size() + 5); | ||||
|         for (Map.Entry<String, DataGenerator> entry : generators.entrySet()) | ||||
|         { | ||||
|             String path = entry.getKey(); | ||||
| @@ -925,6 +930,20 @@ public class AuditComponentImpl implements AuditComponent | ||||
|         return newData; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates an AuditRecord from the provided audit data. | ||||
|      */ | ||||
|     private AuditRecord createAuditRecord(Map<String, Serializable> auditData, boolean inTransaction, String username, Long entryId, String applicationName) | ||||
|     { | ||||
|         int rootSize = applicationName.length() + 2; // Root is constructed like this -> '/' + auditedApplicationName + '/'. | ||||
|         AuditRecord.Builder builder = AuditRecordUtils.generateAuditRecordBuilder(auditData, rootSize); | ||||
|         builder.setAuditRecordType(applicationName); | ||||
|         builder.setInTransaction(inTransaction); | ||||
|         builder.setUsername(username); | ||||
|         builder.setEntryDBId(entryId); | ||||
|         return builder.build(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * {@inheritDoc} | ||||
|      */ | ||||
|   | ||||
| @@ -0,0 +1,130 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 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.audit; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.time.ZonedDateTime; | ||||
| import java.util.Map; | ||||
|  | ||||
| public class AuditRecord | ||||
| { | ||||
|     private final boolean inTransaction; | ||||
|     private final String auditApplicationId; | ||||
|     private final ZonedDateTime createdAt; | ||||
|     private final String username; | ||||
|     private final Long entryDBId; | ||||
|     private final Map<String, Serializable> auditData; | ||||
|  | ||||
|     public AuditRecord(Builder builder) | ||||
|     { | ||||
|         this.auditApplicationId = builder.auditRecordType; | ||||
|         this.inTransaction = builder.inTransaction; | ||||
|         this.auditData = builder.auditRecordData; | ||||
|         this.createdAt = ZonedDateTime.now(); | ||||
|         this.username = builder.username; | ||||
|         this.entryDBId = builder.entryDBId; | ||||
|     } | ||||
|  | ||||
|     public String getAuditApplicationId() | ||||
|     { | ||||
|         return auditApplicationId; | ||||
|     } | ||||
|  | ||||
|     public boolean isInTransaction() | ||||
|     { | ||||
|         return inTransaction; | ||||
|     } | ||||
|  | ||||
|     public ZonedDateTime getCreatedAt() | ||||
|     { | ||||
|         return createdAt; | ||||
|     } | ||||
|  | ||||
|     public String getUsername() | ||||
|     { | ||||
|         return username; | ||||
|     } | ||||
|  | ||||
|     public Long getEntryDBId() | ||||
|     { | ||||
|         return entryDBId; | ||||
|     } | ||||
|  | ||||
|     public Map<String, Serializable> getAuditData() | ||||
|     { | ||||
|         return auditData; | ||||
|     } | ||||
|  | ||||
|     public static Builder builder() | ||||
|     { | ||||
|         return new Builder(); | ||||
|     } | ||||
|  | ||||
|     public static class Builder | ||||
|     { | ||||
|         private String auditRecordType; | ||||
|         private boolean inTransaction; | ||||
|         private Map<String, Serializable> auditRecordData; | ||||
|         private String username; | ||||
|         private Long entryDBId; | ||||
|  | ||||
|         public Builder setAuditRecordType(String auditRecordType) | ||||
|         { | ||||
|             this.auditRecordType = auditRecordType; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder setInTransaction(boolean inTransaction) | ||||
|         { | ||||
|             this.inTransaction = inTransaction; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder setAuditRecordData(Map<String, Serializable> auditRecordData) | ||||
|         { | ||||
|             this.auditRecordData = auditRecordData; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder setUsername(String username) | ||||
|         { | ||||
|             this.username = username; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder setEntryDBId(Long entryDBId) | ||||
|         { | ||||
|             this.entryDBId = entryDBId; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public AuditRecord build() | ||||
|         { | ||||
|             return new AuditRecord(this); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,37 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 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.audit; | ||||
|  | ||||
| public interface AuditRecordReporter | ||||
| { | ||||
|     /** | ||||
|      * This method will report AuditRecord to Audit Storage using RepoEvent2 | ||||
|      *  | ||||
|      * @param auditRecord | ||||
|      *            represent data that will be reported. | ||||
|      */ | ||||
|     void reportAuditRecord(AuditRecord auditRecord); | ||||
| } | ||||
| @@ -0,0 +1,40 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 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.audit; | ||||
|  | ||||
| public class AuditRecordReporterImpl implements AuditRecordReporter | ||||
| { | ||||
|     /** | ||||
|      * This method intentionally has an empty implementation. | ||||
|      * <p> | ||||
|      * This class provides a no-op implementation of {@link AuditRecordReporter}. | ||||
|      */ | ||||
|     @Override | ||||
|     public void reportAuditRecord(AuditRecord auditRecord) | ||||
|     { | ||||
|         // No operation performed. | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,104 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 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.audit; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
|  | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
|  | ||||
| public final class AuditRecordUtils | ||||
| { | ||||
|     private AuditRecordUtils() | ||||
|     { | ||||
|         // This is a utility class and cannot be instantiated. | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generates an {@link AuditRecord.Builder} from flat audit data. | ||||
|      * <p> | ||||
|      * This method: | ||||
|      * <ul> | ||||
|      * <li>Translates flat {@code key-value} pairs into a nested JSON structure.</li> | ||||
|      * <li>Preloads the builder with the provided arguments.</li> | ||||
|      * <li>Splits keys by {@code /} to build the nested structure.</li> | ||||
|      * <li>Uses the root key as the application ID.</li> | ||||
|      * <li>Assumes each key starts with the same root, constructed as {@code '/' + auditedApplicationName + '/'}, which is removed before splitting.</li> | ||||
|      * </ul> | ||||
|      * | ||||
|      * @param data | ||||
|      *            a map containing flat audit data as `key-value` pairs | ||||
|      * @param keyRootLength | ||||
|      *            is a length of key root. | ||||
|      * @return a preloaded {@link AuditRecord.Builder} | ||||
|      */ | ||||
|     public static AuditRecord.Builder generateAuditRecordBuilder(Map<String, Serializable> data, int keyRootLength) | ||||
|     { | ||||
|         var auditRecordBuilder = AuditRecord.builder(); | ||||
|  | ||||
|         var rootNode = createRootNode(data, keyRootLength); | ||||
|  | ||||
|         auditRecordBuilder.setAuditRecordData(rootNode); | ||||
|  | ||||
|         return auditRecordBuilder; | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     private static HashMap<String, Serializable> createRootNode(Map<String, Serializable> data, int keyRootLength) | ||||
|     { | ||||
|         var rootNode = new HashMap<String, Serializable>(); | ||||
|  | ||||
|         data.forEach((k, v) -> { | ||||
|             var keys = k.substring(keyRootLength).split("/"); | ||||
|  | ||||
|             var current = rootNode; | ||||
|             for (int i = 0; i < keys.length - 1; i++) | ||||
|             { | ||||
|                 current = (HashMap<String, Serializable>) current.computeIfAbsent(keys[i], newMap -> new HashMap<String, Serializable>()); | ||||
|             } | ||||
|             current.put(keys[keys.length - 1], decodeValueByInstance(v)); | ||||
|         }); | ||||
|         return rootNode; | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     private static Serializable decodeValueByInstance(Serializable value) | ||||
|     { | ||||
|         if (value instanceof HashMap<?, ?>) | ||||
|         { | ||||
|             return createRootNode((HashMap<String, Serializable>) value, 0); | ||||
|         } | ||||
|         else if (value instanceof NodeRef) | ||||
|         { | ||||
|             return ((NodeRef) value).getId(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return value; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -58,6 +58,21 @@ public interface AuditModelRegistry | ||||
|      */ | ||||
|     public boolean isAuditEnabled(); | ||||
|  | ||||
|     /** | ||||
|      * Determines whether audit values should be stored in database. <code>True</code> by default if not changed by property. | ||||
|      * | ||||
|      * @return <code>true</code> if audit is enabled. | ||||
|      */ | ||||
|     boolean isAuditingToDatabaseEnabled(); | ||||
|  | ||||
|     /** | ||||
|      * Determines whether audit values should be stored in audit storage. | ||||
|      *  | ||||
|      * @return <code>true</code> if auditing to Audit Storage is enabled. | ||||
|      * | ||||
|      */ | ||||
|     boolean isAuditingToAuditStorageEnabled(); | ||||
|  | ||||
|     /** | ||||
|      * Get a map of all audit applications key by name | ||||
|      *  | ||||
|   | ||||
| @@ -85,6 +85,9 @@ public class AuditModelRegistryImpl extends AbstractPropertyBackedBean implement | ||||
| { | ||||
|     /** The name of the global enablement property. */ | ||||
|     public static final String PROPERTY_AUDIT_ENABLED = "audit.enabled"; | ||||
|  | ||||
|     private static final String AUDITING_TO_DATABASE = ".auditingToDatabase"; | ||||
|     private static final String AUDITING_TO_AUDIT_STORAGE = ".auditingToAuditStorage"; | ||||
|     /** The name of the strict loading flag. */ | ||||
|     public static final String PROPERTY_AUDIT_CONFIG_STRICT = "audit.config.strict"; | ||||
|     /** The XSD classpath location. */ | ||||
| @@ -249,6 +252,26 @@ public class AuditModelRegistryImpl extends AbstractPropertyBackedBean implement | ||||
|         return value != null && value.equalsIgnoreCase("true"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * {@inheritDoc} | ||||
|      */ | ||||
|     @Override | ||||
|     public boolean isAuditingToDatabaseEnabled() | ||||
|     { | ||||
|         String value = getProperty(AUDIT_PROPERTY_AUDIT_ENABLED + AUDITING_TO_DATABASE); | ||||
|         return value == null || value.equalsIgnoreCase("true"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * {@inheritDoc} | ||||
|      */ | ||||
|     @Override | ||||
|     public boolean isAuditingToAuditStorageEnabled() | ||||
|     { | ||||
|         String value = getProperty(AUDIT_PROPERTY_AUDIT_ENABLED + AUDITING_TO_AUDIT_STORAGE); | ||||
|         return value != null && value.equalsIgnoreCase("true"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Enables audit and registers an audit model at a given URL. Does not register across the cluster and should only be used for unit test purposes. | ||||
|      *  | ||||
| @@ -296,6 +319,8 @@ public class AuditModelRegistryImpl extends AbstractPropertyBackedBean implement | ||||
|  | ||||
|             // Default value for global enabled property | ||||
|             properties.put(AUDIT_PROPERTY_AUDIT_ENABLED, false); | ||||
|             properties.put(AUDIT_PROPERTY_AUDIT_ENABLED + AUDITING_TO_DATABASE, true); | ||||
|             properties.put(AUDIT_PROPERTY_AUDIT_ENABLED + AUDITING_TO_AUDIT_STORAGE, false); | ||||
|  | ||||
|             // Let's search for config files in the appropriate places. The individual applications they contain can still | ||||
|             // be enabled/disabled by the bean properties | ||||
|   | ||||
| @@ -2066,7 +2066,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO | ||||
|  | ||||
|         Node node = getNodeNotNull(nodeId, false); | ||||
|         // Handle sys:referenceable | ||||
|         ReferenceablePropertiesEntity.addReferenceableProperties(node, props); | ||||
|         ReferenceablePropertiesEntity.addReferenceableProperties(node.getId(), node.getNodeRef(), props); | ||||
|         // Handle sys:localized | ||||
|         LocalizedPropertiesEntity.addLocalizedProperties(localeDAO, node, props); | ||||
|         // Handle cm:auditable | ||||
|   | ||||
| @@ -86,10 +86,8 @@ public class ReferenceablePropertiesEntity | ||||
|     /** | ||||
|      * Adds all {@link ContentModel#ASPECT_REFERENCEABLE referencable} properties. | ||||
|      */ | ||||
|     public static void addReferenceableProperties(Node node, Map<QName, Serializable> properties) | ||||
|     public static void addReferenceableProperties(Long nodeId, NodeRef nodeRef, Map<QName, Serializable> properties) | ||||
|     { | ||||
|         Long nodeId = node.getId(); | ||||
|         NodeRef nodeRef = node.getNodeRef(); | ||||
|         properties.put(ContentModel.PROP_STORE_PROTOCOL, nodeRef.getStoreRef().getProtocol()); | ||||
|         properties.put(ContentModel.PROP_STORE_IDENTIFIER, nodeRef.getStoreRef().getIdentifier()); | ||||
|         properties.put(ContentModel.PROP_NODE_UUID, nodeRef.getId()); | ||||
|   | ||||
| @@ -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 | ||||
| @@ -97,19 +97,26 @@ public abstract class EventConsolidator<REF extends EntityRef, RES extends Resou | ||||
|      * @return the {@link RepoEvent} instance | ||||
|      */ | ||||
|     public RepoEvent<DataAttributes<RES>> getRepoEvent(EventInfo eventInfo) | ||||
|     { | ||||
|         final RepoEvent.Builder<DataAttributes<RES>> builder = RepoEvent.builder(); | ||||
|  | ||||
|         configureRepoEventBuilder(builder, eventInfo); | ||||
|  | ||||
|         return builder.build(); | ||||
|     } | ||||
|  | ||||
|     protected void configureRepoEventBuilder(RepoEvent.Builder<DataAttributes<RES>> builder, EventInfo eventInfo) | ||||
|     { | ||||
|         EventType eventType = getDerivedEvent(); | ||||
|  | ||||
|         DataAttributes<RES> eventData = buildEventData(eventInfo, resource, eventType); | ||||
|  | ||||
|         return RepoEvent.<DataAttributes<RES>> builder() | ||||
|                 .setId(eventInfo.getId()) | ||||
|         builder.setId(eventInfo.getId()) | ||||
|                 .setSource(eventInfo.getSource()) | ||||
|                 .setTime(eventInfo.getTimestamp()) | ||||
|                 .setType(eventType.getType()) | ||||
|                 .setData(eventData) | ||||
|                 .setDataschema(EventJSONSchema.getSchemaV1(eventType)) | ||||
|                 .build(); | ||||
|                 .setDataschema(EventJSONSchema.getSchemaV1(eventType)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -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 | ||||
| @@ -38,7 +38,7 @@ import org.alfresco.util.Pair; | ||||
|  */ | ||||
| public enum EventJSONSchema | ||||
| { | ||||
|     NODE_CREATED_V1("nodeCreated", 1, EventType.NODE_CREATED), NODE_UPDATED_V1("nodeUpdated", 1, EventType.NODE_UPDATED), NODE_DELETED_V1("nodeDeleted", 1, EventType.NODE_DELETED), CHILD_ASSOC_CREATED_V1("childAssocCreated", 1, EventType.CHILD_ASSOC_CREATED), CHILD_ASSOC_DELETED_V1("childAssocDeleted", 1, EventType.CHILD_ASSOC_DELETED), PEER_ASSOC_CREATED_V1("peerAssocCreated", 1, EventType.PEER_ASSOC_CREATED), PEER_ASSOC_DELETED_V1("peerAssocDeleted", 1, EventType.PEER_ASSOC_DELETED), PERMISSION_UPDATED_V1("permissionUpdated", 1, EventType.PERMISSION_UPDATED); | ||||
|     NODE_CREATED_V1("nodeCreated", 1, EventType.NODE_CREATED), NODE_UPDATED_V1("nodeUpdated", 1, EventType.NODE_UPDATED), NODE_DELETED_V1("nodeDeleted", 1, EventType.NODE_DELETED), CHILD_ASSOC_CREATED_V1("childAssocCreated", 1, EventType.CHILD_ASSOC_CREATED), CHILD_ASSOC_DELETED_V1("childAssocDeleted", 1, EventType.CHILD_ASSOC_DELETED), PEER_ASSOC_CREATED_V1("peerAssocCreated", 1, EventType.PEER_ASSOC_CREATED), PEER_ASSOC_DELETED_V1("peerAssocDeleted", 1, EventType.PEER_ASSOC_DELETED), PERMISSION_UPDATED_V1("permissionUpdated", 1, EventType.PERMISSION_UPDATED), AUDIT_ENTRY_CREATED_V1("auditEntryCreated", 1, EventType.AUDIT_ENTRY_CREATED); | ||||
|  | ||||
|     private static final String PREFIX = "https://api.alfresco.com/schema/event/repo/v"; | ||||
|  | ||||
|   | ||||
| @@ -28,8 +28,10 @@ package org.alfresco.repo.node.getchildren; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| import org.alfresco.repo.domain.node.NodeEntity; | ||||
| import org.alfresco.repo.domain.node.AuditablePropertiesEntity; | ||||
| import org.alfresco.repo.domain.node.NodePropertyEntity; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.StoreRef; | ||||
|  | ||||
| /** | ||||
|  * Filterable/Sortable Node Entity | ||||
| @@ -42,12 +44,17 @@ import org.alfresco.repo.domain.node.NodePropertyEntity; | ||||
| public class FilterSortNodeEntity | ||||
| { | ||||
|     private Long id; // node id | ||||
|     private String nodeUuid; | ||||
|     private Long typeQNameId; | ||||
|  | ||||
|     private NodeEntity node; | ||||
|     private AuditablePropertiesEntity auditablePropertiesEntity; | ||||
|     private NodePropertyEntity prop1; | ||||
|     private NodePropertyEntity prop2; | ||||
|     private NodePropertyEntity prop3; | ||||
|  | ||||
|     private String storeProtocol; | ||||
|     private String storeIdentifier; | ||||
|  | ||||
|     // Supplemental query-related parameters | ||||
|     private Long parentNodeId; | ||||
|     private Long prop1qnameId; | ||||
| @@ -80,6 +87,26 @@ public class FilterSortNodeEntity | ||||
|         this.id = id; | ||||
|     } | ||||
|  | ||||
|     public String getNodeUuid() | ||||
|     { | ||||
|         return nodeUuid; | ||||
|     } | ||||
|  | ||||
|     public void setNodeUuid(String nodeUuid) | ||||
|     { | ||||
|         this.nodeUuid = nodeUuid; | ||||
|     } | ||||
|  | ||||
|     public Long getTypeQNameId() | ||||
|     { | ||||
|         return typeQNameId; | ||||
|     } | ||||
|  | ||||
|     public void setTypeQNameId(Long typeQNameId) | ||||
|     { | ||||
|         this.typeQNameId = typeQNameId; | ||||
|     } | ||||
|  | ||||
|     public String getPattern() | ||||
|     { | ||||
|         return pattern; | ||||
| @@ -136,6 +163,16 @@ public class FilterSortNodeEntity | ||||
|         this.namePropertyQNameId = namePropertyQNameId; | ||||
|     } | ||||
|  | ||||
|     public AuditablePropertiesEntity getAuditablePropertiesEntity() | ||||
|     { | ||||
|         return auditablePropertiesEntity; | ||||
|     } | ||||
|  | ||||
|     public void setAuditablePropertiesEntity(AuditablePropertiesEntity auditablePropertiesEntity) | ||||
|     { | ||||
|         this.auditablePropertiesEntity = auditablePropertiesEntity; | ||||
|     } | ||||
|  | ||||
|     public NodePropertyEntity getProp1() | ||||
|     { | ||||
|         return prop1; | ||||
| @@ -166,14 +203,24 @@ public class FilterSortNodeEntity | ||||
|         this.prop3 = prop3; | ||||
|     } | ||||
|  | ||||
|     public NodeEntity getNode() | ||||
|     public String getStoreProtocol() | ||||
|     { | ||||
|         return node; | ||||
|         return storeProtocol; | ||||
|     } | ||||
|  | ||||
|     public void setNode(NodeEntity childNode) | ||||
|     public void setStoreProtocol(String storeProtocol) | ||||
|     { | ||||
|         this.node = childNode; | ||||
|         this.storeProtocol = storeProtocol; | ||||
|     } | ||||
|  | ||||
|     public String getStoreIdentifier() | ||||
|     { | ||||
|         return storeIdentifier; | ||||
|     } | ||||
|  | ||||
|     public void setStoreIdentifier(String storeIdentifier) | ||||
|     { | ||||
|         this.storeIdentifier = storeIdentifier; | ||||
|     } | ||||
|  | ||||
|     // Supplemental query-related parameters | ||||
| @@ -257,4 +304,9 @@ public class FilterSortNodeEntity | ||||
|     { | ||||
|         this.isPrimary = isPrimary; | ||||
|     } | ||||
|  | ||||
|     public NodeRef createNodeRef() | ||||
|     { | ||||
|         return new NodeRef(new StoreRef(storeProtocol, storeIdentifier), nodeUuid); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -48,7 +48,6 @@ import org.alfresco.query.CannedQueryParameters; | ||||
| import org.alfresco.query.CannedQuerySortDetails; | ||||
| import org.alfresco.query.CannedQuerySortDetails.SortOrder; | ||||
| import org.alfresco.repo.domain.node.AuditablePropertiesEntity; | ||||
| import org.alfresco.repo.domain.node.Node; | ||||
| import org.alfresco.repo.domain.node.NodeDAO; | ||||
| import org.alfresco.repo.domain.node.NodeEntity; | ||||
| import org.alfresco.repo.domain.node.NodePropertyEntity; | ||||
| @@ -775,7 +774,8 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR | ||||
|             if (results.size() >= BATCH_SIZE) | ||||
|             { | ||||
|                 // batch | ||||
|                 preloadFilterSort(); | ||||
|                 preloadNodes(); | ||||
|                 filterSort(); | ||||
|             } | ||||
|  | ||||
|             results.add(result); | ||||
| @@ -788,24 +788,27 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR | ||||
|             if (results.size() >= 0) | ||||
|             { | ||||
|                 // finish batch | ||||
|                 preloadFilterSort(); | ||||
|                 preloadNodes(); | ||||
|                 filterSort(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private void preloadFilterSort() | ||||
|         private void preloadNodes() | ||||
|         { | ||||
|             List<NodeRef> nodeRefs = new ArrayList<>(results.size()); | ||||
|             for (FilterSortNodeEntity result : results) | ||||
|             { | ||||
|                 nodeRefs.add(result.getNode().getNodeRef()); | ||||
|                 nodeRefs.add(result.createNodeRef()); | ||||
|             } | ||||
|  | ||||
|             preload(nodeRefs); | ||||
|         } | ||||
|  | ||||
|         private void filterSort() | ||||
|         { | ||||
|             for (FilterSortNodeEntity result : results) | ||||
|             { | ||||
|                 Node node = result.getNode(); | ||||
|                 NodeRef nodeRef = node.getNodeRef(); | ||||
|                 NodeRef nodeRef = result.createNodeRef(); | ||||
|  | ||||
|                 Map<NodePropertyKey, NodePropertyValue> propertyValues = new HashMap<NodePropertyKey, NodePropertyValue>(3); | ||||
|  | ||||
| @@ -830,7 +833,7 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR | ||||
|                 Map<QName, Serializable> propVals = nodePropertyHelper.convertToPublicProperties(propertyValues); | ||||
|  | ||||
|                 // Add referenceable / spoofed properties (including spoofed name if null) | ||||
|                 ReferenceablePropertiesEntity.addReferenceableProperties(node, propVals); | ||||
|                 ReferenceablePropertiesEntity.addReferenceableProperties(result.getId(), nodeRef, propVals); | ||||
|  | ||||
|                 // special cases | ||||
|  | ||||
| @@ -852,7 +855,7 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR | ||||
|                 } | ||||
|  | ||||
|                 // Auditable props (eg. cm:creator, cm:created, cm:modifier, cm:modified, ...) | ||||
|                 AuditablePropertiesEntity auditableProps = node.getAuditableProperties(); | ||||
|                 AuditablePropertiesEntity auditableProps = result.getAuditablePropertiesEntity(); | ||||
|                 if (auditableProps != null) | ||||
|                 { | ||||
|                     for (Map.Entry<QName, Serializable> entry : auditableProps.getAuditableProperties().entrySet()) | ||||
| @@ -862,7 +865,7 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR | ||||
|                 } | ||||
|  | ||||
|                 // Node type | ||||
|                 Long nodeTypeQNameId = node.getTypeQNameId(); | ||||
|                 Long nodeTypeQNameId = result.getTypeQNameId(); | ||||
|                 if (nodeTypeQNameId != null) | ||||
|                 { | ||||
|                     Pair<Long, QName> pair = qnameDAO.getQName(nodeTypeQNameId); | ||||
|   | ||||
| @@ -37,8 +37,9 @@ | ||||
|                 <property name="properties" ref="global-properties" /> | ||||
|             </bean> | ||||
|         </property> | ||||
|         <property name="auditRecordReporter" ref="auditRecordReporter"/> | ||||
|     </bean> | ||||
|      | ||||
|  | ||||
|     <!-- User Audit Filter --> | ||||
|      | ||||
|     <bean id="userAuditFilter" class="org.alfresco.repo.audit.UserAuditFilter"> | ||||
| @@ -108,5 +109,8 @@ | ||||
|     | ||||
|     <!-- Reference in the audit registry managed bean --> | ||||
|     <alias name="Audit" alias="auditModel.modelRegistry"/> | ||||
|        | ||||
| </beans> | ||||
|  | ||||
|     <!-- Audit Record Reported --> | ||||
|     <bean id="auditRecordReporter" class="org.alfresco.repo.audit.AuditRecordReporterImpl"/> | ||||
|  | ||||
| </beans> | ||||
|   | ||||
| @@ -133,7 +133,15 @@ | ||||
|     <resultMap id="result_FilterSortNode" type="FilterSortNode"> | ||||
|          | ||||
|         <id property="id" column="id" jdbcType="BIGINT" javaType="java.lang.Long"/> | ||||
|          | ||||
|         <result property="nodeUuid" column="uuid" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|         <result property="typeQNameId" column="type_qname_id" jdbcType="BIGINT" javaType="java.lang.Long"/> | ||||
|  | ||||
|         <result property="auditablePropertiesEntity.auditCreator" column="audit_creator" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|         <result property="auditablePropertiesEntity.auditCreated" column="audit_created" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|         <result property="auditablePropertiesEntity.auditModifier" column="audit_modifier" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|         <result property="auditablePropertiesEntity.auditModified" column="audit_modified" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|         <result property="auditablePropertiesEntity.auditAccessed" column="audit_accessed" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|  | ||||
|         <result property="prop1.nodeId" column="prop1_node_id" jdbcType="BIGINT" javaType="java.lang.Long"/> | ||||
|         <result property="prop1.key.qnameId" column="prop1_qname_id" jdbcType="BIGINT" javaType="java.lang.Long"/> | ||||
|         <result property="prop1.key.localeId" column="prop1_locale_id" jdbcType="BIGINT" javaType="java.lang.Long"/> | ||||
| @@ -169,9 +177,9 @@ | ||||
|         <result property="prop3.value.floatValue" column="prop3_float_value" jdbcType="FLOAT" javaType="java.lang.Float"/> | ||||
|         <result property="prop3.value.doubleValue" column="prop3_double_value" jdbcType="FLOAT" javaType="java.lang.Double"/> | ||||
|         <result property="prop3.value.stringValue" column="prop3_string_value" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|          | ||||
|         <association property="node" resultMap="alfresco.node.result_Node"/> | ||||
|          | ||||
|  | ||||
|         <result property="storeProtocol" column="protocol" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|         <result property="storeIdentifier" column="identifier" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|     </resultMap> | ||||
|      | ||||
|     <resultMap id="result_ArchivedNodes" type="ArchivedNodes"> | ||||
| @@ -972,8 +980,8 @@ | ||||
|     </select> | ||||
|  | ||||
|     <!-- GetChildren - with explicit prop filtering and/or sorting --> | ||||
|     <select id="select_GetChildrenCannedQueryWithProps" parameterType="FilterSortNode" resultMap="result_FilterSortNode"> | ||||
|        select | ||||
|     <select id="select_GetChildrenCannedQueryWithProps" parameterType="FilterSortNode" resultMap="result_FilterSortNode" flushCache="true"> | ||||
|        select distinct | ||||
|             childNode.id             as id, | ||||
|             childNode.version        as version, | ||||
|             childStore.id            as store_id, | ||||
| @@ -989,7 +997,7 @@ | ||||
|             childNode.audit_created  as audit_created, | ||||
|             childNode.audit_modifier as audit_modifier, | ||||
|             childNode.audit_modified as audit_modified, | ||||
|             childNode.audit_accessed  as audit_accessed | ||||
|             childNode.audit_accessed as audit_accessed | ||||
|             <if test="prop1qnameId != null"> | ||||
|           , prop1.node_id            as prop1_node_id, | ||||
|             prop1.qname_id           as prop1_qname_id, | ||||
| @@ -1067,9 +1075,6 @@ | ||||
|                     #{item} | ||||
|                 </foreach> | ||||
|             </if> | ||||
|         <if test="prop1qnameId == null and auditableProps == false"> | ||||
|             <include refid="alfresco.node.select_ChildAssoc_OrderBy"/> | ||||
|         </if> | ||||
|     </select> | ||||
|      | ||||
|     <!-- GetChildren - with no explicit sorting (or prop filtering) - note: still filtered by child type (and optionally primary or secondary) --> | ||||
|   | ||||
| @@ -286,6 +286,8 @@ audit.alfresco-access.enabled=false | ||||
| audit.alfresco-access.sub-actions.enabled=false | ||||
| audit.cmischangelog.enabled=false | ||||
| audit.dod5015.enabled=false | ||||
| audit.enabled.auditingToAuditStorage=false | ||||
| audit.enabled.auditingToDatabase=true | ||||
| # Setting this flag to true will force startup failure when invalid audit configurations are detected | ||||
| audit.config.strict=false | ||||
| # Audit map filter for AccessAuditor - restricts recorded events to user driven events  | ||||
|   | ||||
| @@ -48,6 +48,7 @@ import org.alfresco.util.testing.category.NonBuildTests; | ||||
|         org.alfresco.repo.audit.UserAuditFilterTest.class, | ||||
|         org.alfresco.repo.audit.AuditMethodInterceptorTest.class, | ||||
|         org.alfresco.repo.audit.access.AccessAuditorTest.class, | ||||
|         org.alfresco.repo.audit.AuditRecordUtilsTest.class, | ||||
|  | ||||
|         // the following test will lock up the DB if run in the applicationContext_01 test suite | ||||
|         org.alfresco.repo.activities.feed.FeedNotifierTest.class, | ||||
|   | ||||
| @@ -877,6 +877,7 @@ public class AuditComponentTest extends TestCase | ||||
|         auditModelRegistry.loadAuditModels(); | ||||
|  | ||||
|         auditModelRegistry.setProperty("audit.enabled", "true"); | ||||
|         auditModelRegistry.setProperty("audit.enabled.auditingToDatabase", "true"); | ||||
|  | ||||
|         auditModelRegistry.setProperty("audit.app1.enabled", "true"); | ||||
|         auditModelRegistry.setProperty("audit.filter.app1.default.enabled", "true"); | ||||
|   | ||||
| @@ -0,0 +1,123 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 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.audit; | ||||
|  | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.Assert.assertNotNull; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.time.Instant; | ||||
| import java.util.Date; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
|  | ||||
| import org.junit.Test; | ||||
|  | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.namespace.QName; | ||||
|  | ||||
| public class AuditRecordUtilsTest | ||||
| { | ||||
|     @SuppressWarnings("unchecked") | ||||
|     @Test | ||||
|     public void testGenerateAuditRecordBuilderTest() | ||||
|     { | ||||
|         var testData = new HashMap<String, Serializable>(); | ||||
|  | ||||
|         testData.put("/alfresco-access/transaction/path", "/app:company_home"); | ||||
|         testData.put("/alfresco-access/transaction/user", "admin"); | ||||
|         testData.put("/alfresco-access/transaction/sub-actions", "updateNodeProperties"); | ||||
|         var now = Instant.now(); | ||||
|         testData.put("/alfresco-access/transaction/properties/from", (Serializable) Map.of(QName.createQName("modified"), Date.from(now))); | ||||
|         testData.put("/alfresco-access/transaction/properties/to", (Serializable) Map.of(QName.createQName("modified"), Date.from(now))); | ||||
|  | ||||
|         var builder = AuditRecordUtils.generateAuditRecordBuilder(testData, "/alfresco-access/".length()); | ||||
|         builder.setAuditRecordType("alfresco-access"); | ||||
|         var auditRecord = builder.build(); | ||||
|  | ||||
|         assertNotNull(auditRecord); | ||||
|         assertEquals("alfresco-access", auditRecord.getAuditApplicationId()); | ||||
|  | ||||
|         var auditData = auditRecord.getAuditData(); | ||||
|         assertEquals(1, auditData.size()); | ||||
|  | ||||
|         var transaction = (HashMap<String, ?>) auditData.get("transaction"); | ||||
|         assertNotNull(transaction); | ||||
|         assertEquals(4, transaction.size()); | ||||
|         assertEquals(testData.get("/alfresco-access/transaction/path"), transaction.get("path")); | ||||
|         assertEquals(testData.get("/alfresco-access/transaction/user"), transaction.get("user")); | ||||
|         assertEquals(testData.get("/alfresco-access/transaction/sub-actions"), transaction.get("sub-actions")); | ||||
|  | ||||
|         var properties = (HashMap<String, Object>) transaction.get("properties"); | ||||
|         assertNotNull(properties); | ||||
|         assertEquals(2, properties.size()); | ||||
|         assertEquals(testData.get("/alfresco-access/transaction/properties/from"), properties.get("from")); | ||||
|         assertEquals(testData.get("/alfresco-access/transaction/properties/to"), properties.get("to")); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     @Test | ||||
|     public void testGenerateAuditRecordBuilderTestNodeRef() | ||||
|     { | ||||
|         var testData = new HashMap<String, Serializable>(); | ||||
|         var expectedValue = new HashMap<String, Serializable>(); | ||||
|  | ||||
|         expectedValue.put("nodeRef", new NodeRef("workspace://SpacesStore/bfa612e6-1a02-46a0-a612-e61a02e6a036")); | ||||
|         expectedValue.put("objectId", "bfa612e6-1a02-46a0-a612-e61a02e6a036;1.0"); | ||||
|  | ||||
|         testData.put("/CMISChangeLog/CREATED/result/value", expectedValue); | ||||
|  | ||||
|         var builder = AuditRecordUtils.generateAuditRecordBuilder(testData, "/CMISChangeLog/".length()); | ||||
|         builder.setAuditRecordType("CMISChangeLog"); | ||||
|         var auditRecord = builder.build(); | ||||
|  | ||||
|         assertNotNull(auditRecord); | ||||
|  | ||||
|         assertEquals("CMISChangeLog", auditRecord.getAuditApplicationId()); | ||||
|  | ||||
|         var auditData = auditRecord.getAuditData(); | ||||
|         assertEquals(1, auditData.size()); | ||||
|  | ||||
|         var created = (HashMap<String, ?>) auditData.get("CREATED"); | ||||
|         assertNotNull(created); | ||||
|  | ||||
|         assertEquals(1, created.size()); | ||||
|         var result = (HashMap<String, Object>) created.get("result"); | ||||
|         assertNotNull(result); | ||||
|         assertEquals(1, result.size()); | ||||
|  | ||||
|         var resultValue = (HashMap<String, Object>) result.get("value"); | ||||
|         assertNotNull(resultValue); | ||||
|         assertEquals(2, resultValue.size()); | ||||
|  | ||||
|         var expectedNodeRef = (NodeRef) expectedValue.get("nodeRef"); | ||||
|         assertEquals(expectedNodeRef.getId(), resultValue.get("nodeRef")); | ||||
|         assertEquals(expectedValue.get("objectId"), resultValue.get("objectId")); | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -50,6 +50,7 @@ public class AuditTestSuite extends TestSuite | ||||
|         suite.addTestSuite(UserAuditFilterTest.class); | ||||
|         suite.addTestSuite(AuditMethodInterceptorTest.class); | ||||
|  | ||||
|         suite.addTest(new JUnit4TestAdapter(AuditRecordUtilsTest.class)); | ||||
|         suite.addTest(new JUnit4TestAdapter(PropertyAuditFilterTest.class)); | ||||
|         suite.addTest(new JUnit4TestAdapter(AccessAuditorTest.class)); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user