mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	Compare commits
	
		
			370 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					8c69432052 | ||
| 
						 | 
					124f87ee21 | ||
| 
						 | 
					3cd3b2c2d6 | ||
| 
						 | 
					14da8d2002 | ||
| 
						 | 
					6a4bbb021c | ||
| 
						 | 
					42d70b17c7 | ||
| 
						 | 
					c7eba0ddc8 | ||
| 
						 | 
					266094c0e1 | ||
| 
						 | 
					e442b4acf0 | ||
| 
						 | 
					fd1028a685 | ||
| 
						 | 
					0a7e275a9c | ||
| 
						 | 
					d1bbba7286 | ||
| 
						 | 
					e1baddebee | ||
| 
						 | 
					3263dcaf2f | ||
| 
						 | 
					8926f7f9a7 | ||
| 
						 | 
					764a1b656c | ||
| 
						 | 
					cf265f2dea | ||
| 
						 | 
					fd0d5204eb | ||
| 
						 | 
					f9b8a4b42d | ||
| 
						 | 
					fcdc1438e7 | ||
| 
						 | 
					7cd1416561 | ||
| 
						 | 
					f197757f94 | ||
| 
						 | 
					af995f1087 | ||
| 
						 | 
					2cfcd3dfa7 | ||
| 
						 | 
					89e09b0162 | ||
| 
						 | 
					495808b172 | ||
| 
						 | 
					57060af84b | ||
| 
						 | 
					60261aafd1 | ||
| 
						 | 
					8dad225394 | ||
| 
						 | 
					5cc21c55e7 | ||
| 
						 | 
					c71aaf7537 | ||
| 
						 | 
					b7d16ac915 | ||
| 
						 | 
					1a436b06e4 | ||
| 
						 | 
					be02be5a8b | ||
| 
						 | 
					a674e574c5 | ||
| 
						 | 
					aacaa62ff9 | ||
| 
						 | 
					371bd1543d | ||
| 
						 | 
					4cb16f046f | ||
| 
						 | 
					2fb7de9ace | ||
| 
						 | 
					ed972c79d7 | ||
| 
						 | 
					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 | ||
| 
						 | 
					812959be2e | ||
| 
						 | 
					5b8c52db67 | ||
| 
						 | 
					20c42b6561 | ||
| 
						 | 
					52dfea9b21 | ||
| 
						 | 
					d04dada44e | ||
| 
						 | 
					2e85de7c81 | ||
| 
						 | 
					42324368e5 | ||
| 
						 | 
					8d885220d8 | ||
| 
						 | 
					6367f5304d | ||
| 
						 | 
					f17b309c27 | ||
| 
						 | 
					bb2cc1765d | ||
| 
						 | 
					d20e8ee158 | ||
| 
						 | 
					254193f9aa | ||
| 
						 | 
					4293f21618 | ||
| 
						 | 
					e0eb43c479 | ||
| 
						 | 
					f1bbb6cce7 | ||
| 
						 | 
					e6e2a2d8ac | ||
| 
						 | 
					c2cfcdc35a | ||
| 
						 | 
					6b2ac86b1d | ||
| 
						 | 
					8b212dc4cf | ||
| 
						 | 
					e2c357c1e0 | ||
| 
						 | 
					4a024e510d | ||
| 
						 | 
					3f75c9b15f | ||
| 
						 | 
					96f94a98be | ||
| 
						 | 
					bfc0445aeb | ||
| 
						 | 
					977f6f12d4 | ||
| 
						 | 
					626640ddc7 | ||
| 
						 | 
					2b00e550a9 | ||
| 
						 | 
					f3dca482ff | ||
| 
						 | 
					94e957cb73 | ||
| 
						 | 
					8868e64a6a | ||
| 
						 | 
					f4af65943f | ||
| 
						 | 
					6fe1b50741 | ||
| 
						 | 
					f300bd6b3a | ||
| 
						 | 
					f7195ef16a | ||
| 
						 | 
					ef228f0614 | ||
| 
						 | 
					6c0f231316 | ||
| 
						 | 
					33b521b421 | ||
| 
						 | 
					eff4e0738c | ||
| 
						 | 
					5685fc3b17 | ||
| 
						 | 
					7c22a2598b | ||
| 
						 | 
					efe053167d | ||
| 
						 | 
					4aae383637 | ||
| 
						 | 
					ca1774ffae | ||
| 
						 | 
					6206f67bdd | ||
| 
						 | 
					82897f449f | ||
| 
						 | 
					e6d30cddac | ||
| 
						 | 
					0e6b444a25 | ||
| 
						 | 
					61dc54bb33 | ||
| 
						 | 
					a6c7f044df | ||
| 
						 | 
					18016df074 | ||
| 
						 | 
					b021c1ebfe | ||
| 
						 | 
					e3e42137d9 | ||
| 
						 | 
					9327814266 | ||
| 
						 | 
					d45fd10431 | ||
| 
						 | 
					09242daaec | ||
| 
						 | 
					491a41e8ba | ||
| 
						 | 
					1dac1e5fdb | ||
| 
						 | 
					2c9d8e63ea | ||
| 
						 | 
					a3998de1ba | ||
| 
						 | 
					582742bbd3 | ||
| 
						 | 
					ebe29717fa | ||
| 
						 | 
					c6a654cbcf | ||
| 
						 | 
					cd947b5581 | ||
| 
						 | 
					ef4dd74580 | ||
| 
						 | 
					7edbdd72f2 | ||
| 
						 | 
					b7978c2a62 | ||
| 
						 | 
					c1d9142a9c | ||
| 
						 | 
					03a5a3cd3a | ||
| 
						 | 
					c1ac217c44 | ||
| 
						 | 
					1b04fef8cd | ||
| 
						 | 
					31bb692b16 | ||
| 
						 | 
					3222f13efe | ||
| 
						 | 
					d66ed689a0 | ||
| 
						 | 
					f84a7035d7 | ||
| 
						 | 
					56177b6790 | ||
| 
						 | 
					3c32899f6e | ||
| 
						 | 
					d3512dd8e4 | ||
| 
						 | 
					d8273b19cf | ||
| 
						 | 
					8abea08762 | ||
| 
						 | 
					42e3ead101 | ||
| 
						 | 
					b1c095b51e | ||
| 
						 | 
					ab85ea8ffe | ||
| 
						 | 
					9a794cfe97 | ||
| 
						 | 
					8e3f610f8a | ||
| 
						 | 
					8c9edde95f | ||
| 
						 | 
					5652317a0a | ||
| 
						 | 
					b8a9a9588c | ||
| 
						 | 
					a48cf7eed3 | ||
| 
						 | 
					73f6c7a8f7 | ||
| 
						 | 
					37aaa0d147 | ||
| 
						 | 
					5d5d1d8680 | ||
| 
						 | 
					120d4acdb0 | ||
| 
						 | 
					fa3acb47a9 | ||
| 
						 | 
					716b039c07 | ||
| 
						 | 
					abec95881e | ||
| 
						 | 
					58164d59bf | ||
| 
						 | 
					8e80b87439 | ||
| 
						 | 
					4e3591cc72 | ||
| 
						 | 
					6aa4c37825 | ||
| 
						 | 
					68dae275a5 | ||
| 
						 | 
					536f8c609c | ||
| 
						 | 
					6686863352 | ||
| 
						 | 
					3774a0e504 | ||
| 
						 | 
					4bf569d297 | ||
| 
						 | 
					13bcfe4125 | ||
| 
						 | 
					982e0d24f5 | ||
| 
						 | 
					c2fbf53b03 | ||
| 
						 | 
					f8b5e9cb03 | ||
| 
						 | 
					ea0b37022b | ||
| 
						 | 
					f20a9b441d | ||
| 
						 | 
					7c3a9113a0 | ||
| 
						 | 
					66d9beef14 | ||
| 
						 | 
					4a97305505 | ||
| 
						 | 
					9b4593e4b3 | ||
| 
						 | 
					01a2a23c4a | ||
| 
						 | 
					2c2b51f47e | ||
| 
						 | 
					1e37b3308d | ||
| 
						 | 
					84a36d68d4 | ||
| 
						 | 
					40d475abf9 | ||
| 
						 | 
					5d3f1f2402 | ||
| 
						 | 
					2cd29d87ec | ||
| 
						 | 
					8f1631fb55 | ||
| 
						 | 
					5f6dced886 | ||
| 
						 | 
					25fdc9ccc7 | ||
| 
						 | 
					b6456cde34 | ||
| 
						 | 
					27b80d8adb | ||
| 
						 | 
					536e12ff76 | ||
| 
						 | 
					96ab699ea8 | ||
| 
						 | 
					9630d4cb02 | ||
| 
						 | 
					fe9175d959 | ||
| 
						 | 
					277d10133d | ||
| 
						 | 
					3fb964a460 | ||
| 
						 | 
					d630dcb7af | ||
| 
						 | 
					7a673966aa | ||
| 
						 | 
					65d2863b9d | ||
| 
						 | 
					56ef484ae0 | ||
| 
						 | 
					d619d5365e | ||
| 
						 | 
					7096276b02 | ||
| 
						 | 
					c9a46e1a8a | ||
| 
						 | 
					98f9175a13 | ||
| 
						 | 
					4e89b0a033 | ||
| 
						 | 
					c6c0779984 | ||
| 
						 | 
					554b26e7e7 | ||
| 
						 | 
					93d704d44c | ||
| 
						 | 
					0916efad7c | ||
| 
						 | 
					6169ec0095 | ||
| 
						 | 
					75ace2a268 | ||
| 
						 | 
					f489c4193b | ||
| 
						 | 
					71c3addf2d | ||
| 
						 | 
					cc43f9338e | ||
| 
						 | 
					45ad5045b1 | ||
| 
						 | 
					773f99453e | ||
| 
						 | 
					538d269f66 | ||
| 
						 | 
					1dbf1c1314 | ||
| 
						 | 
					3969cd8f14 | ||
| 
						 | 
					eaa11fcb34 | ||
| 
						 | 
					cd5c2227e2 | ||
| 
						 | 
					757b26b1b8 | ||
| 
						 | 
					0366449457 | ||
| 
						 | 
					6827b2c7a1 | ||
| 
						 | 
					36bc1dc1a4 | ||
| 
						 | 
					8407159a17 | ||
| 
						 | 
					917dd35c27 | ||
| 
						 | 
					6e815ebd34 | ||
| 
						 | 
					ad0668cce3 | ||
| 
						 | 
					11fd7ca1dd | ||
| 
						 | 
					6be0c3031f | ||
| 
						 | 
					a1d869bdef | ||
| 
						 | 
					d130c12f2a | ||
| 
						 | 
					24e987e735 | ||
| 
						 | 
					63e7492823 | ||
| 
						 | 
					545a957a1b | ||
| 
						 | 
					325e980cf0 | ||
| 
						 | 
					e2a4dcc6ad | ||
| 
						 | 
					a993c9ed97 | ||
| 
						 | 
					85d2a5176b | ||
| 
						 | 
					3c245ae64c | ||
| 
						 | 
					fbf5966d10 | ||
| 
						 | 
					8fca14df40 | ||
| 
						 | 
					e38e7b4bda | ||
| 
						 | 
					b378de58d8 | ||
| 
						 | 
					8a64d9fb0b | ||
| 
						 | 
					3c092b696d | ||
| 
						 | 
					6f222106c5 | ||
| 
						 | 
					bd5a016382 | ||
| 
						 | 
					dc24718717 | ||
| 
						 | 
					e65614d3c2 | ||
| 
						 | 
					698f9b15ce | ||
| 
						 | 
					09a254e5fd | ||
| 
						 | 
					6f442a703a | ||
| 
						 | 
					f1862c9636 | ||
| 
						 | 
					231075fd5e | ||
| 
						 | 
					979420879c | ||
| 
						 | 
					db330e28f5 | ||
| 
						 | 
					bae0573636 | ||
| 
						 | 
					8089fc2572 | ||
| 
						 | 
					1c4fe53c0f | ||
| 
						 | 
					7a8aa1a2c1 | ||
| 
						 | 
					e08ba1fd4f | ||
| 
						 | 
					4f40bd0687 | ||
| 
						 | 
					a3578f7baa | ||
| 
						 | 
					8e8b9c868f | ||
| 
						 | 
					f77b3b79e5 | ||
| 
						 | 
					3a7157f4a7 | ||
| 
						 | 
					d6b979f341 | ||
| 
						 | 
					a090de4e71 | ||
| 
						 | 
					03621db30a | ||
| 
						 | 
					766a6def2b | ||
| 
						 | 
					117804fb68 | ||
| 
						 | 
					f03e6761ce | ||
| 
						 | 
					74c8288206 | ||
| 
						 | 
					c0bd0a680b | ||
| 
						 | 
					8645cdc76d | ||
| 
						 | 
					5055eec2df | ||
| 
						 | 
					892f41d6fd | ||
| 
						 | 
					075b02baee | ||
| 
						 | 
					7c8a75ce6c | ||
| 
						 | 
					cb0a925e27 | ||
| 
						 | 
					c7d2699f7e | ||
| 
						 | 
					b942b55193 | ||
| 
						 | 
					c1100fe983 | ||
| 
						 | 
					62236c90f5 | ||
| 
						 | 
					eabdab91fb | ||
| 
						 | 
					4f83076cfe | ||
| 
						 | 
					7eda1d420f | ||
| 
						 | 
					411388d62d | ||
| 
						 | 
					245d1317ac | ||
| 
						 | 
					ee5e34ca32 | ||
| 
						 | 
					c4dcef73e1 | ||
| 
						 | 
					4f53fee1fc | ||
| 
						 | 
					d163410e3d | ||
| 
						 | 
					9ca251edba | ||
| 
						 | 
					193cb9b30d | ||
| 
						 | 
					e9a36f67fe | ||
| 
						 | 
					c18a58caea | ||
| 
						 | 
					11659ab917 | ||
| 
						 | 
					89b1049809 | ||
| 
						 | 
					192c105719 | ||
| 
						 | 
					b8fc8efa07 | ||
| 
						 | 
					2e851cf88d | ||
| 
						 | 
					ebf081c731 | ||
| 
						 | 
					b979701264 | ||
| 
						 | 
					aa0d02abf2 | ||
| 
						 | 
					2f7b8d50a3 | ||
| 
						 | 
					800736a025 | ||
| 
						 | 
					b8b7e5193e | ||
| 
						 | 
					808faa71b3 | ||
| 
						 | 
					0bd476968b | ||
| 
						 | 
					e51e5e8ca5 | ||
| 
						 | 
					46db14d5ff | ||
| 
						 | 
					b5fa73ca3b | ||
| 
						 | 
					c962daae3b | ||
| 
						 | 
					8efc559b09 | ||
| 
						 | 
					08628732fc | ||
| 
						 | 
					7f74bf7b3e | ||
| 
						 | 
					390073b153 | ||
| 
						 | 
					68c87f69c5 | ||
| 
						 | 
					2b936050c8 | ||
| 
						 | 
					28184ca69a | ||
| 
						 | 
					e05c74813e | ||
| 
						 | 
					e05a1d9ba9 | ||
| 
						 | 
					5aab15a77a | ||
| 
						 | 
					5d267c8d60 | ||
| 
						 | 
					18fc9a58b4 | ||
| 
						 | 
					6a7ba876b7 | ||
| 
						 | 
					65bdb242ec | ||
| 
						 | 
					5a537b301a | ||
| 
						 | 
					515b894241 | ||
| 
						 | 
					4978d9e790 | ||
| 
						 | 
					bf8f5117ac | ||
| 
						 | 
					cfb5cb2c6d | ||
| 
						 | 
					71eed6822d | 
							
								
								
									
										793
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										793
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										20
									
								
								.github/workflows/master_release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								.github/workflows/master_release.yml
									
									
									
									
										vendored
									
									
								
							@@ -31,15 +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.16.0
 | 
			
		||||
      - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
 | 
			
		||||
      - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
 | 
			
		||||
      - uses: Alfresco/alfresco-build-tools/.github/actions/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
 | 
			
		||||
      - name: "Init"
 | 
			
		||||
        run: bash ./scripts/ci/init.sh
 | 
			
		||||
      - uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v8.16.0
 | 
			
		||||
      - uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v8.24.1
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ env.GIT_USERNAME }}
 | 
			
		||||
          email: ${{ env.GIT_EMAIL }}
 | 
			
		||||
@@ -60,15 +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.16.0
 | 
			
		||||
      - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
 | 
			
		||||
      - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
 | 
			
		||||
      - uses: Alfresco/alfresco-build-tools/.github/actions/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
 | 
			
		||||
      - name: "Init"
 | 
			
		||||
        run: bash ./scripts/ci/init.sh
 | 
			
		||||
      - uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v8.16.0
 | 
			
		||||
      - uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v8.24.1
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ env.GIT_USERNAME }}
 | 
			
		||||
          email: ${{ env.GIT_EMAIL }}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/workflows/precommit_formatter.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/precommit_formatter.yml
									
									
									
									
										vendored
									
									
								
							@@ -11,9 +11,9 @@ 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@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
 | 
			
		||||
        uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "3.9"
 | 
			
		||||
      - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
 | 
			
		||||
@@ -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.16.0
 | 
			
		||||
      - uses: Alfresco/alfresco-build-tools/.github/actions/git-commit-changes@v8.24.1
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ secrets.BOT_GITHUB_USERNAME }}
 | 
			
		||||
          add-options: -u
 | 
			
		||||
 
 | 
			
		||||
@@ -127,29 +127,6 @@
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "results": {
 | 
			
		||||
    ".github/workflows/ci.yml": [
 | 
			
		||||
      {
 | 
			
		||||
        "type": "Secret Keyword",
 | 
			
		||||
        "filename": ".github/workflows/ci.yml",
 | 
			
		||||
        "hashed_secret": "b86dc2f033a63f2b7b9e7d270ab806d2910d7572",
 | 
			
		||||
        "is_verified": false,
 | 
			
		||||
        "line_number": 295
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "type": "Secret Keyword",
 | 
			
		||||
        "filename": ".github/workflows/ci.yml",
 | 
			
		||||
        "hashed_secret": "1bfb0e20f886150ba59b853bcd49dea893e00966",
 | 
			
		||||
        "is_verified": false,
 | 
			
		||||
        "line_number": 370
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "type": "Secret Keyword",
 | 
			
		||||
        "filename": ".github/workflows/ci.yml",
 | 
			
		||||
        "hashed_secret": "128f14373ccfaff49e3664045d3a11b50cbb7b39",
 | 
			
		||||
        "is_verified": false,
 | 
			
		||||
        "line_number": 904
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    ".github/workflows/master_release.yml": [
 | 
			
		||||
      {
 | 
			
		||||
        "type": "Secret Keyword",
 | 
			
		||||
@@ -1265,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
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
@@ -1273,7 +1250,7 @@
 | 
			
		||||
        "filename": "repository/src/main/resources/alfresco/repository.properties",
 | 
			
		||||
        "hashed_secret": "84551ae5442affc9f1a2d3b4c86ae8b24860149d",
 | 
			
		||||
        "is_verified": false,
 | 
			
		||||
        "line_number": 770,
 | 
			
		||||
        "line_number": 773,
 | 
			
		||||
        "is_secret": false
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
@@ -1868,5 +1845,5 @@
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  "generated_at": "2025-03-27T23:45:41Z"
 | 
			
		||||
  "generated_at": "2025-07-23T08:25:11Z"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
   <parent>
 | 
			
		||||
      <groupId>org.alfresco</groupId>
 | 
			
		||||
      <artifactId>alfresco-community-repo-amps</artifactId>
 | 
			
		||||
      <version>25.2.0.16</version>
 | 
			
		||||
      <version>25.3.0.62</version>
 | 
			
		||||
   </parent>
 | 
			
		||||
 | 
			
		||||
   <modules>
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
   <parent>
 | 
			
		||||
      <groupId>org.alfresco</groupId>
 | 
			
		||||
      <artifactId>alfresco-governance-services-community-parent</artifactId>
 | 
			
		||||
      <version>25.2.0.16</version>
 | 
			
		||||
      <version>25.3.0.62</version>
 | 
			
		||||
   </parent>
 | 
			
		||||
 | 
			
		||||
   <modules>
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
   <parent>
 | 
			
		||||
      <groupId>org.alfresco</groupId>
 | 
			
		||||
      <artifactId>alfresco-governance-services-automation-community-repo</artifactId>
 | 
			
		||||
      <version>25.2.0.16</version>
 | 
			
		||||
      <version>25.3.0.62</version>
 | 
			
		||||
   </parent>
 | 
			
		||||
 | 
			
		||||
   <build>
 | 
			
		||||
@@ -74,16 +74,6 @@
 | 
			
		||||
         <artifactId>alfresco-testng</artifactId>
 | 
			
		||||
         <version>1.1</version>
 | 
			
		||||
      </dependency>
 | 
			
		||||
      <dependency>
 | 
			
		||||
         <groupId>com.epam.reportportal</groupId>
 | 
			
		||||
         <artifactId>agent-java-testng</artifactId>
 | 
			
		||||
         <scope>test</scope>
 | 
			
		||||
      </dependency>
 | 
			
		||||
      <dependency>
 | 
			
		||||
         <groupId>com.squareup.okhttp3</groupId>
 | 
			
		||||
         <artifactId>okhttp</artifactId>
 | 
			
		||||
         <scope>test</scope>
 | 
			
		||||
      </dependency>
 | 
			
		||||
      <dependency>
 | 
			
		||||
         <groupId>org.awaitility</groupId>
 | 
			
		||||
         <artifactId>awaitility</artifactId>
 | 
			
		||||
@@ -93,7 +83,7 @@
 | 
			
		||||
      <dependency>
 | 
			
		||||
         <groupId>org.apache.commons</groupId>
 | 
			
		||||
         <artifactId>commons-collections4</artifactId>
 | 
			
		||||
         <version>4.4</version>
 | 
			
		||||
         <version>4.5.0</version>
 | 
			
		||||
      </dependency>
 | 
			
		||||
      <dependency>
 | 
			
		||||
         <groupId>com.github.docker-java</groupId>
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,30 @@
 | 
			
		||||
/*
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Records Management Module
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * -
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * -
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
package org.alfresco.rest.rm.community.model;
 | 
			
		||||
 | 
			
		||||
public record CapabilityModel(String name, String title, String description, GroupModel group, int index)
 | 
			
		||||
{}
 | 
			
		||||
@@ -0,0 +1,30 @@
 | 
			
		||||
/*
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Records Management Module
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * -
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * -
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
package org.alfresco.rest.rm.community.model;
 | 
			
		||||
 | 
			
		||||
public record GroupModel(String id, String title)
 | 
			
		||||
{}
 | 
			
		||||
@@ -0,0 +1,91 @@
 | 
			
		||||
/*-
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Records Management Module
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * -
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * -
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
package org.alfresco.rest.rm.community.model.role;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import org.alfresco.rest.rm.community.model.CapabilityModel;
 | 
			
		||||
import org.alfresco.utility.model.TestModel;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * POJO for role
 | 
			
		||||
 */
 | 
			
		||||
@Builder
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public class Role extends TestModel
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    @JsonProperty(required = true)
 | 
			
		||||
    private String name;
 | 
			
		||||
 | 
			
		||||
    @JsonProperty(required = true)
 | 
			
		||||
    private List<CapabilityModel> capabilities;
 | 
			
		||||
 | 
			
		||||
    @JsonProperty(required = true)
 | 
			
		||||
    private String displayLabel;
 | 
			
		||||
 | 
			
		||||
    @JsonProperty(required = true)
 | 
			
		||||
    private String groupShortName;
 | 
			
		||||
 | 
			
		||||
    private List<String> assignedUsers;
 | 
			
		||||
 | 
			
		||||
    private List<String> assignedGroups;
 | 
			
		||||
 | 
			
		||||
    private String roleGroupName;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean equals(Object o)
 | 
			
		||||
    {
 | 
			
		||||
        if (this == o)
 | 
			
		||||
        {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        if (o == null || getClass() != o.getClass())
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        Role role = (Role) o;
 | 
			
		||||
        return Objects.equals(name, role.name) && Objects.equals(capabilities, role.capabilities)
 | 
			
		||||
                && Objects.equals(displayLabel, role.displayLabel) && Objects.equals(groupShortName, role.groupShortName) && Objects.equals(assignedUsers, role.assignedUsers)
 | 
			
		||||
                && Objects.equals(assignedGroups, role.assignedGroups) && Objects.equals(roleGroupName, role.roleGroupName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int hashCode()
 | 
			
		||||
    {
 | 
			
		||||
        return Objects.hash(name, capabilities, displayLabel, groupShortName, assignedUsers, assignedGroups, roleGroupName);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
/*-
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Records Management Module
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * -
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * -
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
package org.alfresco.rest.rm.community.model.role;
 | 
			
		||||
 | 
			
		||||
import org.alfresco.rest.core.RestModels;
 | 
			
		||||
 | 
			
		||||
public class RoleCollection extends RestModels<RoleEntry, RoleCollection>
 | 
			
		||||
{}
 | 
			
		||||
@@ -0,0 +1,47 @@
 | 
			
		||||
/*-
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Records Management Module
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * -
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * -
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
package org.alfresco.rest.rm.community.model.role;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import org.alfresco.rest.core.RestModels;
 | 
			
		||||
 | 
			
		||||
@Builder
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public class RoleEntry extends RestModels<Role, RoleEntry>
 | 
			
		||||
{
 | 
			
		||||
    @JsonProperty
 | 
			
		||||
    private Role entry;
 | 
			
		||||
}
 | 
			
		||||
@@ -35,7 +35,7 @@ package org.alfresco.rest.rm.community.model.user;
 | 
			
		||||
 */
 | 
			
		||||
public enum UserRoles
 | 
			
		||||
{
 | 
			
		||||
    IN_PLACE_WRITERS("ExtendedWriters", "In-Place Writers"), ROLE_RM_ADMIN("Administrator", "Records Management Administrator"), ROLE_RM_MANAGER("RecordsManager", "Records Management Manager"), ROLE_RM_POWER_USER("PowerUser", "Records Management Power User"), ROLE_RM_SECURITY_OFFICER("SecurityOfficer", "Records Management Security Officer"), ROLE_RM_USER("User", "Records Management User");
 | 
			
		||||
    IN_PLACE_WRITERS("ExtendedWriters", "In-Place Writers"), ROLE_RM_ADMIN("Administrator", "Records Management Administrator"), ROLE_RM_MANAGER("RecordsManager", "Records Management Manager"), ROLE_RM_POWER_USER("PowerUser", "Records Management Power User"), ROLE_RM_SECURITY_OFFICER("SecurityOfficer", "Records Management Security Officer"), ROLE_RM_USER("User", "Records Management User"), IN_PLACE_READERS("ExtendedReaders", "In-Place Readers");
 | 
			
		||||
 | 
			
		||||
    public final String roleId;
 | 
			
		||||
    public final String displayName;
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,7 @@ import org.alfresco.rest.rm.community.model.hold.Hold;
 | 
			
		||||
import org.alfresco.rest.rm.community.model.hold.HoldCollection;
 | 
			
		||||
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
 | 
			
		||||
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryCollection;
 | 
			
		||||
import org.alfresco.rest.rm.community.model.role.RoleCollection;
 | 
			
		||||
import org.alfresco.rest.rm.community.requests.RMModelRequest;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -303,4 +304,39 @@ public class FilePlanAPI extends RMModelRequest
 | 
			
		||||
    {
 | 
			
		||||
        return getHolds(filePlanId, EMPTY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the roles of a file plan.
 | 
			
		||||
     *
 | 
			
		||||
     * @param filePlanId
 | 
			
		||||
     *            The identifier of a file plan
 | 
			
		||||
     * @param parameters
 | 
			
		||||
     *            The URL parameters to add
 | 
			
		||||
     * @return The {Pagination and RoleModel Entries} for the given {@code filePlanId}
 | 
			
		||||
     * @throws RuntimeException
 | 
			
		||||
     *             for the following cases:
 | 
			
		||||
     *             <ul>
 | 
			
		||||
     *             <li>authentication fails</li>
 | 
			
		||||
     *             <li>current user does not have permission to read {@code filePlanId}</li>
 | 
			
		||||
     *             <li>{@code filePlanId} does not exist</li>
 | 
			
		||||
     *             </ul>
 | 
			
		||||
     */
 | 
			
		||||
    public RoleCollection getFilePlanRoles(String filePlanId, String parameters)
 | 
			
		||||
    {
 | 
			
		||||
        mandatoryString("filePlanId", filePlanId);
 | 
			
		||||
        return getRmRestWrapper().processModels(RoleCollection.class, simpleRequest(
 | 
			
		||||
                GET,
 | 
			
		||||
                "file-plans/{filePlanId}/roles?{parameters}",
 | 
			
		||||
                filePlanId,
 | 
			
		||||
                parameters));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * See {@link #getFilePlanRoles(String, String)}
 | 
			
		||||
     */
 | 
			
		||||
    public RoleCollection getFilePlanRoles(String filePlanId)
 | 
			
		||||
    {
 | 
			
		||||
        return getFilePlanRoles(filePlanId, EMPTY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ import com.github.dockerjava.core.command.LogContainerResultCallback;
 | 
			
		||||
import com.github.dockerjava.netty.NettyDockerCmdExecFactory;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import org.apache.commons.lang.SystemUtils;
 | 
			
		||||
import org.apache.commons.lang3.SystemUtils;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
 
 | 
			
		||||
@@ -93,6 +93,7 @@ import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI;
 | 
			
		||||
import org.alfresco.rest.search.RestRequestQueryModel;
 | 
			
		||||
import org.alfresco.rest.search.SearchNodeModel;
 | 
			
		||||
import org.alfresco.rest.search.SearchRequest;
 | 
			
		||||
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
 | 
			
		||||
import org.alfresco.rest.v0.SearchAPI;
 | 
			
		||||
import org.alfresco.utility.Utility;
 | 
			
		||||
import org.alfresco.utility.data.DataUserAIS;
 | 
			
		||||
@@ -127,6 +128,10 @@ public class BaseRMRestTest extends RestTest
 | 
			
		||||
    @Getter(value = PROTECTED)
 | 
			
		||||
    private SearchAPI searchApi;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    @Getter(PROTECTED)
 | 
			
		||||
    private RMRolesAndActionsAPI rmRolesAndActionsV0API;
 | 
			
		||||
 | 
			
		||||
    protected static final String iso8601_DateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ package org.alfresco.rest.rm.community.fileplans;
 | 
			
		||||
 | 
			
		||||
import static java.util.Arrays.asList;
 | 
			
		||||
 | 
			
		||||
import static com.google.common.collect.Sets.newHashSet;
 | 
			
		||||
import static org.springframework.http.HttpStatus.CONFLICT;
 | 
			
		||||
import static org.springframework.http.HttpStatus.CREATED;
 | 
			
		||||
import static org.springframework.http.HttpStatus.FORBIDDEN;
 | 
			
		||||
@@ -56,19 +57,27 @@ import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanCo
 | 
			
		||||
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_CONTAINER_TYPE;
 | 
			
		||||
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_RECORD_FOLDER_TYPE;
 | 
			
		||||
import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_FILING;
 | 
			
		||||
import static org.alfresco.rest.rm.community.model.user.UserRoles.IN_PLACE_READERS;
 | 
			
		||||
import static org.alfresco.rest.rm.community.model.user.UserRoles.IN_PLACE_WRITERS;
 | 
			
		||||
import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_ADMIN;
 | 
			
		||||
import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_MANAGER;
 | 
			
		||||
import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_POWER_USER;
 | 
			
		||||
import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_SECURITY_OFFICER;
 | 
			
		||||
import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_USER;
 | 
			
		||||
import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric;
 | 
			
		||||
import static org.alfresco.utility.data.RandomData.getRandomName;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.NoSuchElementException;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import org.testng.annotations.DataProvider;
 | 
			
		||||
import org.testng.annotations.Test;
 | 
			
		||||
 | 
			
		||||
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
 | 
			
		||||
import org.alfresco.rest.rm.community.base.DataProviderClass;
 | 
			
		||||
import org.alfresco.rest.rm.community.model.CapabilityModel;
 | 
			
		||||
import org.alfresco.rest.rm.community.model.fileplan.FilePlan;
 | 
			
		||||
import org.alfresco.rest.rm.community.model.fileplan.FilePlanProperties;
 | 
			
		||||
import org.alfresco.rest.rm.community.model.hold.Hold;
 | 
			
		||||
@@ -76,6 +85,9 @@ import org.alfresco.rest.rm.community.model.hold.HoldCollection;
 | 
			
		||||
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
 | 
			
		||||
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryCollection;
 | 
			
		||||
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryProperties;
 | 
			
		||||
import org.alfresco.rest.rm.community.model.role.Role;
 | 
			
		||||
import org.alfresco.rest.rm.community.model.role.RoleCollection;
 | 
			
		||||
import org.alfresco.rest.rm.community.model.user.UserCapabilities;
 | 
			
		||||
import org.alfresco.rest.rm.community.requests.gscore.api.RMSiteAPI;
 | 
			
		||||
import org.alfresco.utility.constants.ContainerName;
 | 
			
		||||
import org.alfresco.utility.model.UserModel;
 | 
			
		||||
@@ -87,6 +99,7 @@ import org.alfresco.utility.report.Bug;
 | 
			
		||||
 * @author Rodica Sutu
 | 
			
		||||
 * @since 2.6
 | 
			
		||||
 */
 | 
			
		||||
@SuppressWarnings("PMD.UnitTestShouldIncludeAssert")
 | 
			
		||||
public class FilePlanTests extends BaseRMRestTest
 | 
			
		||||
{
 | 
			
		||||
    // ** Number of children (for children creation test) */
 | 
			
		||||
@@ -266,7 +279,7 @@ public class FilePlanTests extends BaseRMRestTest
 | 
			
		||||
     * When I ask the API to create a root record category
 | 
			
		||||
     * Then it is created as a root record category
 | 
			
		||||
     * </pre>
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * Given that a file plan exists
 | 
			
		||||
     * When I use the API to create a folder (cm:folder type) into the fileplan
 | 
			
		||||
@@ -314,7 +327,7 @@ public class FilePlanTests extends BaseRMRestTest
 | 
			
		||||
     * When I ask the API to create a root category having the same name
 | 
			
		||||
     * Then  the response code received is 409 - name clashes with an existing node
 | 
			
		||||
     * </pre>
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * Given a root category
 | 
			
		||||
     * When I ask the API to create a root category having the same name  with autoRename parameter on true
 | 
			
		||||
@@ -594,4 +607,171 @@ public class FilePlanTests extends BaseRMRestTest
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * Given that a file plan exists
 | 
			
		||||
     * When rmAdmin user ask the API for roles
 | 
			
		||||
     * It provides list of all default roles
 | 
			
		||||
     * </pre>
 | 
			
		||||
     */
 | 
			
		||||
    @Test
 | 
			
		||||
    public void listFilePlanAllDefaultRoles()
 | 
			
		||||
    {
 | 
			
		||||
        List<String> defaultRolesDisplayNames = asList(IN_PLACE_READERS.displayName, ROLE_RM_ADMIN.displayName, ROLE_RM_MANAGER.displayName, ROLE_RM_POWER_USER.displayName, ROLE_RM_USER.displayName, IN_PLACE_WRITERS.displayName, ROLE_RM_SECURITY_OFFICER.displayName);
 | 
			
		||||
        // Call to new API to get the roles and capabilities
 | 
			
		||||
        RoleCollection roleCollection = getRestAPIFactory().getFilePlansAPI().getFilePlanRoles(FILE_PLAN_ALIAS);
 | 
			
		||||
        assertStatusCode(OK);
 | 
			
		||||
        roleCollection.getEntries().forEach(roleModelEntry -> {
 | 
			
		||||
            Role role = roleModelEntry.getEntry();
 | 
			
		||||
            assertTrue(defaultRolesDisplayNames.contains(role.getDisplayLabel()));
 | 
			
		||||
            assertNotNull(role.getCapabilities());
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * Given that a file plan exists
 | 
			
		||||
     * When rmAdmin user ask the API for roles with SystemRoles as false
 | 
			
		||||
     * It provides list of all roles excluding SystemRoles
 | 
			
		||||
     * </pre>
 | 
			
		||||
     */
 | 
			
		||||
    @Test
 | 
			
		||||
    public void listFilePlanAllRolesExcludeSystemRoles()
 | 
			
		||||
    {
 | 
			
		||||
        String parameters = "where=(systemRoles=false)";
 | 
			
		||||
        List<String> systemRolesDisplayNames = asList(IN_PLACE_WRITERS.displayName, IN_PLACE_READERS.displayName);
 | 
			
		||||
        // Call to new API to get the roles and capabilities
 | 
			
		||||
        RoleCollection roleCollection = getRestAPIFactory().getFilePlansAPI().getFilePlanRoles(FILE_PLAN_ALIAS, parameters);
 | 
			
		||||
        assertStatusCode(OK);
 | 
			
		||||
        roleCollection.getEntries().forEach(roleModelEntry -> {
 | 
			
		||||
            Role role = roleModelEntry.getEntry();
 | 
			
		||||
            assertFalse(systemRolesDisplayNames.contains(role.getDisplayLabel()));
 | 
			
		||||
            assertNotNull(role.getCapabilities());
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * Given that a file plan exists
 | 
			
		||||
     * When a non-RM user asks the API for the roles
 | 
			
		||||
     * Then the status code 403 (Permission denied) is return
 | 
			
		||||
     * </pre>
 | 
			
		||||
     */
 | 
			
		||||
    @Test
 | 
			
		||||
    public void nonRmUserFilePlanRoles()
 | 
			
		||||
    {
 | 
			
		||||
        // Create a random user
 | 
			
		||||
        UserModel nonRMuser = getDataUser().createRandomTestUser("testUser");
 | 
			
		||||
        // Call to new API to get the roles and capabilities
 | 
			
		||||
        getRestAPIFactory().getFilePlansAPI(nonRMuser).getFilePlanRoles(FILE_PLAN_ALIAS);
 | 
			
		||||
        assertStatusCode(FORBIDDEN);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * Given that a file plan exists
 | 
			
		||||
     * When a RM_Manager user asks the API for the roles
 | 
			
		||||
     * returns the RM_Manager role and capabilities
 | 
			
		||||
     * </pre>
 | 
			
		||||
     */
 | 
			
		||||
    @Test
 | 
			
		||||
    public void rmManagerFilePlanRolesAndCapabilities()
 | 
			
		||||
    {
 | 
			
		||||
        // Create a random user
 | 
			
		||||
        UserModel managerUser = getDataUser().createRandomTestUser("managerUser");
 | 
			
		||||
        // Assign RecordsManager role to user
 | 
			
		||||
        getRestAPIFactory().getRMUserAPI().assignRoleToUser(managerUser.getUsername(), ROLE_RM_MANAGER.roleId);
 | 
			
		||||
        String parameters = "where=(personId='" + managerUser.getUsername() + "')";
 | 
			
		||||
        // Call to new API to get the roles and capabilities
 | 
			
		||||
        RoleCollection roleCollection = getRestAPIFactory().getFilePlansAPI(managerUser).getFilePlanRoles(FILE_PLAN_ALIAS, parameters);
 | 
			
		||||
        roleCollection.getEntries().forEach(roleModelEntry -> {
 | 
			
		||||
            Role role = roleModelEntry.getEntry();
 | 
			
		||||
            assertEquals(ROLE_RM_MANAGER.displayName, role.getDisplayLabel());
 | 
			
		||||
            assertNotNull(role.getCapabilities());
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * Given that a file plan exists
 | 
			
		||||
     * When a User with more than one role asks the API for the roles and relation
 | 
			
		||||
     * returns the roles and capabilities
 | 
			
		||||
     * </pre>
 | 
			
		||||
     */
 | 
			
		||||
    @Test
 | 
			
		||||
    public void multipleRoleUserFilePlanRolesAndCapabilities()
 | 
			
		||||
    {
 | 
			
		||||
        // Create a random user
 | 
			
		||||
        UserModel rmUser = getDataUser().createRandomTestUser("rmUser");
 | 
			
		||||
        // Assign rmUser role to user
 | 
			
		||||
        getRestAPIFactory().getRMUserAPI().assignRoleToUser(rmUser.getUsername(), ROLE_RM_USER.roleId);
 | 
			
		||||
        getRestAPIFactory().getRMUserAPI().assignRoleToUser(rmUser.getUsername(), ROLE_RM_POWER_USER.roleId);
 | 
			
		||||
        String parameters = "where=(personId='" + rmUser.getUsername() + "')";
 | 
			
		||||
        // Call to new API to get the roles and capabilities
 | 
			
		||||
        RoleCollection roleCollection = getRestAPIFactory().getFilePlansAPI(rmUser).getFilePlanRoles(FILE_PLAN_ALIAS, parameters);
 | 
			
		||||
        assertStatusCode(OK);
 | 
			
		||||
        assertEquals(roleCollection.getEntries().size(), 2);
 | 
			
		||||
        roleCollection.getEntries().forEach(roleModelEntry -> {
 | 
			
		||||
            Role role = roleModelEntry.getEntry();
 | 
			
		||||
            assertTrue(role.getDisplayLabel().equals(ROLE_RM_USER.displayName) || role.getDisplayLabel().equals(ROLE_RM_POWER_USER.displayName));
 | 
			
		||||
            assertNotNull(role.getCapabilities());
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * Given that a file plan exists
 | 
			
		||||
     * When a new user with a new role asks the API for the roles and relation
 | 
			
		||||
     * returns the new role and new capabilities
 | 
			
		||||
     * </pre>
 | 
			
		||||
     */
 | 
			
		||||
    @Test
 | 
			
		||||
    public void newRoleUserFilePlanRolesAndCapabilities()
 | 
			
		||||
    {
 | 
			
		||||
        /** A list of capabilities. */
 | 
			
		||||
        Set<String> newCapabilities = newHashSet(UserCapabilities.VIEW_RECORDS_CAP, UserCapabilities.DECLARE_RECORDS_CAP);
 | 
			
		||||
        // Create a new role using old API
 | 
			
		||||
        getRmRolesAndActionsV0API().createRole(getAdminUser().getUsername(), getAdminUser().getPassword(), "NewTestRole",
 | 
			
		||||
                "New Role Label", newCapabilities);
 | 
			
		||||
        // Create a random user
 | 
			
		||||
        UserModel rmNewUser = getDataUser().createRandomTestUser("rmPowerUser");
 | 
			
		||||
        // Assign New role to user
 | 
			
		||||
        getRestAPIFactory().getRMUserAPI().assignRoleToUser(rmNewUser.getUsername(), "NewTestRole");
 | 
			
		||||
        String parameters = "where=(personId='" + rmNewUser.getUsername() + "')";
 | 
			
		||||
        // Call to new API to get the roles and capabilities
 | 
			
		||||
        RoleCollection roleCollection = getRestAPIFactory().getFilePlansAPI(rmNewUser).getFilePlanRoles(FILE_PLAN_ALIAS, parameters);
 | 
			
		||||
        assertStatusCode(OK);
 | 
			
		||||
        assertEquals(roleCollection.getEntries().size(), 1);
 | 
			
		||||
        roleCollection.getEntries().forEach(roleModelEntry -> {
 | 
			
		||||
            List<CapabilityModel> capabilities = roleModelEntry.getEntry().getCapabilities();
 | 
			
		||||
            capabilities.forEach(capabilityModel -> {
 | 
			
		||||
                assertTrue(newCapabilities.contains(capabilityModel.name()));
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * Given that a file plan exists
 | 
			
		||||
     * When API call happens with Capability filter
 | 
			
		||||
     * returns roles associated with the capability
 | 
			
		||||
     * </pre>
 | 
			
		||||
     */
 | 
			
		||||
    @Test
 | 
			
		||||
    public void filePlanRolesAndCapabilitiesFilter()
 | 
			
		||||
    {
 | 
			
		||||
        String parameters = "where=(systemRoles=true and capabilityName in ('ManageRules'))";
 | 
			
		||||
        // Call to new API to get the roles and capabilities, filter by capability, include assigned users
 | 
			
		||||
        RoleCollection roleCollection = getRestAPIFactory().getFilePlansAPI().getFilePlanRoles(FILE_PLAN_ALIAS, parameters);
 | 
			
		||||
        assertStatusCode(OK);
 | 
			
		||||
        assertEquals(roleCollection.getEntries().size(), 1);
 | 
			
		||||
        roleCollection.getEntries().forEach(roleModelEntry -> {
 | 
			
		||||
            Role role = roleModelEntry.getEntry();
 | 
			
		||||
            assertEquals(ROLE_RM_ADMIN.displayName, role.getDisplayLabel());
 | 
			
		||||
            assertNotNull(role.getCapabilities());
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -134,6 +134,16 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
 | 
			
		||||
                .until(() -> getRestAPIFactory().getSearchAPI(null).search(searchRequest).getPagination()
 | 
			
		||||
                        .getTotalItems() == NUMBER_OF_FILES);
 | 
			
		||||
 | 
			
		||||
        RestRequestQueryModel ancestorReq = getContentFromFolderAndAllSubfoldersQuery(rootFolder.getNodeRefWithoutVersion());
 | 
			
		||||
        SearchRequest ancestorSearchRequest = new SearchRequest();
 | 
			
		||||
        ancestorSearchRequest.setQuery(ancestorReq);
 | 
			
		||||
 | 
			
		||||
        STEP("Wait until paths are indexed.");
 | 
			
		||||
        // to improve stability on CI - seems that sometimes during big load we need to wait longer for the condition
 | 
			
		||||
        await().atMost(120, TimeUnit.SECONDS)
 | 
			
		||||
                .until(() -> getRestAPIFactory().getSearchAPI(null).search(ancestorSearchRequest).getPagination()
 | 
			
		||||
                        .getTotalItems() == NUMBER_OF_FILES);
 | 
			
		||||
 | 
			
		||||
        holdBulkOperation = HoldBulkOperation.builder()
 | 
			
		||||
                .query(queryReq)
 | 
			
		||||
                .op(HoldBulkOperationType.ADD).build();
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPr
 | 
			
		||||
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.lang.StringUtils;
 | 
			
		||||
import org.apache.commons.lang3.StringUtils;
 | 
			
		||||
import org.json.JSONObject;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.testng.annotations.Test;
 | 
			
		||||
@@ -56,7 +56,7 @@ import org.alfresco.test.AlfrescoTest;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Add Relationship tests
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * @author Kavit Shah
 | 
			
		||||
 */
 | 
			
		||||
public class AddRelationshipTests extends BaseRMRestTest
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,7 @@ import java.time.Instant;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.lang.StringUtils;
 | 
			
		||||
import org.apache.commons.lang3.StringUtils;
 | 
			
		||||
import org.apache.http.HttpEntity;
 | 
			
		||||
import org.apache.http.HttpResponse;
 | 
			
		||||
import org.apache.http.HttpStatus;
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
com.epam.reportportal.testng.ReportPortalTestNGListener
 | 
			
		||||
@@ -23,7 +23,7 @@ Recorded content can be explicitly destroyed whilst maintaining the original nod
 | 
			
		||||
* License: Alfresco Community
 | 
			
		||||
* Issue Tracker Link: [JIRA RM](https://issues.alfresco.com/jira/projects/RM/summary)
 | 
			
		||||
* Contribution Model: Alfresco Closed Source
 | 
			
		||||
* Documentation: [docs.alfresco.com (Records Management)](https://support.hyland.com/r/Alfresco/Alfresco-Governance-Services-Community-Edition/23.4/Alfresco-Governance-Services-Community-Edition/Introduction)
 | 
			
		||||
* Documentation: [docs.alfresco.com (Records Management)](https://support.hyland.com/access?dita:id=job1721300866386&vrm_version=25.1&component=Alfresco%20Governance%20Services%20Community%20Edition)
 | 
			
		||||
 | 
			
		||||
*** 
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,18 +21,18 @@ RM is split into two main parts - a repository integration and a Share integrati
 | 
			
		||||
* [Community License](../LICENSE.txt)
 | 
			
		||||
* [Enterprise License](../../rm-enterprise/LICENSE.txt) (this file will only be present in clones of the Enterprise repository)
 | 
			
		||||
* [Issue Tracker Link](https://issues.alfresco.com/jira/projects/RM)
 | 
			
		||||
* [Community Documentation Link](https://support.hyland.com/r/Alfresco/Alfresco-Governance-Services-Community-Edition/23.4/Alfresco-Governance-Services-Community-Edition/Introduction)
 | 
			
		||||
* [Enterprise Documentation Link](https://support.hyland.com/r/Alfresco/Alfresco-Governance-Services/23.4/Alfresco-Governance-Services/Introduction)
 | 
			
		||||
* [Community Documentation Link](https://support.hyland.com/access?dita:id=job1721300866386&vrm_version=25.1&component=Alfresco%20Governance%20Services%20Community%20Edition)
 | 
			
		||||
* [Enterprise Documentation Link](https://support.hyland.com/access?dita:id=job1721300866386&vrm_version=25.1)
 | 
			
		||||
* [Contribution Model](../../CONTRIBUTING.md)
 | 
			
		||||
 | 
			
		||||
*** 
 | 
			
		||||
 | 
			
		||||
### Prerequisite Knowledge
 | 
			
		||||
An understanding of Alfresco Content Services is assumed. The following pages from the [developer documentation](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services-Community-Edition/23.4/Alfresco-Content-Services-Community-Edition/Develop) give useful background information:
 | 
			
		||||
An understanding of Alfresco Content Services is assumed. The following pages from the [developer documentation](https://support.hyland.com/access?dita:id=lsl1724405261585&vrm_version=25.2&component=Alfresco%20Content%20Services%20Community%20Edition) give useful background information:
 | 
			
		||||
 | 
			
		||||
* [ACS Architecture](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Software-Architecture)
 | 
			
		||||
* [Platform Extensions](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Extension-Points-Overview)
 | 
			
		||||
* [Share Extensions](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Share-UI-Extension-Points)
 | 
			
		||||
* [ACS Architecture](https://support.hyland.com/access?dita:id=lfo1719554691023&vrm_version=25.2)
 | 
			
		||||
* [Platform Extensions](https://support.hyland.com/access?dita:id=ifr1720080387005&vrm_version=25.2)
 | 
			
		||||
* [Share Extensions](https://support.hyland.com/access?dita:id=wqu1720687386891&vrm_version=25.2)
 | 
			
		||||
 | 
			
		||||
*** 
 | 
			
		||||
 | 
			
		||||
@@ -44,12 +44,12 @@ The RM Share module communicates with the repository module via REST APIs. Inter
 | 
			
		||||
* A DAO layer responsible for CRUD operations against the database.
 | 
			
		||||
 | 
			
		||||
#### REST API
 | 
			
		||||
The REST API endpoints fall into two main types - v0 (Webscripts) and v1. The [v0 API](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/In-Process-Platform-Extension-Points/Web-Scripts) is older and not recommended for integrations. The [v1 API](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/REST-API-Guide) is newer but isn't yet feature complete. If you are running RM locally then the GS API Explorer will be available at [this link](http://localhost:8080/gs-api-explorer/).
 | 
			
		||||
The REST API endpoints fall into two main types - v0 (Webscripts) and v1. The [v0 API](https://support.hyland.com/access?dita:id=fga1720080409048&vrm_version=25.2) is older and not recommended for integrations. The [v1 API](https://support.hyland.com/access?dita:id=cmm1721136574006&vrm_version=25.2) is newer but isn't yet feature complete. If you are running RM locally then the GS API Explorer will be available at [this link](http://localhost:8080/gs-api-explorer/).
 | 
			
		||||
 | 
			
		||||
Internally the GS v1 REST API is built on the [Alfresco v1 REST API framework](https://community.alfresco.com/community/ecm/blog/2016/10/11/v1-rest-api-part-1-introduction). It aims to be consistent with this in terms of behaviour and naming.
 | 
			
		||||
 | 
			
		||||
#### Java Public API
 | 
			
		||||
The Java service layer is fronted by a [Java Public API](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Reference/Java-Foundation-API), which we will ensure backward compatible with previous releases. Before we remove any methods there will first be a release containing that method deprecated to allow third party integrations to migrate to a new method.  The Java Public API also includes a set of POJO objects which are needed to communicate with the services. It is easy to identify classes that are part of the Java Public API as they are annotated `@AlfrescoPublicApi`.
 | 
			
		||||
The Java service layer is fronted by a [Java Public API](https://support.hyland.com/access?dita:id=lol1721390191517&vrm_version=25.2), which we will ensure backward compatible with previous releases. Before we remove any methods there will first be a release containing that method deprecated to allow third party integrations to migrate to a new method.  The Java Public API also includes a set of POJO objects which are needed to communicate with the services. It is easy to identify classes that are part of the Java Public API as they are annotated `@AlfrescoPublicApi`.
 | 
			
		||||
 | 
			
		||||
Each Java service will have at least four beans defined for it:
 | 
			
		||||
 | 
			
		||||
@@ -61,7 +61,7 @@ Each Java service will have at least four beans defined for it:
 | 
			
		||||
#### DAOs
 | 
			
		||||
The DAOs are not part of the Java Public API, but handle CRUD operations against RM stored data. We have some custom queries to improve performance for particularly heavy operations.
 | 
			
		||||
 | 
			
		||||
We use standard Alfresco [data modelling](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/In-Process-Platform-Extension-Points/Content-Model-Extension-Point) to store RM metadata. We extend the [Alfresco patching mechanism](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/In-Process-Platform-Extension-Points/Patches) to provide community and enterprise schema upgrades.
 | 
			
		||||
We use standard Alfresco [data modelling](https://support.hyland.com/access?dita:id=ekx1720080373621&vrm_version=25.2) to store RM metadata. We extend the [Alfresco patching mechanism](https://support.hyland.com/access?dita:id=ato1720080396825&vrm_version=25.2) to provide community and enterprise schema upgrades.
 | 
			
		||||
 | 
			
		||||
***
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
   <parent>
 | 
			
		||||
      <groupId>org.alfresco</groupId>
 | 
			
		||||
      <artifactId>alfresco-governance-services-community-parent</artifactId>
 | 
			
		||||
      <version>25.2.0.16</version>
 | 
			
		||||
      <version>25.3.0.62</version>
 | 
			
		||||
   </parent>
 | 
			
		||||
 | 
			
		||||
   <modules>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,3 @@
 | 
			
		||||
SOLR6_TAG=2.0.15
 | 
			
		||||
SOLR6_TAG=2.0.16
 | 
			
		||||
POSTGRES_TAG=16.6
 | 
			
		||||
ACTIVEMQ_TAG=5.18.3-jre17-rockylinux8
 | 
			
		||||
 
 | 
			
		||||
@@ -119,6 +119,11 @@ rm.patch.v35.holdNewChildAssocPatch.batchSize=1000
 | 
			
		||||
rm.haspermissionmap.read=Read
 | 
			
		||||
rm.haspermissionmap.write=WriteProperties,AddChildren,ReadContent
 | 
			
		||||
 | 
			
		||||
# Extended Permissions
 | 
			
		||||
# Enable matching the given username with the correct casing username when retrieving an IPR group.
 | 
			
		||||
# Only needs to be used if there are owners that don't have the username in the correct casing.
 | 
			
		||||
rm.extendedSecurity.enableUsernameNormalization=false
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Extended auto-version behaviour.  If true and other auto-version properties are satisfied, then
 | 
			
		||||
# a document will be auto-versioned when its type is changed.
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
 
 | 
			
		||||
@@ -78,6 +78,12 @@
 | 
			
		||||
      <property name="transactionService" ref="transactionService" />
 | 
			
		||||
   </bean>
 | 
			
		||||
 | 
			
		||||
    <bean class="org.alfresco.rm.rest.api.fileplans.FilePlanRolesRelation">
 | 
			
		||||
        <property name="apiUtils" ref="apiUtils" />
 | 
			
		||||
        <property name="rmRoles" ref="rm.roles" />
 | 
			
		||||
        <property name="filePlanService" ref="FilePlanService" />
 | 
			
		||||
    </bean>
 | 
			
		||||
 | 
			
		||||
   <bean class="org.alfresco.rm.rest.api.holds.HoldsEntityResource" >
 | 
			
		||||
      <property name="holdService" ref="HoldService" />
 | 
			
		||||
      <property name="apiUtils" ref="apiUtils" />
 | 
			
		||||
@@ -228,6 +234,11 @@
 | 
			
		||||
        <property name="siteSurfConfig" ref="rm.siteSurfConfig" />
 | 
			
		||||
   </bean>
 | 
			
		||||
 | 
			
		||||
    <bean id="rm.roles" class="org.alfresco.rm.rest.api.impl.RMRolesImpl">
 | 
			
		||||
        <property name="nodesModelFactory" ref="nodesModelFactory" />
 | 
			
		||||
        <property name="filePlanRoleService" ref="FilePlanRoleService"/>
 | 
			
		||||
    </bean>
 | 
			
		||||
 | 
			
		||||
    <bean id="rm.siteSurfConfig" class="org.alfresco.rest.api.impl.SiteSurfConfig">
 | 
			
		||||
        <property name="configPath" value="alfresco/module/org_alfresco_module_rm/bootstrap/site"/>
 | 
			
		||||
    </bean>
 | 
			
		||||
@@ -280,4 +291,4 @@
 | 
			
		||||
      <property name="beanName" value="restJsonModule" />
 | 
			
		||||
      <property name="extendingBeanName" value="rm.restJsonModule" />
 | 
			
		||||
   </bean>
 | 
			
		||||
</beans>
 | 
			
		||||
</beans>
 | 
			
		||||
 
 | 
			
		||||
@@ -611,6 +611,7 @@
 | 
			
		||||
        <property name="authorityService" ref="authorityService"/>
 | 
			
		||||
        <property name="permissionService" ref="permissionService"/>
 | 
			
		||||
        <property name="transactionService" ref="transactionService"/>
 | 
			
		||||
        <property name="enableUsernameNormalization" value="${rm.extendedSecurity.enableUsernameNormalization}" />
 | 
			
		||||
    </bean>
 | 
			
		||||
 | 
			
		||||
    <bean id="ExtendedSecurityService" class="org.springframework.aop.framework.ProxyFactoryBean">
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
   <parent>
 | 
			
		||||
      <groupId>org.alfresco</groupId>
 | 
			
		||||
      <artifactId>alfresco-governance-services-community-repo-parent</artifactId>
 | 
			
		||||
      <version>25.2.0.16</version>
 | 
			
		||||
      <version>25.3.0.62</version>
 | 
			
		||||
   </parent>
 | 
			
		||||
 | 
			
		||||
   <properties>
 | 
			
		||||
@@ -84,11 +84,6 @@
 | 
			
		||||
         <artifactId>junit</artifactId>
 | 
			
		||||
         <scope>test</scope>
 | 
			
		||||
      </dependency>
 | 
			
		||||
      <dependency>
 | 
			
		||||
         <groupId>com.epam.reportportal</groupId>
 | 
			
		||||
         <artifactId>agent-java-testng</artifactId>
 | 
			
		||||
         <scope>test</scope>
 | 
			
		||||
      </dependency>
 | 
			
		||||
      <dependency>
 | 
			
		||||
         <groupId>org.postgresql</groupId>
 | 
			
		||||
         <artifactId>postgresql</artifactId>
 | 
			
		||||
 
 | 
			
		||||
@@ -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())
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,13 +31,16 @@ import static org.alfresco.service.cmr.security.PermissionService.GROUP_PREFIX;
 | 
			
		||||
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.LinkedHashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
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;
 | 
			
		||||
import org.alfresco.model.RenditionModel;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
 | 
			
		||||
@@ -96,6 +99,8 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
 | 
			
		||||
    /** transaction service */
 | 
			
		||||
    private TransactionService transactionService;
 | 
			
		||||
 | 
			
		||||
    private boolean enableUsernameNormalization;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param filePlanService
 | 
			
		||||
     *            file plan service
 | 
			
		||||
@@ -141,6 +146,15 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
 | 
			
		||||
        this.transactionService = transactionService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param enableUsernameNormalization
 | 
			
		||||
     *            enable username normalization to ensure correct casing
 | 
			
		||||
     */
 | 
			
		||||
    public void setEnableUsernameNormalization(boolean enableUsernameNormalization)
 | 
			
		||||
    {
 | 
			
		||||
        this.enableUsernameNormalization = enableUsernameNormalization;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Application context refresh event handler
 | 
			
		||||
     */
 | 
			
		||||
@@ -234,7 +248,7 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
 | 
			
		||||
     */
 | 
			
		||||
    private Set<String> getAuthorities(String group)
 | 
			
		||||
    {
 | 
			
		||||
        Set<String> result = new HashSet<>();
 | 
			
		||||
        Set<String> result = new LinkedHashSet<>();
 | 
			
		||||
        result.addAll(authorityService.getContainedAuthorities(null, group, true));
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
@@ -359,12 +373,18 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
 | 
			
		||||
    {
 | 
			
		||||
        String group = null;
 | 
			
		||||
 | 
			
		||||
        // If enabled, the authorities are forced to match the correct casing of the usernames in case they were set
 | 
			
		||||
        // with the incorrect casing.
 | 
			
		||||
        // If not, it will just use the authorities as they are.
 | 
			
		||||
        // In normal circumstances, the authorities are in the correct casing, so this is disabled by default.
 | 
			
		||||
        Set<String> authoritySet = normalizeAuthorities(authorities);
 | 
			
		||||
 | 
			
		||||
        // find group or determine what the next index is if no group exists or there is a clash
 | 
			
		||||
        Pair<String, Integer> groupResult = findIPRGroup(groupPrefix, authorities);
 | 
			
		||||
        Pair<String, Integer> groupResult = findIPRGroup(groupPrefix, authoritySet);
 | 
			
		||||
 | 
			
		||||
        if (groupResult.getFirst() == null)
 | 
			
		||||
        {
 | 
			
		||||
            group = createIPRGroup(groupPrefix, authorities, groupResult.getSecond());
 | 
			
		||||
            group = createIPRGroup(groupPrefix, authoritySet, groupResult.getSecond());
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
@@ -399,7 +419,8 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
 | 
			
		||||
        while (hasMoreItems == true)
 | 
			
		||||
        {
 | 
			
		||||
            // get matching authorities
 | 
			
		||||
            PagingResults<String> results = authorityService.getAuthorities(AuthorityType.GROUP,
 | 
			
		||||
            PagingResults<String> results = authorityService.getAuthorities(
 | 
			
		||||
                    AuthorityType.GROUP,
 | 
			
		||||
                    RMAuthority.ZONE_APP_RM,
 | 
			
		||||
                    groupShortNamePrefix,
 | 
			
		||||
                    false,
 | 
			
		||||
@@ -427,6 +448,63 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
 | 
			
		||||
        return new Pair<>(iprGroup, nextGroupIndex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Given a set of authorities, normalizes the authority names to ensure correct casing.
 | 
			
		||||
     * 
 | 
			
		||||
     * @param authNames
 | 
			
		||||
     * @return
 | 
			
		||||
     */
 | 
			
		||||
    private Set<String> normalizeAuthorities(Set<String> authNames)
 | 
			
		||||
    {
 | 
			
		||||
        // If disabled or no authorities, return as is
 | 
			
		||||
        if (!enableUsernameNormalization || authNames == null || authNames.isEmpty())
 | 
			
		||||
        {
 | 
			
		||||
            return authNames;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Set<String> normalizedAuthorities = new HashSet<>();
 | 
			
		||||
        for (String authorityName : authNames)
 | 
			
		||||
        {
 | 
			
		||||
            normalizedAuthorities.add(normalizeAuthorityName(authorityName));
 | 
			
		||||
        }
 | 
			
		||||
        return normalizedAuthorities;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Usernames are case insensitive but affect the IPR group matching when set with different casing. For a given authority of type user, this method normalizes the authority name. If group, it returns the name as-is.
 | 
			
		||||
     *
 | 
			
		||||
     * @param authorityName
 | 
			
		||||
     *            the authority name to normalize
 | 
			
		||||
     * @return the normalized authority name
 | 
			
		||||
     */
 | 
			
		||||
    private String normalizeAuthorityName(String authorityName)
 | 
			
		||||
    {
 | 
			
		||||
        if (authorityName == null || authorityName.startsWith(GROUP_PREFIX))
 | 
			
		||||
        {
 | 
			
		||||
            return authorityName;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // For users, attempt to get the correct casing from the username property of the user node
 | 
			
		||||
        if (authorityService.authorityExists(authorityName))
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                NodeRef authorityNodeRef = authorityService.getAuthorityNodeRef(authorityName);
 | 
			
		||||
                if (authorityNodeRef != null)
 | 
			
		||||
                {
 | 
			
		||||
                    String username = (String) nodeService.getProperty(authorityNodeRef, ContentModel.PROP_USERNAME);
 | 
			
		||||
                    return username != null ? username : authorityName;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception e)
 | 
			
		||||
            {
 | 
			
		||||
                // If anything goes wrong, fallback to the original name
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return authorityName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determines whether a group exactly matches a list of authorities.
 | 
			
		||||
     *
 | 
			
		||||
@@ -573,8 +651,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;
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,7 @@ import java.io.Serializable;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.LinkedHashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
@@ -325,8 +326,8 @@ public class ExtendedPermissionServiceImpl extends PermissionServiceImpl impleme
 | 
			
		||||
            return aclReaders;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        HashSet<String> assigned = new HashSet<>();
 | 
			
		||||
        HashSet<String> readers = new HashSet<>();
 | 
			
		||||
        Set<String> assigned = new LinkedHashSet<>();
 | 
			
		||||
        Set<String> readers = new LinkedHashSet<>();
 | 
			
		||||
 | 
			
		||||
        for (AccessControlEntry ace : acl.getEntries())
 | 
			
		||||
        {
 | 
			
		||||
@@ -412,8 +413,8 @@ public class ExtendedPermissionServiceImpl extends PermissionServiceImpl impleme
 | 
			
		||||
            return aclWriters;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        HashSet<String> assigned = new HashSet<>();
 | 
			
		||||
        HashSet<String> readers = new HashSet<>();
 | 
			
		||||
        Set<String> assigned = new LinkedHashSet<>();
 | 
			
		||||
        Set<String> readers = new LinkedHashSet<>();
 | 
			
		||||
 | 
			
		||||
        for (AccessControlEntry ace : acl.getEntries())
 | 
			
		||||
        {
 | 
			
		||||
@@ -485,7 +486,7 @@ public class ExtendedPermissionServiceImpl extends PermissionServiceImpl impleme
 | 
			
		||||
        Set<String> writers = getWriters(aclId);
 | 
			
		||||
 | 
			
		||||
        // add the current owner to the list of extended writers
 | 
			
		||||
        Set<String> modifiedWrtiers = new HashSet<>(writers);
 | 
			
		||||
        Set<String> modifiedWrtiers = new LinkedHashSet<>(writers);
 | 
			
		||||
        String owner = ownableService.getOwner(nodeRef);
 | 
			
		||||
        if (StringUtils.isNotBlank(owner) &&
 | 
			
		||||
                !owner.equals(OwnableService.NO_OWNER) &&
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,56 @@
 | 
			
		||||
/*
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Records Management Module
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * -
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * -
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
package org.alfresco.rm.rest.api;
 | 
			
		||||
 | 
			
		||||
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
 | 
			
		||||
import org.alfresco.rest.framework.resource.parameters.Parameters;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.RoleModel;
 | 
			
		||||
import org.alfresco.service.cmr.repository.NodeRef;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * RM Roles API
 | 
			
		||||
 */
 | 
			
		||||
public interface RMRoles
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    String PARAM_INCLUDE_ASSIGNED_USERS = "assignedUsers";
 | 
			
		||||
    String PARAM_INCLUDE_ASSIGNED_GROUPS = "assignedGroups";
 | 
			
		||||
    String PARAM_INCLUDE_SYSTEM_ROLES = "systemRoles";
 | 
			
		||||
    String PARAM_CAPABILITY_NAME = "capabilityName";
 | 
			
		||||
    String PARAM_PERSON_ID = "personId";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets a list of roles.
 | 
			
		||||
     *
 | 
			
		||||
     * @param filePlan
 | 
			
		||||
     *            the file plan node reference
 | 
			
		||||
     * @param parameters
 | 
			
		||||
     *            the {@link Parameters} object to get the parameters passed into the request including: - filter, sort & paging params (where, orderBy, skipCount, maxItems) - include param (personId, includeSystemRoles)
 | 
			
		||||
     * @return a paged list of {@code org.alfresco.rm.rest.api.model.RoleModel} objects
 | 
			
		||||
     */
 | 
			
		||||
    CollectionWithPagingInfo<RoleModel> getRoles(NodeRef filePlan, Parameters parameters);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,96 @@
 | 
			
		||||
/*
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Records Management Module
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * -
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * -
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
package org.alfresco.rm.rest.api.fileplans;
 | 
			
		||||
 | 
			
		||||
import static org.alfresco.util.ParameterCheck.mandatory;
 | 
			
		||||
 | 
			
		||||
import org.springframework.beans.factory.InitializingBean;
 | 
			
		||||
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
 | 
			
		||||
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
 | 
			
		||||
import org.alfresco.rest.framework.resource.RelationshipResource;
 | 
			
		||||
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
 | 
			
		||||
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
 | 
			
		||||
import org.alfresco.rest.framework.resource.parameters.Parameters;
 | 
			
		||||
import org.alfresco.rm.rest.api.RMRoles;
 | 
			
		||||
import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.RoleModel;
 | 
			
		||||
import org.alfresco.service.cmr.repository.NodeRef;
 | 
			
		||||
 | 
			
		||||
@RelationshipResource(name = "roles", entityResource = FilePlanEntityResource.class, title = "Roles in a file plan")
 | 
			
		||||
public class FilePlanRolesRelation implements RelationshipResourceAction.Read<RoleModel>, InitializingBean
 | 
			
		||||
{
 | 
			
		||||
    private RMRoles rmRoles;
 | 
			
		||||
    private FilePlanService filePlanService;
 | 
			
		||||
    private FilePlanComponentsApiUtils apiUtils;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void afterPropertiesSet() throws Exception
 | 
			
		||||
    {
 | 
			
		||||
        mandatory("rmRoles", this.rmRoles);
 | 
			
		||||
        mandatory("apiUtils", this.apiUtils);
 | 
			
		||||
        mandatory("filePlanService", this.filePlanService);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public CollectionWithPagingInfo<RoleModel> readAll(String filePlanId, Parameters params)
 | 
			
		||||
    {
 | 
			
		||||
        NodeRef filePlanNodeRef = getFilePlan(filePlanId);
 | 
			
		||||
        if (filePlanNodeRef == null)
 | 
			
		||||
        {
 | 
			
		||||
            throw new EntityNotFoundException(filePlanId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return rmRoles.getRoles(filePlanNodeRef, params);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private NodeRef getFilePlan(String filePlanId)
 | 
			
		||||
    {
 | 
			
		||||
        NodeRef filePlanNodeRef = apiUtils.lookupAndValidateNodeType(filePlanId, RecordsManagementModel.TYPE_FILE_PLAN);
 | 
			
		||||
        if (!FilePlanComponentsApiUtils.FILE_PLAN_ALIAS.equals(filePlanId))
 | 
			
		||||
        {
 | 
			
		||||
            filePlanNodeRef = filePlanService.getFilePlan(filePlanNodeRef);
 | 
			
		||||
        }
 | 
			
		||||
        return filePlanNodeRef;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setRmRoles(RMRoles rmRoles)
 | 
			
		||||
    {
 | 
			
		||||
        this.rmRoles = rmRoles;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
 | 
			
		||||
    {
 | 
			
		||||
        this.apiUtils = apiUtils;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setFilePlanService(FilePlanService filePlanService)
 | 
			
		||||
    {
 | 
			
		||||
        this.filePlanService = filePlanService;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -30,6 +30,7 @@ package org.alfresco.rm.rest.api.impl;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Comparator;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
@@ -42,12 +43,15 @@ import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import org.alfresco.model.ContentModel;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.capability.Capability;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.capability.Group;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinitionImpl;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.role.Role;
 | 
			
		||||
import org.alfresco.rest.api.Nodes;
 | 
			
		||||
import org.alfresco.rest.api.model.AssocChild;
 | 
			
		||||
import org.alfresco.rest.api.model.ContentInfo;
 | 
			
		||||
@@ -55,7 +59,9 @@ import org.alfresco.rest.api.model.Node;
 | 
			
		||||
import org.alfresco.rest.api.model.UserInfo;
 | 
			
		||||
import org.alfresco.rest.framework.jacksonextensions.BeanPropertiesFilter;
 | 
			
		||||
import org.alfresco.rest.framework.resource.parameters.Parameters;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.CapabilityModel;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.FilePlan;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.GroupModel;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.HoldModel;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.RMNode;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.Record;
 | 
			
		||||
@@ -66,6 +72,7 @@ import org.alfresco.rm.rest.api.model.RetentionPeriod;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.RetentionSchedule;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.RetentionScheduleActionDefinition;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.RetentionSteps;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.RoleModel;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.Transfer;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.TransferChild;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.TransferContainer;
 | 
			
		||||
@@ -696,6 +703,36 @@ public class ApiNodesModelFactory
 | 
			
		||||
                (String) info.getProperties().get(RecordsManagementModel.PROP_HOLD_REASON));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public RoleModel createRoleModel(Role role, List<String> assignedUsers, List<String> assignedGroups)
 | 
			
		||||
    {
 | 
			
		||||
        return new RoleModel(role.getName(),
 | 
			
		||||
                role.getDisplayLabel(),
 | 
			
		||||
                role.getCapabilities()
 | 
			
		||||
                        .stream()
 | 
			
		||||
                        .map(this::createCapabilityModel)
 | 
			
		||||
                        .sorted(Comparator.comparing(CapabilityModel::name))
 | 
			
		||||
                        .toList(),
 | 
			
		||||
                role.getRoleGroupName(),
 | 
			
		||||
                role.getGroupShortName(),
 | 
			
		||||
                assignedUsers,
 | 
			
		||||
                assignedGroups);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public CapabilityModel createCapabilityModel(Capability capability)
 | 
			
		||||
    {
 | 
			
		||||
        return new CapabilityModel(capability.getName(), capability.getTitle(), capability.getDescription(),
 | 
			
		||||
                createGroupModel(capability.getGroup()), capability.getIndex());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public GroupModel createGroupModel(Group group)
 | 
			
		||||
    {
 | 
			
		||||
        if (group == null)
 | 
			
		||||
        {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return new GroupModel(group.getId(), group.getTitle());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates an object of type FilePlan
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,264 @@
 | 
			
		||||
/*
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Records Management Module
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * -
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * -
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
package org.alfresco.rm.rest.api.impl;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Comparator;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.LinkedList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.function.Predicate;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.role.Role;
 | 
			
		||||
import org.alfresco.rest.antlr.WhereClauseParser;
 | 
			
		||||
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
 | 
			
		||||
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
 | 
			
		||||
import org.alfresco.rest.framework.resource.parameters.Parameters;
 | 
			
		||||
import org.alfresco.rest.framework.resource.parameters.where.Query;
 | 
			
		||||
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
 | 
			
		||||
import org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker;
 | 
			
		||||
import org.alfresco.rm.rest.api.RMRoles;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.RoleModel;
 | 
			
		||||
import org.alfresco.service.cmr.repository.NodeRef;
 | 
			
		||||
 | 
			
		||||
public class RMRolesImpl implements RMRoles
 | 
			
		||||
{
 | 
			
		||||
    private ApiNodesModelFactory nodesModelFactory;
 | 
			
		||||
    private FilePlanRoleService filePlanRoleService;
 | 
			
		||||
 | 
			
		||||
    private static final Set<String> LIST_ROLES_QUERY_PROPERTIES = new HashSet<>(List.of(PARAM_PERSON_ID, PARAM_INCLUDE_SYSTEM_ROLES, PARAM_CAPABILITY_NAME));
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public CollectionWithPagingInfo<RoleModel> getRoles(NodeRef filePlan, Parameters parameters)
 | 
			
		||||
    {
 | 
			
		||||
        var rolesFilter = getRolesFilter(parameters.getQuery());
 | 
			
		||||
        var roles = getRolesByFilter(filePlan, rolesFilter);
 | 
			
		||||
 | 
			
		||||
        var filteredRoles = roles.stream()
 | 
			
		||||
                .map(role -> createRoleModel(filePlan, role, parameters.getInclude()))
 | 
			
		||||
                .filter(hasRoleCapabilities(rolesFilter.getCapabilities()))
 | 
			
		||||
                .toList();
 | 
			
		||||
        var page = filteredRoles
 | 
			
		||||
                .stream()
 | 
			
		||||
                .sorted(Comparator.comparing(RoleModel::name))
 | 
			
		||||
                .skip(parameters.getPaging().getSkipCount())
 | 
			
		||||
                .limit(parameters.getPaging().getMaxItems())
 | 
			
		||||
                .collect(Collectors.toCollection(LinkedList::new));
 | 
			
		||||
 | 
			
		||||
        int totalItems = filteredRoles.size();
 | 
			
		||||
        boolean hasMore = parameters.getPaging().getSkipCount() + parameters.getPaging().getMaxItems() < totalItems;
 | 
			
		||||
        return CollectionWithPagingInfo.asPaged(parameters.getPaging(), page, hasMore, totalItems);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Predicate<RoleModel> hasRoleCapabilities(List<String> capabilities)
 | 
			
		||||
    {
 | 
			
		||||
        return role -> capabilities == null ||
 | 
			
		||||
                capabilities.isEmpty() ||
 | 
			
		||||
                role.capabilities().stream().anyMatch(capability -> capabilities.contains(capability.name()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Set<Role> getRolesByFilter(NodeRef filePlan, RolesFilter rolesFilter)
 | 
			
		||||
    {
 | 
			
		||||
        if (rolesFilter.getPersonId() != null)
 | 
			
		||||
        {
 | 
			
		||||
            return filePlanRoleService.getRolesByUser(filePlan, rolesFilter.getPersonId(), rolesFilter.includeSystemRoles());
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return filePlanRoleService.getRoles(filePlan, rolesFilter.includeSystemRoles());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private RoleModel createRoleModel(NodeRef filePlan, Role role, List<String> include)
 | 
			
		||||
    {
 | 
			
		||||
        List<String> assignedUsers = getAssignedUsers(filePlan, role, include);
 | 
			
		||||
        List<String> assignedGroups = getAssignedGroups(filePlan, role, include);
 | 
			
		||||
 | 
			
		||||
        return nodesModelFactory.createRoleModel(role, assignedUsers, assignedGroups);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private List<String> getAssignedUsers(NodeRef filePlan, Role role, List<String> include)
 | 
			
		||||
    {
 | 
			
		||||
        if (include != null && include.contains(PARAM_INCLUDE_ASSIGNED_USERS))
 | 
			
		||||
        {
 | 
			
		||||
            return new ArrayList<>(filePlanRoleService.getAllAssignedToRole(filePlan, role.getName()));
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private List<String> getAssignedGroups(NodeRef filePlan, Role role, List<String> include)
 | 
			
		||||
    {
 | 
			
		||||
        if (include != null && include.contains(PARAM_INCLUDE_ASSIGNED_GROUPS))
 | 
			
		||||
        {
 | 
			
		||||
            return new ArrayList<>(filePlanRoleService.getGroupsAssignedToRole(filePlan, role.getName()));
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
 | 
			
		||||
    {
 | 
			
		||||
        this.nodesModelFactory = nodesModelFactory;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setFilePlanRoleService(FilePlanRoleService filePlanRoleService)
 | 
			
		||||
    {
 | 
			
		||||
        this.filePlanRoleService = filePlanRoleService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private RolesFilter getRolesFilter(Query queryParameters)
 | 
			
		||||
    {
 | 
			
		||||
        var rolesFilterBuilder = RolesFilter.builder();
 | 
			
		||||
 | 
			
		||||
        if (queryParameters != null)
 | 
			
		||||
        {
 | 
			
		||||
            var propertyWalker = new RolesQueryWalker();
 | 
			
		||||
            QueryHelper.walk(queryParameters, propertyWalker);
 | 
			
		||||
 | 
			
		||||
            rolesFilterBuilder
 | 
			
		||||
                    .withPersonId(propertyWalker.getPersonId())
 | 
			
		||||
                    .withCapabilities(propertyWalker.getCapabilitiesNames())
 | 
			
		||||
                    .withIncludeSystemRoles(propertyWalker.includeSystemRoles());
 | 
			
		||||
        }
 | 
			
		||||
        return rolesFilterBuilder.build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class RolesQueryWalker extends MapBasedQueryWalker
 | 
			
		||||
    {
 | 
			
		||||
        private List<String> capabilitiesNames;
 | 
			
		||||
 | 
			
		||||
        public RolesQueryWalker()
 | 
			
		||||
        {
 | 
			
		||||
            super(LIST_ROLES_QUERY_PROPERTIES, null);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void in(String propertyName, boolean negated, String... propertyValues)
 | 
			
		||||
        {
 | 
			
		||||
            if (negated)
 | 
			
		||||
            {
 | 
			
		||||
                throw new InvalidArgumentException("Cannot use NOT for " + propertyName);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (PARAM_CAPABILITY_NAME.equalsIgnoreCase(propertyName))
 | 
			
		||||
            {
 | 
			
		||||
                capabilitiesNames = Arrays.asList(propertyValues);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void and()
 | 
			
		||||
        {
 | 
			
		||||
            // allow AND, e.g. personId='123' AND includeSystemRoles=true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public List<String> getCapabilitiesNames()
 | 
			
		||||
        {
 | 
			
		||||
            return this.capabilitiesNames;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public String getPersonId()
 | 
			
		||||
        {
 | 
			
		||||
            return getProperty(PARAM_PERSON_ID, WhereClauseParser.EQUALS, String.class);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Boolean includeSystemRoles()
 | 
			
		||||
        {
 | 
			
		||||
            return getProperty(PARAM_INCLUDE_SYSTEM_ROLES, WhereClauseParser.EQUALS, Boolean.class);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class RolesFilter
 | 
			
		||||
{
 | 
			
		||||
    private String personId;
 | 
			
		||||
    private boolean includeSystemRoles;
 | 
			
		||||
    private List<String> capabilities;
 | 
			
		||||
 | 
			
		||||
    private RolesFilter()
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    public static RolesFilterBuilder builder()
 | 
			
		||||
    {
 | 
			
		||||
        return new RolesFilterBuilder();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getPersonId()
 | 
			
		||||
    {
 | 
			
		||||
        return personId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean includeSystemRoles()
 | 
			
		||||
    {
 | 
			
		||||
        return includeSystemRoles;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public List<String> getCapabilities()
 | 
			
		||||
    {
 | 
			
		||||
        return capabilities;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class RolesFilterBuilder
 | 
			
		||||
    {
 | 
			
		||||
        private String personId;
 | 
			
		||||
        private boolean includeSystemRoles = true;
 | 
			
		||||
        private List<String> capabilities;
 | 
			
		||||
 | 
			
		||||
        public RolesFilterBuilder withPersonId(String personId)
 | 
			
		||||
        {
 | 
			
		||||
            this.personId = personId;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public RolesFilterBuilder withIncludeSystemRoles(Boolean includeSystemRoles)
 | 
			
		||||
        {
 | 
			
		||||
            if (includeSystemRoles != null)
 | 
			
		||||
            {
 | 
			
		||||
                this.includeSystemRoles = includeSystemRoles;
 | 
			
		||||
            }
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public RolesFilterBuilder withCapabilities(List<String> capabilities)
 | 
			
		||||
        {
 | 
			
		||||
            this.capabilities = capabilities;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public RolesFilter build()
 | 
			
		||||
        {
 | 
			
		||||
            RolesFilter rolesFilter = new RolesFilter();
 | 
			
		||||
            rolesFilter.personId = this.personId;
 | 
			
		||||
            rolesFilter.includeSystemRoles = this.includeSystemRoles;
 | 
			
		||||
            rolesFilter.capabilities = this.capabilities;
 | 
			
		||||
            return rolesFilter;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,30 @@
 | 
			
		||||
/*
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Records Management Module
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * -
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * -
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
package org.alfresco.rm.rest.api.model;
 | 
			
		||||
 | 
			
		||||
public record CapabilityModel(String name, String title, String description, GroupModel group, int index)
 | 
			
		||||
{}
 | 
			
		||||
@@ -0,0 +1,30 @@
 | 
			
		||||
/*
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Records Management Module
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * -
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * -
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
package org.alfresco.rm.rest.api.model;
 | 
			
		||||
 | 
			
		||||
public record GroupModel(String id, String title)
 | 
			
		||||
{}
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
/*
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Records Management Module
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * -
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * -
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
package org.alfresco.rm.rest.api.model;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public record RoleModel(String name, String displayLabel, List<CapabilityModel> capabilities, String roleGroupName, String groupShortName, List<String> assignedUsers, List<String> assignedGroups)
 | 
			
		||||
{}
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
/*
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Records Management Module
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * -
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * -
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
package org.alfresco.rm.rest.api.model;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public record RoleModelList(List<RoleModel> roleModelList)
 | 
			
		||||
{}
 | 
			
		||||
@@ -0,0 +1,37 @@
 | 
			
		||||
/*
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Records Management Module
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * -
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * -
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Package info that defines the Information Governance Roles REST API
 | 
			
		||||
 *
 | 
			
		||||
 * @author Damian Ujma
 | 
			
		||||
 */
 | 
			
		||||
@WebApi(name = "gs", scope = Api.SCOPE.PUBLIC, version = 1)
 | 
			
		||||
package org.alfresco.rm.rest.api.roles;
 | 
			
		||||
 | 
			
		||||
import org.alfresco.rest.framework.Api;
 | 
			
		||||
import org.alfresco.rest.framework.WebApi;
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
com.epam.reportportal.testng.ReportPortalTestNGListener
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
 | 
			
		||||
# Version label
 | 
			
		||||
version.major=25
 | 
			
		||||
version.minor=2
 | 
			
		||||
version.minor=3
 | 
			
		||||
version.revision=0
 | 
			
		||||
version.label=
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -52,6 +52,7 @@ import java.util.Set;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
import java.util.stream.Stream;
 | 
			
		||||
 | 
			
		||||
import org.alfresco.model.ContentModel;
 | 
			
		||||
import org.alfresco.model.RenditionModel;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
 | 
			
		||||
@@ -67,6 +68,7 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransacti
 | 
			
		||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
 | 
			
		||||
import org.alfresco.service.cmr.repository.NodeRef;
 | 
			
		||||
import org.alfresco.service.cmr.repository.NodeService;
 | 
			
		||||
import org.alfresco.service.cmr.repository.StoreRef;
 | 
			
		||||
import org.alfresco.service.cmr.security.AccessPermission;
 | 
			
		||||
import org.alfresco.service.cmr.security.AccessStatus;
 | 
			
		||||
import org.alfresco.service.cmr.security.AuthorityService;
 | 
			
		||||
@@ -522,6 +524,104 @@ public class ExtendedSecurityServiceImplUnitTest
 | 
			
		||||
        verify(mockedPermissionService).setPermission(nodeRef, writeGroup, RMPermissionModel.FILING, true);
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Given a node with no previous IPR groups assigned
 | 
			
		||||
     * And having pre-existing IPR groups matching the ones we need
 | 
			
		||||
     * When I add some read and write authorities but with a different casing
 | 
			
		||||
     * Then the existing IPR groups are used
 | 
			
		||||
     */
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    @Test public void addExtendedSecurityWithMixedCasingUsernames()
 | 
			
		||||
    {
 | 
			
		||||
        // Have the usernames in the node as the correct usernames but with incorrect casing
 | 
			
		||||
        String user1 = "UseR";
 | 
			
		||||
        String user2 = "UseR_w";
 | 
			
		||||
 | 
			
		||||
        // Incorrect IPR Group names
 | 
			
		||||
        Set<String> diffCasingReaders = Stream.of(user1, GROUP).collect(Collectors.toSet());
 | 
			
		||||
        Set<String> diffCasingWriters = Stream.of(user2, GROUP_W).collect(Collectors.toSet());
 | 
			
		||||
        String wrongReadGroupPrefix = extendedSecurityService.getIPRGroupPrefixShortName(READER_GROUP_PREFIX, diffCasingReaders);
 | 
			
		||||
        String wrongWriteGroupPrefix = extendedSecurityService.getIPRGroupPrefixShortName(WRITER_GROUP_PREFIX, diffCasingWriters);
 | 
			
		||||
        String wrongReadGroup = wrongReadGroupPrefix + "0";
 | 
			
		||||
        String wrongWriteGroup = wrongWriteGroupPrefix + "0";
 | 
			
		||||
 | 
			
		||||
        // Correct Group names
 | 
			
		||||
        String correctReadGroup = readGroupPrefix + "0";
 | 
			
		||||
        String correctWriteGroup = writeGroupPrefix + "0";
 | 
			
		||||
 | 
			
		||||
        // If queried for the correct groups, return the results
 | 
			
		||||
        PagingResults<String> mockedCorrectReadPResults = mock(PagingResults.class);
 | 
			
		||||
        PagingResults<String> mockedCorrectWritePResults = mock(PagingResults.class);
 | 
			
		||||
        when(mockedCorrectReadPResults.getPage())
 | 
			
		||||
            .thenReturn(Stream.of(GROUP_PREFIX + correctReadGroup).collect(Collectors.toList()));
 | 
			
		||||
        when(mockedAuthorityService.getAuthorities(
 | 
			
		||||
                eq(AuthorityType.GROUP), 
 | 
			
		||||
                eq(RMAuthority.ZONE_APP_RM), 
 | 
			
		||||
                eq(readGroupPrefix),
 | 
			
		||||
                eq(false), 
 | 
			
		||||
                eq(false), 
 | 
			
		||||
                any(PagingRequest.class)))
 | 
			
		||||
            .thenReturn(mockedCorrectReadPResults);
 | 
			
		||||
        
 | 
			
		||||
        when(mockedCorrectWritePResults.getPage())
 | 
			
		||||
            .thenReturn(Stream.of(GROUP_PREFIX + correctWriteGroup).collect(Collectors.toList()));
 | 
			
		||||
        when(mockedAuthorityService.getAuthorities(
 | 
			
		||||
                eq(AuthorityType.GROUP), 
 | 
			
		||||
                eq(RMAuthority.ZONE_APP_RM), 
 | 
			
		||||
                eq(writeGroupPrefix),
 | 
			
		||||
                eq(false), 
 | 
			
		||||
                eq(false), 
 | 
			
		||||
                any(PagingRequest.class)))
 | 
			
		||||
            .thenReturn(mockedCorrectWritePResults);
 | 
			
		||||
 | 
			
		||||
        // Don't return results for the incorrect groups (lenient as these may not be called with normalization enabled)
 | 
			
		||||
        PagingResults<String> mockedWrongReadPResults = mock(PagingResults.class);
 | 
			
		||||
        PagingResults<String> mockedWrongWritePResults = mock(PagingResults.class);
 | 
			
		||||
        lenient().when(mockedWrongReadPResults.getPage())
 | 
			
		||||
            .thenReturn(Collections.emptyList());
 | 
			
		||||
        lenient().when(mockedAuthorityService.getAuthorities(
 | 
			
		||||
                eq(AuthorityType.GROUP), 
 | 
			
		||||
                eq(RMAuthority.ZONE_APP_RM), 
 | 
			
		||||
                eq(wrongReadGroupPrefix),
 | 
			
		||||
                eq(false), 
 | 
			
		||||
                eq(false), 
 | 
			
		||||
                any(PagingRequest.class)))
 | 
			
		||||
            .thenReturn(mockedWrongReadPResults);
 | 
			
		||||
        
 | 
			
		||||
        lenient().when(mockedWrongWritePResults.getPage())
 | 
			
		||||
            .thenReturn(Collections.emptyList());
 | 
			
		||||
        lenient().when(mockedAuthorityService.getAuthorities(
 | 
			
		||||
                eq(AuthorityType.GROUP), 
 | 
			
		||||
                eq(RMAuthority.ZONE_APP_RM), 
 | 
			
		||||
                eq(wrongWriteGroupPrefix),
 | 
			
		||||
                eq(false), 
 | 
			
		||||
                eq(false), 
 | 
			
		||||
                any(PagingRequest.class)))
 | 
			
		||||
            .thenReturn(mockedWrongWritePResults);
 | 
			
		||||
 | 
			
		||||
        // The users do exist, despite being in a different casing and are able to be retrieved
 | 
			
		||||
        NodeRef noderefUser1 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, USER);
 | 
			
		||||
        when(mockedAuthorityService.authorityExists(user1)).thenReturn(true);
 | 
			
		||||
        when(mockedAuthorityService.getAuthorityNodeRef(user1)).thenReturn(noderefUser1);
 | 
			
		||||
        when(mockedNodeService.getProperty(noderefUser1, ContentModel.PROP_USERNAME)).thenReturn(USER);
 | 
			
		||||
 | 
			
		||||
        NodeRef noderefUser2 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, USER_W);
 | 
			
		||||
        when(mockedAuthorityService.authorityExists(user2)).thenReturn(true);
 | 
			
		||||
        when(mockedAuthorityService.getAuthorityNodeRef(user2)).thenReturn(noderefUser2);
 | 
			
		||||
        when(mockedNodeService.getProperty(noderefUser2, ContentModel.PROP_USERNAME)).thenReturn(USER_W);
 | 
			
		||||
 | 
			
		||||
        // Set the extended security service to normalize usernames
 | 
			
		||||
        extendedSecurityService.setEnableUsernameNormalization(true);
 | 
			
		||||
        extendedSecurityService.set(nodeRef, diffCasingReaders, diffCasingWriters);
 | 
			
		||||
 | 
			
		||||
        // Verify that the incorrect read group is not created
 | 
			
		||||
        verify(mockedAuthorityService, never()).createAuthority(AuthorityType.GROUP, wrongReadGroup, wrongReadGroup, Collections.singleton(RMAuthority.ZONE_APP_RM));
 | 
			
		||||
 | 
			
		||||
        // Verify that the incorrect write group is not created
 | 
			
		||||
        verify(mockedAuthorityService, never()).createAuthority(AuthorityType.GROUP, wrongWriteGroup, wrongWriteGroup, Collections.singleton(RMAuthority.ZONE_APP_RM));
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Given a node with no previous IPR groups assigned
 | 
			
		||||
@@ -571,7 +671,7 @@ public class ExtendedSecurityServiceImplUnitTest
 | 
			
		||||
            .thenReturn(Stream
 | 
			
		||||
                .of(USER_W, AlfMock.generateText())
 | 
			
		||||
                .collect(Collectors.toSet()));
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // add extended security
 | 
			
		||||
        extendedSecurityService.set(nodeRef, READERS, WRITERS);
 | 
			
		||||
        
 | 
			
		||||
@@ -895,7 +995,7 @@ public class ExtendedSecurityServiceImplUnitTest
 | 
			
		||||
        // group names
 | 
			
		||||
        String readGroup = extendedSecurityService.getIPRGroupShortName(READER_GROUP_FULL_PREFIX, READERS, 0);
 | 
			
		||||
        String writeGroup = extendedSecurityService.getIPRGroupShortName(WRITER_GROUP_FULL_PREFIX, WRITERS, 0);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // setup renditions
 | 
			
		||||
        NodeRef renditionNodeRef = AlfMock.generateNodeRef(mockedNodeService);
 | 
			
		||||
        when(mockedNodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_RECORD))
 | 
			
		||||
@@ -904,7 +1004,7 @@ public class ExtendedSecurityServiceImplUnitTest
 | 
			
		||||
            .thenReturn(renditionNodeRef);
 | 
			
		||||
        when(mockedNodeService.getChildAssocs(nodeRef, RenditionModel.ASSOC_RENDITION, RegexQNamePattern.MATCH_ALL))
 | 
			
		||||
            .thenReturn(Collections.singletonList(mockedChildAssociationRef));        
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // setup permissions
 | 
			
		||||
        Set<AccessPermission> permissions = Stream
 | 
			
		||||
            .of(new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, readGroup, 0),
 | 
			
		||||
@@ -913,17 +1013,17 @@ public class ExtendedSecurityServiceImplUnitTest
 | 
			
		||||
            .collect(Collectors.toSet());        
 | 
			
		||||
        when(mockedPermissionService.getAllSetPermissions(nodeRef))
 | 
			
		||||
            .thenReturn(permissions);      
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // remove extended security
 | 
			
		||||
        extendedSecurityService.remove(nodeRef);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // verify that the groups permissions have been removed
 | 
			
		||||
        verify(mockedPermissionService).clearPermission(nodeRef, readGroup);
 | 
			
		||||
        verify(mockedPermissionService).clearPermission(nodeRef, writeGroup);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // verify that the groups permissions have been removed from the rendition
 | 
			
		||||
        verify(mockedPermissionService).clearPermission(renditionNodeRef, readGroup);
 | 
			
		||||
        verify(mockedPermissionService).clearPermission(renditionNodeRef, writeGroup);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,177 @@
 | 
			
		||||
/*
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Records Management Module
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * -
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * -
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * -
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
package org.alfresco.rm.rest.api.impl;
 | 
			
		||||
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.capability.Capability;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.role.Role;
 | 
			
		||||
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
 | 
			
		||||
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
 | 
			
		||||
import org.alfresco.rest.framework.resource.parameters.Paging;
 | 
			
		||||
import org.alfresco.rest.framework.resource.parameters.Parameters;
 | 
			
		||||
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.CapabilityModel;
 | 
			
		||||
import org.alfresco.rm.rest.api.model.RoleModel;
 | 
			
		||||
import org.alfresco.service.cmr.repository.NodeRef;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import static org.junit.Assert.assertEquals;
 | 
			
		||||
import static org.mockito.Mockito.*;
 | 
			
		||||
 | 
			
		||||
public class RMRolesImplUnitTest extends BaseUnitTest {
 | 
			
		||||
 | 
			
		||||
    private final RecognizedParamsExtractor queryExtractor = new RecognizedParamsExtractor() {};
 | 
			
		||||
 | 
			
		||||
    private RMRolesImpl rmRolesImpl;
 | 
			
		||||
    private FilePlanRoleService mockedFilePlanRoleService;
 | 
			
		||||
    private ApiNodesModelFactory mockedNodesModelFactory;
 | 
			
		||||
 | 
			
		||||
    private final Capability viewRecordsCapability = mock(Capability.class);
 | 
			
		||||
    private final Capability editMetadataCapability = mock(Capability.class);
 | 
			
		||||
 | 
			
		||||
    private final Role role1 = new Role("Role1", "Role 1", Set.of(viewRecordsCapability), "Group1");
 | 
			
		||||
    private final Role role2 = new Role("Role2", "Role 2", Set.of(editMetadataCapability), "Group2");
 | 
			
		||||
 | 
			
		||||
    private final RoleModel roleModel1 = new RoleModel("Role1", "Role 1", List.of(new CapabilityModel("ViewRecords", "", "", null, 0)), "Group1", null, List.of("User1"), List.of("Group1"));
 | 
			
		||||
    private final RoleModel roleModel2 = new RoleModel("Role2", "Role 2", List.of(new CapabilityModel("EditMetadata", "", "", null, 0)), "Group2", null, List.of("User2"), List.of("Group2"));
 | 
			
		||||
 | 
			
		||||
    private final NodeRef filePlan = new NodeRef("workspace://SpacesStore/testFilePlan");
 | 
			
		||||
 | 
			
		||||
    private final Parameters parameters = mock(Parameters.class);
 | 
			
		||||
    private final Paging paging = mock(Paging.class);
 | 
			
		||||
 | 
			
		||||
    @Before
 | 
			
		||||
    public void setUp() {
 | 
			
		||||
        mockedFilePlanRoleService = mock(FilePlanRoleService.class);
 | 
			
		||||
        mockedNodesModelFactory = mock(ApiNodesModelFactory.class);
 | 
			
		||||
 | 
			
		||||
        rmRolesImpl = new RMRolesImpl();
 | 
			
		||||
        rmRolesImpl.setFilePlanRoleService(mockedFilePlanRoleService);
 | 
			
		||||
        rmRolesImpl.setNodesModelFactory(mockedNodesModelFactory);
 | 
			
		||||
 | 
			
		||||
        when(mockedFilePlanRoleService.getRoles(filePlan, true)).thenReturn(Set.of(role1, role2));
 | 
			
		||||
        when(mockedNodesModelFactory.createRoleModel(eq(role1), any(), any())).thenReturn(roleModel1);
 | 
			
		||||
        when(mockedNodesModelFactory.createRoleModel(eq(role2), any(), any())).thenReturn(roleModel2);
 | 
			
		||||
 | 
			
		||||
        when(viewRecordsCapability.getName()).thenReturn("ViewRecords");
 | 
			
		||||
        when(editMetadataCapability.getName()).thenReturn("EditMetadata");
 | 
			
		||||
 | 
			
		||||
        when(parameters.getPaging()).thenReturn(paging);
 | 
			
		||||
        when(paging.getSkipCount()).thenReturn(0);
 | 
			
		||||
        when(paging.getMaxItems()).thenReturn(10);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testGetRoles_NoFilters() {
 | 
			
		||||
        // when
 | 
			
		||||
        CollectionWithPagingInfo<RoleModel> result = rmRolesImpl.getRoles(filePlan, parameters);
 | 
			
		||||
 | 
			
		||||
        // then
 | 
			
		||||
        List<RoleModel> roleModelList = (List<RoleModel>) result.getCollection();
 | 
			
		||||
        assertEquals(2, (int) result.getTotalItems());
 | 
			
		||||
        assertEquals(List.of(roleModel1, roleModel2), roleModelList);
 | 
			
		||||
        verify(mockedFilePlanRoleService).getRoles(filePlan, true);
 | 
			
		||||
        verify(mockedNodesModelFactory).createRoleModel(eq(role1), any(), any());
 | 
			
		||||
        verify(mockedNodesModelFactory).createRoleModel(eq(role2), any(), any());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testGetRoles_WithPersonId() {
 | 
			
		||||
        // given
 | 
			
		||||
        String personId = "testUser";
 | 
			
		||||
        when(mockedFilePlanRoleService.getRolesByUser(filePlan, personId, true)).thenReturn(Set.of(role1));
 | 
			
		||||
        when(parameters.getQuery()).thenReturn(queryExtractor.getWhereClause("(personId='" + personId + "')"));
 | 
			
		||||
 | 
			
		||||
        // when
 | 
			
		||||
        CollectionWithPagingInfo<RoleModel> result = rmRolesImpl.getRoles(filePlan, parameters);
 | 
			
		||||
 | 
			
		||||
        // then
 | 
			
		||||
        assertEquals(1, (int) result.getTotalItems());
 | 
			
		||||
        assertEquals(List.of(roleModel1), result.getCollection());
 | 
			
		||||
        verify(mockedFilePlanRoleService).getRolesByUser(filePlan, personId, true);
 | 
			
		||||
        verify(mockedNodesModelFactory).createRoleModel(eq(role1), any(), any());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testGetNonSystemRoles() {
 | 
			
		||||
        //given
 | 
			
		||||
        when(mockedFilePlanRoleService.getRoles(filePlan, false)).thenReturn(Set.of(role2));
 | 
			
		||||
        when(parameters.getQuery()).thenReturn(queryExtractor.getWhereClause("(systemRoles=false)"));
 | 
			
		||||
 | 
			
		||||
        // when
 | 
			
		||||
        CollectionWithPagingInfo<RoleModel> result = rmRolesImpl.getRoles(filePlan, parameters);
 | 
			
		||||
 | 
			
		||||
        // then
 | 
			
		||||
        assertEquals(1, (int) result.getTotalItems());
 | 
			
		||||
        assertEquals(List.of(roleModel2), result.getCollection());
 | 
			
		||||
        verify(mockedFilePlanRoleService).getRoles(filePlan, false);
 | 
			
		||||
        verify(mockedNodesModelFactory).createRoleModel(eq(role2), any(), any());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testGetRoles_WithCapabilitiesFilter() {
 | 
			
		||||
        // given
 | 
			
		||||
        when(parameters.getQuery()).thenReturn(queryExtractor.getWhereClause("(capabilityName IN ('ViewRecords'))"));
 | 
			
		||||
 | 
			
		||||
        // when
 | 
			
		||||
        CollectionWithPagingInfo<RoleModel> result = rmRolesImpl.getRoles(filePlan, parameters);
 | 
			
		||||
 | 
			
		||||
        // then
 | 
			
		||||
        assertEquals(1, (int) result.getTotalItems());
 | 
			
		||||
        assertEquals(List.of(roleModel1), result.getCollection());
 | 
			
		||||
        verify(mockedFilePlanRoleService).getRoles(filePlan, true);
 | 
			
		||||
        verify(mockedNodesModelFactory).createRoleModel(eq(role1), any(), any());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testGetRoles_IncludeAssignedUsersAndGroups() {
 | 
			
		||||
        // given
 | 
			
		||||
        when(mockedFilePlanRoleService.getRoles(filePlan, true)).thenReturn(Set.of(role1));
 | 
			
		||||
        when(mockedFilePlanRoleService.getAllAssignedToRole(filePlan, "Role1")).thenReturn(Set.of("User1"));
 | 
			
		||||
        when(mockedFilePlanRoleService.getGroupsAssignedToRole(filePlan, "Role1")).thenReturn(Set.of("Group1"));
 | 
			
		||||
 | 
			
		||||
        when(parameters.getInclude()).thenReturn(List.of("assignedUsers", "assignedGroups"));
 | 
			
		||||
 | 
			
		||||
        // when
 | 
			
		||||
        CollectionWithPagingInfo<RoleModel> result = rmRolesImpl.getRoles(filePlan, parameters);
 | 
			
		||||
 | 
			
		||||
        // then
 | 
			
		||||
        List<RoleModel> roleModelList = (List<RoleModel>) result.getCollection();
 | 
			
		||||
        assertEquals(1, (int) result.getTotalItems());
 | 
			
		||||
        assertEquals(List.of(roleModel1), roleModelList);
 | 
			
		||||
        assertEquals(List.of("User1"), roleModelList.get(0).assignedUsers());
 | 
			
		||||
        assertEquals(List.of("Group1"), roleModelList.get(0).assignedGroups());
 | 
			
		||||
        verify(mockedFilePlanRoleService).getRoles(filePlan, true);
 | 
			
		||||
        verify(mockedFilePlanRoleService).getAllAssignedToRole(filePlan, "Role1");
 | 
			
		||||
        verify(mockedFilePlanRoleService).getGroupsAssignedToRole(filePlan, "Role1");
 | 
			
		||||
        verify(mockedNodesModelFactory).createRoleModel(role1, List.of("User1"), List.of("Group1"));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
com.epam.reportportal.testng.ReportPortalTestNGListener
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.alfresco</groupId>
 | 
			
		||||
        <artifactId>alfresco-governance-services-community-repo-parent</artifactId>
 | 
			
		||||
        <version>25.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <build>
 | 
			
		||||
 
 | 
			
		||||
@@ -540,6 +540,66 @@ paths:
 | 
			
		||||
          description: Unexpected error
 | 
			
		||||
          schema:
 | 
			
		||||
            $ref: '#/definitions/Error'
 | 
			
		||||
  '/file-plans/{filePlanId}/roles':
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
        - file-plans
 | 
			
		||||
      summary: Get roles in a file plan
 | 
			
		||||
      description: |
 | 
			
		||||
        Gets a list of roles for the specified file plan **filePlanId**.
 | 
			
		||||
        
 | 
			
		||||
        You can use the **include** parameter to return additional information.
 | 
			
		||||
        
 | 
			
		||||
        The **where** parameter can also be used to filter by **personId**, **systemRoles** and **capabilityName**.
 | 
			
		||||
 | 
			
		||||
        You can use the **where** parameter to 
 | 
			
		||||
        * filter roles by user:
 | 
			
		||||
        `where=(personId='admin')`
 | 
			
		||||
        
 | 
			
		||||
        * not include system roles in the results:
 | 
			
		||||
        `where=(systemRoles=false)`
 | 
			
		||||
        
 | 
			
		||||
        * filter roles by specified capabilities:
 | 
			
		||||
        `where=(capabilityName in ('AddToHold', 'ViewRecords'))`
 | 
			
		||||
        
 | 
			
		||||
        This may be combined with all filter, as shown below:
 | 
			
		||||
        
 | 
			
		||||
        `where=(systemRoles=false and personId='johndoe' and capabilityName in ('AddToHold', 'ViewRecords'))`
 | 
			
		||||
 | 
			
		||||
      operationId: getFilePlanRoles
 | 
			
		||||
      parameters:
 | 
			
		||||
        - $ref: '#/parameters/filePlanIdWithAliasParam'
 | 
			
		||||
        - $ref: '#/parameters/whereParam'
 | 
			
		||||
        - $ref: '#/parameters/rolesIncludeParam'
 | 
			
		||||
        - $ref: '#/parameters/skipCountParam'
 | 
			
		||||
        - $ref: '#/parameters/maxItemsParam'
 | 
			
		||||
      produces:
 | 
			
		||||
        - application/json
 | 
			
		||||
      responses:
 | 
			
		||||
        '200':
 | 
			
		||||
          description: Successful response
 | 
			
		||||
          schema:
 | 
			
		||||
            type: object
 | 
			
		||||
            properties:
 | 
			
		||||
              list:
 | 
			
		||||
                type: array
 | 
			
		||||
                items:
 | 
			
		||||
                  $ref: '#/definitions/RoleModel'
 | 
			
		||||
              pagination:
 | 
			
		||||
                $ref: '#/definitions/Pagination'
 | 
			
		||||
        '400':
 | 
			
		||||
          description: |
 | 
			
		||||
            Invalid parameter: value of **maxItems** or **skipCount**, or **include**, or **where** is invalid
 | 
			
		||||
        '401':
 | 
			
		||||
          description: Authentication failed
 | 
			
		||||
        '403':
 | 
			
		||||
          description: Current user does not have permission to read **filePlanId**
 | 
			
		||||
        '404':
 | 
			
		||||
          description: "**filePlanId** does not exist"
 | 
			
		||||
        default:
 | 
			
		||||
          description: Unexpected error
 | 
			
		||||
          schema:
 | 
			
		||||
            $ref: '#/definitions/Error'
 | 
			
		||||
  ## Unfiled records containers
 | 
			
		||||
  '/unfiled-containers/{unfiledContainerId}':
 | 
			
		||||
    get:
 | 
			
		||||
@@ -3307,6 +3367,21 @@ parameters:
 | 
			
		||||
      * actions
 | 
			
		||||
    required: false
 | 
			
		||||
    type: string
 | 
			
		||||
  rolesIncludeParam:
 | 
			
		||||
    name: include
 | 
			
		||||
    in: query
 | 
			
		||||
    description: |
 | 
			
		||||
      Returns additional information about roles. Any optional field from the response model can be requested. For example:
 | 
			
		||||
      * assignedUsers
 | 
			
		||||
      * assignedGroups
 | 
			
		||||
    required: false
 | 
			
		||||
    type: string
 | 
			
		||||
  whereParam:
 | 
			
		||||
    name: where
 | 
			
		||||
    in: query
 | 
			
		||||
    description: A string to restrict the returned objects by using a predicate.
 | 
			
		||||
    required: false
 | 
			
		||||
    type: string
 | 
			
		||||
definitions:
 | 
			
		||||
  FilePlanComponentBodyUpdate:
 | 
			
		||||
    type: object
 | 
			
		||||
@@ -4389,6 +4464,49 @@ definitions:
 | 
			
		||||
      query:
 | 
			
		||||
        description: The query which may have been generated in some way from the userQuery
 | 
			
		||||
        type: string
 | 
			
		||||
  CapabilityModel:
 | 
			
		||||
    type: object
 | 
			
		||||
    properties:
 | 
			
		||||
      name:
 | 
			
		||||
        type: string
 | 
			
		||||
      title:
 | 
			
		||||
        type: string
 | 
			
		||||
      description:
 | 
			
		||||
        type: string
 | 
			
		||||
      group:
 | 
			
		||||
        $ref: '#/definitions/GroupModel'
 | 
			
		||||
      index:
 | 
			
		||||
        type: integer
 | 
			
		||||
  GroupModel:
 | 
			
		||||
    type: object
 | 
			
		||||
    properties:
 | 
			
		||||
      id:
 | 
			
		||||
        type: string
 | 
			
		||||
      title:
 | 
			
		||||
        type: string
 | 
			
		||||
  RoleModel:
 | 
			
		||||
    type: object
 | 
			
		||||
    properties:
 | 
			
		||||
      name:
 | 
			
		||||
        type: string
 | 
			
		||||
      displayLabel:
 | 
			
		||||
        type: string
 | 
			
		||||
      capabilities:
 | 
			
		||||
        type: array
 | 
			
		||||
        items:
 | 
			
		||||
          $ref: '#/definitions/CapabilityModel'
 | 
			
		||||
      roleGroupName:
 | 
			
		||||
        type: string
 | 
			
		||||
      groupShortName:
 | 
			
		||||
        type: string
 | 
			
		||||
      assignedUsers:
 | 
			
		||||
        type: array
 | 
			
		||||
        items:
 | 
			
		||||
          type: string
 | 
			
		||||
      assignedGroups:
 | 
			
		||||
        type: array
 | 
			
		||||
        items:
 | 
			
		||||
          type: string
 | 
			
		||||
  HoldBulkOperation:
 | 
			
		||||
    type: object
 | 
			
		||||
    properties:
 | 
			
		||||
@@ -4929,4 +5047,4 @@ definitions:
 | 
			
		||||
          - SiteConsumer
 | 
			
		||||
          - SiteCollaborator
 | 
			
		||||
          - SiteContributor
 | 
			
		||||
          - SiteManager
 | 
			
		||||
          - SiteManager
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.alfresco</groupId>
 | 
			
		||||
        <artifactId>alfresco-community-repo</artifactId>
 | 
			
		||||
        <version>25.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <modules>
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.alfresco</groupId>
 | 
			
		||||
        <artifactId>alfresco-community-repo-amps</artifactId>
 | 
			
		||||
        <version>25.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <properties>
 | 
			
		||||
@@ -51,8 +51,8 @@
 | 
			
		||||
            </exclusions>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>commons-lang</groupId>
 | 
			
		||||
            <artifactId>commons-lang</artifactId>
 | 
			
		||||
            <groupId>org.apache.commons</groupId>
 | 
			
		||||
            <artifactId>commons-lang3</artifactId>
 | 
			
		||||
            <scope>provided</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
@@ -70,11 +70,6 @@
 | 
			
		||||
            <artifactId>junit</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.epam.reportportal</groupId>
 | 
			
		||||
            <artifactId>agent-java-testng</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.postgresql</groupId>
 | 
			
		||||
            <artifactId>postgresql</artifactId>
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ import java.util.ResourceBundle;
 | 
			
		||||
import java.util.regex.Matcher;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.lang.StringEscapeUtils;
 | 
			
		||||
import org.apache.commons.lang3.StringEscapeUtils;
 | 
			
		||||
import org.json.simple.JSONObject;
 | 
			
		||||
import org.springframework.extensions.webscripts.Cache;
 | 
			
		||||
import org.springframework.extensions.webscripts.Status;
 | 
			
		||||
@@ -92,7 +92,7 @@ public class WikiPageGet extends AbstractWikiWebScript
 | 
			
		||||
                {
 | 
			
		||||
                    links.add(link);
 | 
			
		||||
                    // build the list of available pages
 | 
			
		||||
                    WikiPageInfo wikiPage = wikiService.getWikiPage(site.getShortName(), StringEscapeUtils.unescapeHtml(link));
 | 
			
		||||
                    WikiPageInfo wikiPage = wikiService.getWikiPage(site.getShortName(), StringEscapeUtils.unescapeHtml4(link));
 | 
			
		||||
                    if (wikiPage != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        pageTitles.add(wikiPage.getTitle());
 | 
			
		||||
 
 | 
			
		||||
@@ -91,6 +91,15 @@ function doclist_getAllNodes(parsedArgs, filterParams, query, totalItemCount)
 | 
			
		||||
   };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sanitizeJunkFavouriteKeys(favourites){
 | 
			
		||||
   for (var key in favourites) {
 | 
			
		||||
      if (!key || key.trim() === "") {
 | 
			
		||||
         delete favourites[key];
 | 
			
		||||
      }
 | 
			
		||||
   }
 | 
			
		||||
   return favourites;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Main entry point: Create collection of documents and folders in the given space
 | 
			
		||||
 *
 | 
			
		||||
@@ -123,6 +132,47 @@ function doclist_main()
 | 
			
		||||
   
 | 
			
		||||
   if (logger.isLoggingEnabled())
 | 
			
		||||
      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)
 | 
			
		||||
   {
 | 
			
		||||
      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()
 | 
			
		||||
      };
 | 
			
		||||
   }
 | 
			
		||||
   
 | 
			
		||||
   var totalItemCount = filterParams.limitResults ? parseInt(filterParams.limitResults, 10) : -1;
 | 
			
		||||
   // For all sites documentLibrary query we pull in all available results and post filter
 | 
			
		||||
 
 | 
			
		||||
@@ -182,11 +182,14 @@ var Filters =
 | 
			
		||||
         case "favourites":
 | 
			
		||||
            for (var favourite in favourites)
 | 
			
		||||
            {
 | 
			
		||||
               if (filterQuery)
 | 
			
		||||
               if (favourite && favourite.trim() !== "")
 | 
			
		||||
               {
 | 
			
		||||
                  filterQuery += " OR ";
 | 
			
		||||
                  if (filterQuery)
 | 
			
		||||
                  {
 | 
			
		||||
                     filterQuery += " OR ";
 | 
			
		||||
                  }
 | 
			
		||||
                  filterQuery += "ID:\"" + favourite + "\"";
 | 
			
		||||
               }
 | 
			
		||||
               filterQuery += "ID:\"" + favourite + "\"";
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            if (filterQuery.length !== 0)
 | 
			
		||||
@@ -201,7 +204,13 @@ var Filters =
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
               // empty favourites query
 | 
			
		||||
               filterQuery = "+ID:\"\"";
 | 
			
		||||
               logger.warn("No favourites found for user: " + person.properties.userName);
 | 
			
		||||
               return {
 | 
			
		||||
                  query: null,
 | 
			
		||||
                  limitResults: 0,
 | 
			
		||||
                  sort: [],
 | 
			
		||||
                  language: "lucene"
 | 
			
		||||
               };
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            filterParams.query = filterQuery;
 | 
			
		||||
@@ -224,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
 | 
			
		||||
@@ -240,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":
 | 
			
		||||
@@ -262,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")
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,11 @@ function runAction(p_params)
 | 
			
		||||
         {
 | 
			
		||||
            result.fileExist = true;
 | 
			
		||||
         }
 | 
			
		||||
         if (error.indexOf("FolderExistsException") != -1)
 | 
			
		||||
         {
 | 
			
		||||
            result.fileExist = true;
 | 
			
		||||
            result.type = "folder";
 | 
			
		||||
         }
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      results.push(result);
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ import java.util.regex.Matcher;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
import jakarta.transaction.UserTransaction;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.lang.StringEscapeUtils;
 | 
			
		||||
import org.apache.commons.lang3.StringEscapeUtils;
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
import org.json.JSONArray;
 | 
			
		||||
@@ -958,7 +958,7 @@ public class WikiRestApiTest extends BaseWebScriptTest
 | 
			
		||||
                String link = m.group(1);
 | 
			
		||||
                link += "?title=<script>alert('xss');</script>";
 | 
			
		||||
                WikiPageInfo wikiPage2 = this.wikiService.getWikiPage(SITE_SHORT_NAME_WIKI, link);
 | 
			
		||||
                WikiPageInfo wikiPage1 = this.wikiService.getWikiPage(SITE_SHORT_NAME_WIKI, StringEscapeUtils.unescapeHtml(link));
 | 
			
		||||
                WikiPageInfo wikiPage1 = this.wikiService.getWikiPage(SITE_SHORT_NAME_WIKI, StringEscapeUtils.unescapeHtml4(link));
 | 
			
		||||
                assertEquals(wikiPage2, wikiPage1);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
com.epam.reportportal.testng.ReportPortalTestNGListener
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
   <parent>
 | 
			
		||||
      <groupId>org.alfresco</groupId>
 | 
			
		||||
      <artifactId>alfresco-community-repo</artifactId>
 | 
			
		||||
      <version>25.2.0.16</version>
 | 
			
		||||
      <version>25.3.0.62</version>
 | 
			
		||||
   </parent>
 | 
			
		||||
 | 
			
		||||
   <dependencies>
 | 
			
		||||
@@ -126,11 +126,6 @@
 | 
			
		||||
         <artifactId>junit</artifactId>
 | 
			
		||||
         <scope>test</scope>
 | 
			
		||||
      </dependency>
 | 
			
		||||
      <dependency>
 | 
			
		||||
         <groupId>com.epam.reportportal</groupId>
 | 
			
		||||
         <artifactId>agent-java-testng</artifactId>
 | 
			
		||||
         <scope>test</scope>
 | 
			
		||||
      </dependency>
 | 
			
		||||
      <dependency>
 | 
			
		||||
         <groupId>org.mockito</groupId>
 | 
			
		||||
         <artifactId>mockito-core</artifactId>
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
com.epam.reportportal.testng.ReportPortalTestNGListener
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.alfresco</groupId>
 | 
			
		||||
        <artifactId>alfresco-community-repo</artifactId>
 | 
			
		||||
        <version>25.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <properties>
 | 
			
		||||
@@ -144,7 +144,7 @@
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.fasterxml.woodstox</groupId>
 | 
			
		||||
            <artifactId>woodstox-core</artifactId>
 | 
			
		||||
            <version>7.0.0</version>
 | 
			
		||||
            <version>7.1.1</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <!-- the cxf libs were updated, see dependencyManagement section -->
 | 
			
		||||
@@ -247,11 +247,6 @@
 | 
			
		||||
            <artifactId>junit</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.epam.reportportal</groupId>
 | 
			
		||||
            <artifactId>agent-java-testng</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.antlr</groupId>
 | 
			
		||||
            <artifactId>gunit</artifactId>
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
com.epam.reportportal.testng.ReportPortalTestNGListener
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.alfresco</groupId>
 | 
			
		||||
        <artifactId>alfresco-community-repo</artifactId>
 | 
			
		||||
        <version>25.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <dependencies>
 | 
			
		||||
@@ -46,11 +46,6 @@
 | 
			
		||||
            <artifactId>junit</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.epam.reportportal</groupId>
 | 
			
		||||
            <artifactId>agent-java-testng</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.mockito</groupId>
 | 
			
		||||
            <artifactId>mockito-core</artifactId>
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
com.epam.reportportal.testng.ReportPortalTestNGListener
 | 
			
		||||
@@ -9,6 +9,6 @@
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.alfresco</groupId>
 | 
			
		||||
        <artifactId>alfresco-community-repo-packaging</artifactId>
 | 
			
		||||
        <version>25.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
</project>
 | 
			
		||||
 
 | 
			
		||||
@@ -37,8 +37,7 @@ commons-fileupload  http://jakarta.apache.org/commons/
 | 
			
		||||
commons-httpclient  http://jakarta.apache.org/commons/ 
 | 
			
		||||
commons-io  http://jakarta.apache.org/commons/ 
 | 
			
		||||
commons-jxpath  http://jakarta.apache.org/commons/ 
 | 
			
		||||
commons-lang    http://jakarta.apache.org/commons/ 
 | 
			
		||||
commons-lang3   http://jakarta.apache.org/commons/ 
 | 
			
		||||
commons-lang3   http://jakarta.apache.org/commons/
 | 
			
		||||
commons-logging http://jakarta.apache.org/commons/ 
 | 
			
		||||
commons-net http://jakarta.apache.org/commons/
 | 
			
		||||
commons-pool    http://jakarta.apache.org/commons/ 
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
# More infos about this image: https://github.com/Alfresco/alfresco-docker-base-tomcat
 | 
			
		||||
FROM alfresco/alfresco-base-tomcat:tomcat10-jre17-rockylinux9@sha256:9622418e142fb4fe1c5320666ad61ea292bc5c98f3dd0b550b6add33d18f659f
 | 
			
		||||
 | 
			
		||||
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.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <properties>
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.alfresco</groupId>
 | 
			
		||||
        <artifactId>alfresco-community-repo</artifactId>
 | 
			
		||||
        <version>25.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <modules>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,3 @@
 | 
			
		||||
SOLR6_TAG=2.0.15
 | 
			
		||||
SOLR6_TAG=2.0.16
 | 
			
		||||
POSTGRES_TAG=16.6
 | 
			
		||||
ACTIVEMQ_TAG=5.18.3-jre17-rockylinux8
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.alfresco</groupId>
 | 
			
		||||
        <artifactId>alfresco-community-repo-packaging</artifactId>
 | 
			
		||||
        <version>25.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <modules>
 | 
			
		||||
 
 | 
			
		||||
@@ -49,5 +49,6 @@ then
 | 
			
		||||
  echo "Docker Compose started ok"
 | 
			
		||||
else
 | 
			
		||||
  echo "Docker Compose failed to start" >&2
 | 
			
		||||
  docker compose ${DOCKER_COMPOSES} logs --tail 200
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
fi
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@
 | 
			
		||||
 | 
			
		||||
## Synopsis
 | 
			
		||||
 | 
			
		||||
**TAS**( **T**est **A**utomation **S**ystem)- **CMIS** is the project that handles the automated tests related only to CMIS API integrated with Alfresco One [Alfresco CMIS API](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Reference/CMIS-API). 
 | 
			
		||||
**TAS**( **T**est **A**utomation **S**ystem)- **CMIS** is the project that handles the automated tests related only to CMIS API integrated with Alfresco One [Alfresco CMIS API](https://support.hyland.com/access?dita:id=kvf1721390177551&vrm_version=25.2). 
 | 
			
		||||
 | 
			
		||||
It is based on Apache Maven, compatible with major IDEs and is using also Spring capabilities for dependency injection.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.alfresco</groupId>
 | 
			
		||||
        <artifactId>alfresco-community-repo-tests</artifactId>
 | 
			
		||||
        <version>25.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <organization>
 | 
			
		||||
@@ -68,16 +68,6 @@
 | 
			
		||||
                </exclusion>
 | 
			
		||||
            </exclusions>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.epam.reportportal</groupId>
 | 
			
		||||
            <artifactId>agent-java-testng</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.squareup.okhttp3</groupId>
 | 
			
		||||
            <artifactId>okhttp</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
 | 
			
		||||
    <build>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import java.util.Date;
 | 
			
		||||
import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
 | 
			
		||||
import org.apache.chemistry.opencmis.commons.exceptions.CmisPermissionDeniedException;
 | 
			
		||||
import org.apache.chemistry.opencmis.commons.exceptions.CmisUnauthorizedException;
 | 
			
		||||
import org.apache.commons.lang.time.DateUtils;
 | 
			
		||||
import org.apache.commons.lang3.time.DateUtils;
 | 
			
		||||
import org.testng.annotations.BeforeClass;
 | 
			
		||||
import org.testng.annotations.Test;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
com.epam.reportportal.testng.ReportPortalTestNGListener
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.alfresco</groupId>
 | 
			
		||||
        <artifactId>alfresco-community-repo-tests</artifactId>
 | 
			
		||||
        <version>25.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <developers>
 | 
			
		||||
@@ -36,11 +36,6 @@
 | 
			
		||||
            <artifactId>jakarta.mail-api</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.epam.reportportal</groupId>
 | 
			
		||||
            <artifactId>agent-java-testng</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
 | 
			
		||||
    <build>
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
com.epam.reportportal.testng.ReportPortalTestNGListener
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.alfresco</groupId>
 | 
			
		||||
        <artifactId>alfresco-community-repo-tests</artifactId>
 | 
			
		||||
        <version>25.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <developers>
 | 
			
		||||
@@ -44,18 +44,6 @@
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.epam.reportportal</groupId>
 | 
			
		||||
            <artifactId>agent-java-testng</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.squareup.okhttp3</groupId>
 | 
			
		||||
            <artifactId>okhttp</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.sun.mail</groupId>
 | 
			
		||||
            <artifactId>jakarta.mail</artifactId>
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
com.epam.reportportal.testng.ReportPortalTestNGListener
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.alfresco</groupId>
 | 
			
		||||
        <artifactId>alfresco-community-repo-tests</artifactId>
 | 
			
		||||
        <version>25.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <properties>
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
        <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.17.0</commons-lang3.version>
 | 
			
		||||
        <commons-lang3.version>3.18.0</commons-lang3.version>
 | 
			
		||||
        <scribejava-apis.version>8.3.3</scribejava-apis.version>
 | 
			
		||||
        <java.version>17</java.version>
 | 
			
		||||
    </properties>
 | 
			
		||||
@@ -79,18 +79,6 @@
 | 
			
		||||
            <version>${commons-lang3.version}</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.epam.reportportal</groupId>
 | 
			
		||||
            <artifactId>agent-java-testng</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.squareup.okhttp3</groupId>
 | 
			
		||||
            <artifactId>okhttp</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.awaitility</groupId>
 | 
			
		||||
            <artifactId>awaitility</artifactId>
 | 
			
		||||
@@ -171,14 +159,14 @@
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.codehaus.groovy</groupId>
 | 
			
		||||
            <artifactId>groovy</artifactId>
 | 
			
		||||
            <version>3.0.23</version>
 | 
			
		||||
            <version>3.0.25</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <!-- https://mvnrepository.com/artifact/org.codehaus.groovy/groovy-json-->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.codehaus.groovy</groupId>
 | 
			
		||||
            <artifactId>groovy-json</artifactId>
 | 
			
		||||
            <version>3.0.22</version>
 | 
			
		||||
            <version>3.0.25</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
       <dependency>
 | 
			
		||||
 
 | 
			
		||||
@@ -675,6 +675,11 @@ public class RestWrapper extends DSLWrapper<RestWrapper>
 | 
			
		||||
        {
 | 
			
		||||
            returnedResponse = onRequest().get(restRequest.getPath(), restRequest.getPathParams()).andReturn();
 | 
			
		||||
        }
 | 
			
		||||
        else if (HttpMethod.PATCH.equals(httpMethod))
 | 
			
		||||
        {
 | 
			
		||||
            returnedResponse = onRequest().body(restRequest.getBody())
 | 
			
		||||
                    .patch(restRequest.getPath(), restRequest.getPathParams()).andReturn();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            returnedResponse = onRequest().get(restRequest.getPath(), restRequest.getPathParams()).andReturn();
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Repository
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2022 Alfresco Software Limited
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
@@ -747,7 +747,7 @@ public class CreateRulesTests extends RulesRestTest
 | 
			
		||||
                .createSingleRule(ruleModel);
 | 
			
		||||
 | 
			
		||||
        restClient.assertStatusCodeIs(NOT_FOUND);
 | 
			
		||||
        restClient.assertLastError().containsSummary("The entity with id: non-existent-node was not found");
 | 
			
		||||
        restClient.assertLastError().containsSummary("Destination folder having Id: non-existent-node no longer exists. Please update your rule definition.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,95 @@
 | 
			
		||||
/*
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Repository
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2025 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 *
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.alfresco.rest.search;
 | 
			
		||||
 | 
			
		||||
import static org.testng.Assert.*;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.collections.CollectionUtils;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.testng.annotations.Test;
 | 
			
		||||
 | 
			
		||||
import org.alfresco.rest.RestTest;
 | 
			
		||||
import org.alfresco.rest.model.RestNodeModel;
 | 
			
		||||
import org.alfresco.utility.RetryOperation;
 | 
			
		||||
import org.alfresco.utility.Utility;
 | 
			
		||||
import org.alfresco.utility.model.ContentModel;
 | 
			
		||||
import org.alfresco.utility.model.FileModel;
 | 
			
		||||
import org.alfresco.utility.model.TestGroup;
 | 
			
		||||
import org.alfresco.utility.model.UserModel;
 | 
			
		||||
import org.alfresco.utility.testrail.ExecutionType;
 | 
			
		||||
import org.alfresco.utility.testrail.annotation.TestRail;
 | 
			
		||||
 | 
			
		||||
public class NodeContentTest extends RestTest
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    @TestRail(section = {TestGroup.REST_API,
 | 
			
		||||
            TestGroup.SEARCH}, executionType = ExecutionType.SANITY, description = "Check basic functionality of GET queries/sites")
 | 
			
		||||
    @Test(groups = {TestGroup.REST_API, TestGroup.RATINGS, TestGroup.CORE})
 | 
			
		||||
    public void testNodeContent() throws Exception
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        UserModel adminUser = dataContent.getAdminUser();
 | 
			
		||||
        final String fileName = "nodecontent.pdf";
 | 
			
		||||
        final String term = "babekyrtso";
 | 
			
		||||
 | 
			
		||||
        FileModel fileModel = FileModel.getFileModelBasedOnTestDataFile(fileName);
 | 
			
		||||
        restClient.authenticateUser(adminUser)
 | 
			
		||||
                .configureRequestSpec()
 | 
			
		||||
                .addMultiPart("filedata", fileModel.toFile());
 | 
			
		||||
        RestNodeModel node = restClient.authenticateUser(adminUser).withCoreAPI().usingNode(ContentModel.my()).createNode();
 | 
			
		||||
        restClient.assertStatusCodeIs(HttpStatus.CREATED);
 | 
			
		||||
 | 
			
		||||
        SearchRequest query = new SearchRequest();
 | 
			
		||||
        RestRequestQueryModel queryModel = new RestRequestQueryModel();
 | 
			
		||||
        queryModel.setLanguage("afts");
 | 
			
		||||
        queryModel.setQuery(term);
 | 
			
		||||
        query.setQuery(queryModel);
 | 
			
		||||
 | 
			
		||||
        RetryOperation op = () -> {
 | 
			
		||||
            List<SearchNodeModel> entries = restClient.authenticateUser(adminUser)
 | 
			
		||||
                    .withSearchAPI()
 | 
			
		||||
                    .search(query).getEntries();
 | 
			
		||||
 | 
			
		||||
            assertFalse(CollectionUtils.isEmpty(entries), "Search results should not be empty");
 | 
			
		||||
            boolean fileFound = entries.stream()
 | 
			
		||||
                    .map(SearchNodeModel::getModel)
 | 
			
		||||
                    .anyMatch(e -> fileName.equals(e.getName()));
 | 
			
		||||
            assertTrue(fileFound, "Search results should contain the file: " + fileName);
 | 
			
		||||
 | 
			
		||||
            restClient.assertStatusCodeIs(HttpStatus.OK);
 | 
			
		||||
        };
 | 
			
		||||
        Utility.sleep(300, 100000, op);
 | 
			
		||||
 | 
			
		||||
        restClient.authenticateUser(adminUser)
 | 
			
		||||
                .withCoreAPI()
 | 
			
		||||
                .usingNode(ContentModel.my())
 | 
			
		||||
                .deleteNode(node);
 | 
			
		||||
        restClient.assertStatusCodeIs(HttpStatus.NO_CONTENT);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
com.epam.reportportal.testng.ReportPortalTestNGListener
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								packaging/tests/tas-restapi/src/test/resources/shared-resources/testdata/nodecontent.pdf
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								packaging/tests/tas-restapi/src/test/resources/shared-resources/testdata/nodecontent.pdf
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -15,6 +15,7 @@
 | 
			
		||||
            <package name="org.alfresco.rest.people.*"/>
 | 
			
		||||
            <package name="org.alfresco.rest.queries.*"/>
 | 
			
		||||
            <package name="org.alfresco.rest.ratings.*"/>
 | 
			
		||||
            <package name="org.alfresco.rest.search.*"/>
 | 
			
		||||
        </packages>
 | 
			
		||||
    </test>
 | 
			
		||||
</suite>
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.alfresco</groupId>
 | 
			
		||||
        <artifactId>alfresco-community-repo-tests</artifactId>
 | 
			
		||||
        <version>25.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <developers>
 | 
			
		||||
@@ -31,11 +31,6 @@
 | 
			
		||||
            <artifactId>webdav</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.epam.reportportal</groupId>
 | 
			
		||||
            <artifactId>agent-java-testng</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
 | 
			
		||||
    <build>
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
com.epam.reportportal.testng.ReportPortalTestNGListener
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.alfresco</groupId>
 | 
			
		||||
        <artifactId>alfresco-community-repo-packaging</artifactId>
 | 
			
		||||
        <version>25.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <properties>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
  <!-- Resource defaultTransactionIsolation="-1" defaultAutoCommit="false" maxActive="100" initialSize="10" password="alfresco" username="alfresco" url="jdbc:mysql:///alfresco" driverClassName="org.gjt.mm.mysql.Driver" type="javax.sql.DataSource" auth="Container" name="jdbc/dataSource"/-->
 | 
			
		||||
  <Environment override="false" type="java.lang.Boolean" name="properties/startup.enable" description="A flag that globally enables or disables startup of the major Alfresco subsystems." value="true"/>
 | 
			
		||||
  <Environment override="false" type="java.lang.String" name="properties/dir.root" description="The filesystem directory below which content and index data is stored. Should be on a shared disk if this is a clustered installation."/>
 | 
			
		||||
  <Environment override="false" type="java.lang.String" name="properties/hibernate.dialect" description="The fully qualified name of a org.hibernate.dialect.Dialect subclass that allows Hibernate to generate SQL optimized for a particular relational database. Choose from org.hibernate.dialect.DerbyDialect, org.hibernate.dialect.MySQLInnoDBDialect, org.alfresco.repo.domain.hibernate.dialect.AlfrescoOracle9Dialect, org.alfresco.repo.domain.hibernate.dialect.AlfrescoSybaseAnywhereDialect, org.alfresco.repo.domain.hibernate.dialect.AlfrescoSQLServerDialect, org.hibernate.dialect.PostgreSQLDialect"/>
 | 
			
		||||
  <Environment override="false" type="java.lang.String" name="properties/hibernate.dialect" description="The fully qualified name of a org.hibernate.dialect.Dialect subclass that allows Hibernate to generate SQL optimized for a particular relational database. Choose from org.hibernate.dialect.DerbyDialect, org.hibernate.dialect.MySQLInnoDBDialect, org.alfresco.repo.domain.hibernate.dialect.AlfrescoOracle9Dialect, org.alfresco.repo.domain.hibernate.dialect.AlfrescoSybaseAnywhereDialect, org.alfresco.repo.domain.hibernate.dialect.SQLServerDialect, org.hibernate.dialect.PostgreSQLDialect"/>
 | 
			
		||||
  <Environment override="false" type="java.lang.String" name="properties/hibernate.query.substitutions" description="Mapping from tokens in Hibernate queries to SQL tokens. For PostgreSQL, set this to "true TRUE, false FALSE"."/>
 | 
			
		||||
  <Environment override="false" type="java.lang.Boolean" name="properties/hibernate.jdbc.use_get_generated_keys" description="Enable use of JDBC3 PreparedStatement.getGeneratedKeys() to retrieve natively generated keys after insert. Requires JDBC3+ driver. Set to false if your driver has problems with the Hibernate identifier generators. By default, tries to determine the driver capabilities using connection metadata."/>
 | 
			
		||||
  <Environment override="false" type="java.lang.String" name="properties/hibernate.default_schema" description="Qualify unqualified table names with the given schema/tablespace in generated SQL. It may be necessary to set this when the target database has more than one schema."/>
 | 
			
		||||
 
 | 
			
		||||
@@ -500,7 +500,7 @@
 | 
			
		||||
         org.hibernate.dialect.MySQLInnoDBDialect,
 | 
			
		||||
         org.alfresco.repo.domain.hibernate.dialect.AlfrescoOracle9Dialect,
 | 
			
		||||
         org.alfresco.repo.domain.hibernate.dialect.AlfrescoSybaseAnywhereDialect,
 | 
			
		||||
         org.alfresco.repo.domain.hibernate.dialect.AlfrescoSQLServerDialect, org.hibernate.dialect.PostgreSQLDialect</description>
 | 
			
		||||
         org.alfresco.repo.domain.hibernate.dialect.SQLServerDialect, org.hibernate.dialect.PostgreSQLDialect</description>
 | 
			
		||||
      <env-entry-name>properties/hibernate.dialect</env-entry-name>
 | 
			
		||||
      <env-entry-type>java.lang.String</env-entry-type>
 | 
			
		||||
      <env-entry-value/> <!-- Empty value included for JBoss compatibility -->
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										110
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								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.2.0.16</version>
 | 
			
		||||
    <version>25.3.0.62</version>
 | 
			
		||||
    <packaging>pom</packaging>
 | 
			
		||||
    <name>Alfresco Community Repo Parent</name>
 | 
			
		||||
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
 | 
			
		||||
    <properties>
 | 
			
		||||
        <acs.version.major>25</acs.version.major>
 | 
			
		||||
        <acs.version.minor>2</acs.version.minor>
 | 
			
		||||
        <acs.version.minor>3</acs.version.minor>
 | 
			
		||||
        <acs.version.revision>0</acs.version.revision>
 | 
			
		||||
        <acs.version.label />
 | 
			
		||||
        <amp.min.version>${acs.version.major}.0.0</amp.min.version>
 | 
			
		||||
@@ -51,41 +51,42 @@
 | 
			
		||||
        <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.1.7</dependency.alfresco-transform-core.version>
 | 
			
		||||
        <dependency.alfresco-transform-service.version>4.1.7</dependency.alfresco-transform-service.version>
 | 
			
		||||
        <dependency.alfresco-transform-core.version>5.2.2</dependency.alfresco-transform-core.version>
 | 
			
		||||
        <dependency.alfresco-transform-service.version>4.2.2</dependency.alfresco-transform-service.version>
 | 
			
		||||
        <dependency.alfresco-greenmail.version>7.1</dependency.alfresco-greenmail.version>
 | 
			
		||||
        <dependency.acs-event-model.version>1.0.2</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.2</dependency.spring.version>
 | 
			
		||||
        <dependency.spring-security.version>6.3.7</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.0</dependency.cxf.version>
 | 
			
		||||
        <dependency.cxf.version>4.1.2</dependency.cxf.version>
 | 
			
		||||
        <dependency.opencmis.version>1.0.0-jakarta-1</dependency.opencmis.version>
 | 
			
		||||
        <dependency.webscripts.version>10.0</dependency.webscripts.version>
 | 
			
		||||
        <dependency.bouncycastle.version>1.79</dependency.bouncycastle.version>
 | 
			
		||||
        <dependency.mockito-core.version>5.14.1</dependency.mockito-core.version>
 | 
			
		||||
        <dependency.webscripts.version>10.2</dependency.webscripts.version>
 | 
			
		||||
        <dependency.bouncycastle.version>1.81</dependency.bouncycastle.version>
 | 
			
		||||
        <dependency.mockito-core.version>5.18.0</dependency.mockito-core.version>
 | 
			
		||||
        <dependency.assertj.version>3.27.3</dependency.assertj.version>
 | 
			
		||||
        <dependency.org-json.version>20240303</dependency.org-json.version>
 | 
			
		||||
        <dependency.org-json.version>20250517</dependency.org-json.version>
 | 
			
		||||
        <dependency.commons-dbcp.version>2.12.0</dependency.commons-dbcp.version>
 | 
			
		||||
        <dependency.commons-io.version>2.18.0</dependency.commons-io.version>
 | 
			
		||||
        <dependency.gson.version>2.12.1</dependency.gson.version>
 | 
			
		||||
        <dependency.commons-io.version>2.20.0</dependency.commons-io.version>
 | 
			
		||||
        <dependency.gson.version>2.13.1</dependency.gson.version>
 | 
			
		||||
        <dependency.guava.version>33.3.1-jre</dependency.guava.version>
 | 
			
		||||
        <dependency.httpclient.version>4.5.14</dependency.httpclient.version>
 | 
			
		||||
        <dependency.httpcore.version>4.4.16</dependency.httpcore.version>
 | 
			
		||||
        <dependency.httpcomponents-httpclient5.version>5.4.1</dependency.httpcomponents-httpclient5.version>
 | 
			
		||||
        <dependency.httpcomponents-httpcore5.version>5.3.3</dependency.httpcomponents-httpcore5.version>
 | 
			
		||||
        <dependency.httpcomponents-httpclient5.version>5.5</dependency.httpcomponents-httpclient5.version>
 | 
			
		||||
        <dependency.httpcomponents-httpcore5.version>5.3.4</dependency.httpcomponents-httpcore5.version>
 | 
			
		||||
        <dependency.httpcomponents-httpcore5-h2.version>5.3.4</dependency.httpcomponents-httpcore5-h2.version>
 | 
			
		||||
        <dependency.commons-httpclient.version>3.1-HTTPCLIENT-1265</dependency.commons-httpclient.version>
 | 
			
		||||
        <dependency.xercesImpl.version>2.12.2</dependency.xercesImpl.version>
 | 
			
		||||
        <dependency.slf4j.version>2.0.16</dependency.slf4j.version>
 | 
			
		||||
        <dependency.log4j.version>2.24.3</dependency.log4j.version>
 | 
			
		||||
        <dependency.groovy.version>3.0.23</dependency.groovy.version>
 | 
			
		||||
        <dependency.tika.version>2.9.2</dependency.tika.version>
 | 
			
		||||
        <dependency.log4j.version>2.25.1</dependency.log4j.version>
 | 
			
		||||
        <dependency.groovy.version>3.0.25</dependency.groovy.version>
 | 
			
		||||
        <dependency.tika.version>3.2.3</dependency.tika.version>
 | 
			
		||||
        <dependency.truezip.version>7.7.10</dependency.truezip.version>
 | 
			
		||||
        <dependency.poi.version>5.3.0</dependency.poi.version>
 | 
			
		||||
        <dependency.poi.version>5.4.0</dependency.poi.version>
 | 
			
		||||
        <dependency.jboss.logging.version>3.5.0.Final</dependency.jboss.logging.version>
 | 
			
		||||
        <dependency.camel.version>4.6.0</dependency.camel.version> <!-- when bumping this version, please keep track/sync with included netty.io dependencies -->
 | 
			
		||||
        <dependency.camel.version>4.11.0</dependency.camel.version> <!-- when bumping this version, please keep track/sync with included netty.io dependencies -->
 | 
			
		||||
        <dependency.netty.version>4.1.118.Final</dependency.netty.version> <!-- must be in sync with camels transitive dependencies, e.g.: netty-common -->
 | 
			
		||||
        <dependency.activemq.version>5.18.6</dependency.activemq.version>
 | 
			
		||||
        <dependency.apache-compress.version>1.27.1</dependency.apache-compress.version>
 | 
			
		||||
@@ -114,19 +115,19 @@
 | 
			
		||||
        <dependency.jakarta-json-path.version>2.9.0</dependency.jakarta-json-path.version>
 | 
			
		||||
        <dependency.json-smart.version>2.5.2</dependency.json-smart.version>
 | 
			
		||||
        <alfresco.googledrive.version>4.1.0</alfresco.googledrive.version>
 | 
			
		||||
        <alfresco.aos-module.version>3.3.0-A1</alfresco.aos-module.version>
 | 
			
		||||
        <alfresco.api-explorer.version>25.1.0</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share -->
 | 
			
		||||
        <alfresco.aos-module.version>3.4.0</alfresco.aos-module.version>
 | 
			
		||||
        <alfresco.api-explorer.version>25.2.0</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share -->
 | 
			
		||||
 | 
			
		||||
        <alfresco.maven-plugin.version>2.2.0</alfresco.maven-plugin.version>
 | 
			
		||||
        <license-maven-plugin.version>2.4.0</license-maven-plugin.version>
 | 
			
		||||
        <spotless-plugin.version>2.44.2</spotless-plugin.version>
 | 
			
		||||
        <spotless-plugin.version>2.45.0</spotless-plugin.version>
 | 
			
		||||
 | 
			
		||||
        <dependency.postgresql.version>42.7.5</dependency.postgresql.version>
 | 
			
		||||
        <dependency.mysql.version>8.0.30</dependency.mysql.version>
 | 
			
		||||
        <dependency.mysql-image.version>8</dependency.mysql-image.version>
 | 
			
		||||
        <dependency.mariadb.version>2.7.4</dependency.mariadb.version>
 | 
			
		||||
        <dependency.tas-utility.version>5.0.2</dependency.tas-utility.version>
 | 
			
		||||
        <dependency.rest-assured.version>5.5.0</dependency.rest-assured.version>
 | 
			
		||||
        <dependency.rest-assured.version>5.5.5</dependency.rest-assured.version>
 | 
			
		||||
        <dependency.tas-email.version>2.0.0</dependency.tas-email.version>
 | 
			
		||||
        <dependency.tas-webdav.version>1.21</dependency.tas-webdav.version>
 | 
			
		||||
        <dependency.tas-ftp.version>1.19</dependency.tas-ftp.version>
 | 
			
		||||
@@ -153,7 +154,7 @@
 | 
			
		||||
        <connection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</connection>
 | 
			
		||||
        <developerConnection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</developerConnection>
 | 
			
		||||
        <url>https://github.com/Alfresco/alfresco-community-repo</url>
 | 
			
		||||
        <tag>25.2.0.16</tag>
 | 
			
		||||
        <tag>25.3.0.62</tag>
 | 
			
		||||
    </scm>
 | 
			
		||||
 | 
			
		||||
    <distributionManagement>
 | 
			
		||||
@@ -169,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>
 | 
			
		||||
@@ -400,15 +407,20 @@
 | 
			
		||||
                <artifactId>httpcore5</artifactId>
 | 
			
		||||
                <version>${dependency.httpcomponents-httpcore5.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>org.apache.httpcomponents.core5</groupId>
 | 
			
		||||
                <artifactId>httpcore5-h2</artifactId>
 | 
			
		||||
                <version>${dependency.httpcomponents-httpcore5-h2.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>commons-logging</groupId>
 | 
			
		||||
                <artifactId>commons-logging</artifactId>
 | 
			
		||||
                <version>1.3.3</version>
 | 
			
		||||
                <version>1.3.5</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>commons-beanutils</groupId>
 | 
			
		||||
                <artifactId>commons-beanutils</artifactId>
 | 
			
		||||
                <version>1.9.4</version>
 | 
			
		||||
                <version>1.11.0</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>commons-codec</groupId>
 | 
			
		||||
@@ -416,9 +428,9 @@
 | 
			
		||||
                <version>1.18.0</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>commons-lang</groupId>
 | 
			
		||||
                <artifactId>commons-lang</artifactId>
 | 
			
		||||
                <version>2.6</version>
 | 
			
		||||
                <groupId>org.apache.commons</groupId>
 | 
			
		||||
                <artifactId>commons-lang3</artifactId>
 | 
			
		||||
                <version>3.18.0</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>commons-io</groupId>
 | 
			
		||||
@@ -438,8 +450,8 @@
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>org.apache.commons</groupId>
 | 
			
		||||
                <artifactId>commons-fileupload2-jakarta</artifactId>
 | 
			
		||||
                <version>2.0.0-M1</version>
 | 
			
		||||
                <artifactId>commons-fileupload2-jakarta-servlet6</artifactId>
 | 
			
		||||
                <version>2.0.0-M4</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>commons-net</groupId>
 | 
			
		||||
@@ -702,13 +714,13 @@
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>com.networknt</groupId>
 | 
			
		||||
                <artifactId>json-schema-validator</artifactId>
 | 
			
		||||
                <version>1.5.5</version>
 | 
			
		||||
                <version>1.5.8</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <!-- upgrade dependency from TIKA -->
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>org.jsoup</groupId>
 | 
			
		||||
                <artifactId>jsoup</artifactId>
 | 
			
		||||
                <version>1.18.1</version>
 | 
			
		||||
                <version>1.21.1</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <!-- upgrade dependency from TIKA -->
 | 
			
		||||
            <dependency>
 | 
			
		||||
@@ -792,7 +804,7 @@
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>joda-time</groupId>
 | 
			
		||||
                <artifactId>joda-time</artifactId>
 | 
			
		||||
                <version>2.13.1</version>
 | 
			
		||||
                <version>2.14.0</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <!-- provided dependencies -->
 | 
			
		||||
@@ -822,18 +834,6 @@
 | 
			
		||||
                <version>4.13.2</version>
 | 
			
		||||
                <scope>test</scope>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>com.epam.reportportal</groupId>
 | 
			
		||||
                <artifactId>agent-java-testng</artifactId>
 | 
			
		||||
                <version>5.4.0</version>
 | 
			
		||||
                <scope>test</scope>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>com.squareup.okhttp3</groupId>
 | 
			
		||||
                <artifactId>okhttp</artifactId>
 | 
			
		||||
                <version>4.12.0</version>
 | 
			
		||||
                <scope>test</scope>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>org.antlr</groupId>
 | 
			
		||||
                <artifactId>gunit</artifactId>
 | 
			
		||||
@@ -949,7 +949,7 @@
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>org.projectlombok</groupId>
 | 
			
		||||
                <artifactId>lombok</artifactId>
 | 
			
		||||
                <version>1.18.36</version>
 | 
			
		||||
                <version>1.18.38</version>
 | 
			
		||||
                <scope>provided</scope>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
@@ -1006,7 +1006,7 @@
 | 
			
		||||
                <plugin>
 | 
			
		||||
                    <groupId>io.fabric8</groupId>
 | 
			
		||||
                    <artifactId>docker-maven-plugin</artifactId>
 | 
			
		||||
                    <version>0.45.0</version>
 | 
			
		||||
                    <version>0.46.0</version>
 | 
			
		||||
                </plugin>
 | 
			
		||||
                <plugin>
 | 
			
		||||
                    <artifactId>maven-surefire-plugin</artifactId>
 | 
			
		||||
@@ -1087,7 +1087,7 @@
 | 
			
		||||
                <plugin>
 | 
			
		||||
                    <groupId>org.codehaus.cargo</groupId>
 | 
			
		||||
                    <artifactId>cargo-maven3-plugin</artifactId>
 | 
			
		||||
                    <version>1.10.16</version>
 | 
			
		||||
                    <version>1.10.20</version>
 | 
			
		||||
                </plugin>
 | 
			
		||||
                <plugin>
 | 
			
		||||
                    <groupId>org.apache.maven.plugins</groupId>
 | 
			
		||||
@@ -1131,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.2.0.16</version>
 | 
			
		||||
        <version>25.3.0.62</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
 | 
			
		||||
    <dependencies>
 | 
			
		||||
@@ -45,7 +45,7 @@
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.apache.santuario</groupId>
 | 
			
		||||
            <artifactId>xmlsec</artifactId>
 | 
			
		||||
            <version>4.0.3</version>
 | 
			
		||||
            <version>4.0.4</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <!-- newer version, see REPO-3133 -->
 | 
			
		||||
        <dependency>
 | 
			
		||||
@@ -66,11 +66,6 @@
 | 
			
		||||
            <artifactId>junit</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.epam.reportportal</groupId>
 | 
			
		||||
            <artifactId>agent-java-testng</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.mockito</groupId>
 | 
			
		||||
            <artifactId>mockito-core</artifactId>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Remote API
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software. 
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of 
 | 
			
		||||
@@ -31,7 +31,10 @@ import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.lang3.StringUtils;
 | 
			
		||||
import org.json.simple.JSONObject;
 | 
			
		||||
import org.owasp.html.PolicyFactory;
 | 
			
		||||
import org.owasp.html.Sanitizers;
 | 
			
		||||
import org.springframework.extensions.webscripts.Cache;
 | 
			
		||||
import org.springframework.extensions.webscripts.Status;
 | 
			
		||||
import org.springframework.extensions.webscripts.WebScriptRequest;
 | 
			
		||||
@@ -67,6 +70,19 @@ public class CommentsPost extends AbstractCommentsWebScript
 | 
			
		||||
        // get json object from request
 | 
			
		||||
        JSONObject json = parseJSON(req);
 | 
			
		||||
 | 
			
		||||
        // Validating and Sanitizing comment content to prevent XSS
 | 
			
		||||
        String commentContent = getOrNull(json, "content");
 | 
			
		||||
        if (StringUtils.isBlank(commentContent))
 | 
			
		||||
        {
 | 
			
		||||
            throw new IllegalArgumentException("Comment content must not be empty");
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            PolicyFactory policy = Sanitizers.FORMATTING.and(Sanitizers.LINKS);
 | 
			
		||||
            String safeContent = policy.sanitize(commentContent);
 | 
			
		||||
            json.replace("content", safeContent);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* MNT-10231, MNT-9771 fix */
 | 
			
		||||
        this.behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Remote API
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2023 Alfresco Software Limited
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software. 
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of 
 | 
			
		||||
@@ -46,7 +46,7 @@ import org.alfresco.repo.management.subsystems.ActivateableBean;
 | 
			
		||||
import org.alfresco.repo.security.authentication.AuthenticationComponent;
 | 
			
		||||
import org.alfresco.repo.security.authentication.AuthenticationException;
 | 
			
		||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
 | 
			
		||||
import org.alfresco.repo.security.authentication.external.AdminConsoleAuthenticator;
 | 
			
		||||
import org.alfresco.repo.security.authentication.external.ExternalUserAuthenticator;
 | 
			
		||||
import org.alfresco.repo.security.authentication.external.RemoteUserMapper;
 | 
			
		||||
import org.alfresco.repo.web.auth.AuthenticationListener;
 | 
			
		||||
import org.alfresco.repo.web.auth.TicketCredentials;
 | 
			
		||||
@@ -71,9 +71,11 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
 | 
			
		||||
 | 
			
		||||
    protected RemoteUserMapper remoteUserMapper;
 | 
			
		||||
    protected AuthenticationComponent authenticationComponent;
 | 
			
		||||
    protected AdminConsoleAuthenticator adminConsoleAuthenticator;
 | 
			
		||||
    protected ExternalUserAuthenticator adminConsoleAuthenticator;
 | 
			
		||||
    protected ExternalUserAuthenticator webScriptsHomeAuthenticator;
 | 
			
		||||
 | 
			
		||||
    private boolean alwaysAllowBasicAuthForAdminConsole = true;
 | 
			
		||||
    private boolean alwaysAllowBasicAuthForWebScriptsHome = true;
 | 
			
		||||
    List<String> adminConsoleScriptFamilies;
 | 
			
		||||
    long getRemoteUserTimeoutMilliseconds = GET_REMOTE_USER_TIMEOUT_MILLISECONDS_DEFAULT;
 | 
			
		||||
 | 
			
		||||
@@ -97,6 +99,16 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
 | 
			
		||||
        this.alwaysAllowBasicAuthForAdminConsole = alwaysAllowBasicAuthForAdminConsole;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isAlwaysAllowBasicAuthForWebScriptsHome()
 | 
			
		||||
    {
 | 
			
		||||
        return alwaysAllowBasicAuthForWebScriptsHome;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setAlwaysAllowBasicAuthForWebScriptsHome(boolean alwaysAllowBasicAuthForWebScriptsHome)
 | 
			
		||||
    {
 | 
			
		||||
        this.alwaysAllowBasicAuthForWebScriptsHome = alwaysAllowBasicAuthForWebScriptsHome;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public List<String> getAdminConsoleScriptFamilies()
 | 
			
		||||
    {
 | 
			
		||||
        return adminConsoleScriptFamilies;
 | 
			
		||||
@@ -118,11 +130,17 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setAdminConsoleAuthenticator(
 | 
			
		||||
            AdminConsoleAuthenticator adminConsoleAuthenticator)
 | 
			
		||||
            ExternalUserAuthenticator adminConsoleAuthenticator)
 | 
			
		||||
    {
 | 
			
		||||
        this.adminConsoleAuthenticator = adminConsoleAuthenticator;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setWebScriptsHomeAuthenticator(
 | 
			
		||||
            ExternalUserAuthenticator webScriptsHomeAuthenticator)
 | 
			
		||||
    {
 | 
			
		||||
        this.webScriptsHomeAuthenticator = webScriptsHomeAuthenticator;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Authenticator create(WebScriptServletRequest req, WebScriptServletResponse res)
 | 
			
		||||
    {
 | 
			
		||||
@@ -136,6 +154,8 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
 | 
			
		||||
     */
 | 
			
		||||
    public class RemoteUserAuthenticator extends BasicHttpAuthenticator
 | 
			
		||||
    {
 | 
			
		||||
        private static final String WEB_SCRIPTS_BASE_PATH = "org/springframework/extensions/webscripts";
 | 
			
		||||
 | 
			
		||||
        public RemoteUserAuthenticator(WebScriptServletRequest req, WebScriptServletResponse res, AuthenticationListener listener)
 | 
			
		||||
        {
 | 
			
		||||
            super(req, res, listener);
 | 
			
		||||
@@ -156,24 +176,47 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (servletReq.getServiceMatch() != null &&
 | 
			
		||||
                        isAdminConsoleWebScript(servletReq.getServiceMatch().getWebScript()) && isAdminConsoleAuthenticatorActive())
 | 
			
		||||
                        isAdminConsole(servletReq.getServiceMatch().getWebScript()) && isAdminConsoleAuthenticatorActive())
 | 
			
		||||
                {
 | 
			
		||||
                    userId = getAdminConsoleUser();
 | 
			
		||||
                }
 | 
			
		||||
                else if (servletReq.getServiceMatch() != null &&
 | 
			
		||||
                        isWebScriptsHome(servletReq.getServiceMatch().getWebScript()) && isWebScriptsHomeAuthenticatorActive())
 | 
			
		||||
                {
 | 
			
		||||
                    userId = getWebScriptsHomeUser();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (userId == null)
 | 
			
		||||
                {
 | 
			
		||||
                    if (isAlwaysAllowBasicAuthForAdminConsole())
 | 
			
		||||
                    {
 | 
			
		||||
                        final boolean useTimeoutForAdminAccessingAdminConsole = shouldUseTimeoutForAdminAccessingAdminConsole(required, isGuest);
 | 
			
		||||
                        boolean shouldUseTimeout = shouldUseTimeoutForAdminAccessingAdminConsole(required, isGuest);
 | 
			
		||||
 | 
			
		||||
                        if (useTimeoutForAdminAccessingAdminConsole && isBasicAuthHeaderPresentForAdmin())
 | 
			
		||||
                        if (shouldUseTimeout && isBasicAuthHeaderPresentForAdmin())
 | 
			
		||||
                        {
 | 
			
		||||
                            return callBasicAuthForAdminConsoleAccess(required, isGuest);
 | 
			
		||||
                            return callBasicAuthForAdminConsoleOrWebScriptsHomeAccess(required, isGuest);
 | 
			
		||||
                        }
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            userId = getRemoteUserWithTimeout(useTimeoutForAdminAccessingAdminConsole);
 | 
			
		||||
                            userId = getRemoteUserWithTimeout(shouldUseTimeout);
 | 
			
		||||
                        }
 | 
			
		||||
                        catch (AuthenticationTimeoutException e)
 | 
			
		||||
                        {
 | 
			
		||||
                            // return basic auth challenge
 | 
			
		||||
                            return false;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (isAlwaysAllowBasicAuthForWebScriptsHome())
 | 
			
		||||
                    {
 | 
			
		||||
                        boolean shouldUseTimeout = shouldUseTimeoutForAdminAccessingWebScriptsHome(required, isGuest);
 | 
			
		||||
 | 
			
		||||
                        if (shouldUseTimeout && isBasicAuthHeaderPresentForAdmin())
 | 
			
		||||
                        {
 | 
			
		||||
                            return callBasicAuthForAdminConsoleOrWebScriptsHomeAccess(required, isGuest);
 | 
			
		||||
                        }
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            userId = getRemoteUserWithTimeout(shouldUseTimeout);
 | 
			
		||||
                        }
 | 
			
		||||
                        catch (AuthenticationTimeoutException e)
 | 
			
		||||
                        {
 | 
			
		||||
@@ -252,38 +295,63 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
 | 
			
		||||
                    authenticated = super.authenticate(required, isGuest);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (!authenticated && servletReq.getServiceMatch() != null &&
 | 
			
		||||
                    isAdminConsoleWebScript(servletReq.getServiceMatch().getWebScript()) && isAdminConsoleAuthenticatorActive())
 | 
			
		||||
            if (!authenticated && servletReq.getServiceMatch() != null)
 | 
			
		||||
            {
 | 
			
		||||
                adminConsoleAuthenticator.requestAuthentication(this.servletReq.getHttpServletRequest(), this.servletRes.getHttpServletResponse());
 | 
			
		||||
                WebScript webScript = servletReq.getServiceMatch().getWebScript();
 | 
			
		||||
 | 
			
		||||
                if (isAdminConsole(webScript) && isAdminConsoleAuthenticatorActive())
 | 
			
		||||
                {
 | 
			
		||||
                    adminConsoleAuthenticator.requestAuthentication(
 | 
			
		||||
                            this.servletReq.getHttpServletRequest(),
 | 
			
		||||
                            this.servletRes.getHttpServletResponse());
 | 
			
		||||
                }
 | 
			
		||||
                else if (isWebScriptsHome(webScript)
 | 
			
		||||
                        && isWebScriptsHomeAuthenticatorActive())
 | 
			
		||||
                {
 | 
			
		||||
                    webScriptsHomeAuthenticator.requestAuthentication(
 | 
			
		||||
                            this.servletReq.getHttpServletRequest(),
 | 
			
		||||
                            this.servletRes.getHttpServletResponse());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return authenticated;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private boolean callBasicAuthForAdminConsoleAccess(RequiredAuthentication required, boolean isGuest)
 | 
			
		||||
        private boolean callBasicAuthForAdminConsoleOrWebScriptsHomeAccess(RequiredAuthentication required, boolean isGuest)
 | 
			
		||||
        {
 | 
			
		||||
            // return REST call, after a timeout/basic auth challenge
 | 
			
		||||
            if (LOGGER.isTraceEnabled())
 | 
			
		||||
            {
 | 
			
		||||
                LOGGER.trace("An Admin Console request has come in with Basic Auth headers present for an admin user.");
 | 
			
		||||
                LOGGER.trace("An Admin Console or WebScripts Home request has come in with Basic Auth headers present for an admin user.");
 | 
			
		||||
            }
 | 
			
		||||
            // In order to prompt for another password, in case it was not entered correctly,
 | 
			
		||||
            // the output of this method should be returned by the calling "authenticate" method;
 | 
			
		||||
            // This would also mean, that once the admin basic auth header is present,
 | 
			
		||||
            // the authentication chain will not be used for the admin console access
 | 
			
		||||
            // the authentication chain will not be used for access
 | 
			
		||||
            return super.authenticate(required, isGuest);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private boolean shouldUseTimeoutForAdminAccessingAdminConsole(RequiredAuthentication required, boolean isGuest)
 | 
			
		||||
        {
 | 
			
		||||
            boolean useTimeoutForAdminAccessingAdminConsole = RequiredAuthentication.admin.equals(required) && !isGuest &&
 | 
			
		||||
                    servletReq.getServiceMatch() != null && isAdminConsoleWebScript(servletReq.getServiceMatch().getWebScript());
 | 
			
		||||
            boolean adminConsoleTimeout = RequiredAuthentication.admin.equals(required) && !isGuest &&
 | 
			
		||||
                    servletReq.getServiceMatch() != null && isAdminConsole(servletReq.getServiceMatch().getWebScript());
 | 
			
		||||
 | 
			
		||||
            if (LOGGER.isTraceEnabled())
 | 
			
		||||
            {
 | 
			
		||||
                LOGGER.trace("Should ensure that the admins can login with basic auth: " + useTimeoutForAdminAccessingAdminConsole);
 | 
			
		||||
                LOGGER.trace("Should ensure that the admins can login with basic auth: " + adminConsoleTimeout);
 | 
			
		||||
            }
 | 
			
		||||
            return useTimeoutForAdminAccessingAdminConsole;
 | 
			
		||||
            return adminConsoleTimeout;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private boolean shouldUseTimeoutForAdminAccessingWebScriptsHome(RequiredAuthentication required, boolean isGuest)
 | 
			
		||||
        {
 | 
			
		||||
            boolean adminWebScriptsHomeTimeout = RequiredAuthentication.admin.equals(required) && !isGuest &&
 | 
			
		||||
                    servletReq.getServiceMatch() != null && isWebScriptsHome(servletReq.getServiceMatch().getWebScript());
 | 
			
		||||
 | 
			
		||||
            if (LOGGER.isTraceEnabled())
 | 
			
		||||
            {
 | 
			
		||||
                LOGGER.trace("Should ensure that the admins can login with basic auth: " + adminWebScriptsHomeTimeout);
 | 
			
		||||
            }
 | 
			
		||||
            return adminWebScriptsHomeTimeout;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private boolean isRemoteUserMapperActive()
 | 
			
		||||
@@ -296,7 +364,12 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
 | 
			
		||||
            return adminConsoleAuthenticator != null && (!(adminConsoleAuthenticator instanceof ActivateableBean) || ((ActivateableBean) adminConsoleAuthenticator).isActive());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected boolean isAdminConsoleWebScript(WebScript webScript)
 | 
			
		||||
        private boolean isWebScriptsHomeAuthenticatorActive()
 | 
			
		||||
        {
 | 
			
		||||
            return webScriptsHomeAuthenticator != null && (!(webScriptsHomeAuthenticator instanceof ActivateableBean) || ((ActivateableBean) webScriptsHomeAuthenticator).isActive());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected boolean isAdminConsole(WebScript webScript)
 | 
			
		||||
        {
 | 
			
		||||
            if (webScript == null || adminConsoleScriptFamilies == null || webScript.getDescription() == null
 | 
			
		||||
                    || webScript.getDescription().getFamilys() == null)
 | 
			
		||||
@@ -310,7 +383,7 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // intersect the "family" sets defined
 | 
			
		||||
            Set<String> families = new HashSet<String>(webScript.getDescription().getFamilys());
 | 
			
		||||
            Set<String> families = new HashSet<>(webScript.getDescription().getFamilys());
 | 
			
		||||
            families.retainAll(adminConsoleScriptFamilies);
 | 
			
		||||
            final boolean isAdminConsole = !families.isEmpty();
 | 
			
		||||
 | 
			
		||||
@@ -322,6 +395,23 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
 | 
			
		||||
            return isAdminConsole;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected boolean isWebScriptsHome(WebScript webScript)
 | 
			
		||||
        {
 | 
			
		||||
            if (webScript == null || webScript.toString() == null)
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            boolean isWebScriptsHome = webScript.toString().startsWith(WEB_SCRIPTS_BASE_PATH);
 | 
			
		||||
 | 
			
		||||
            if (LOGGER.isTraceEnabled() && isWebScriptsHome)
 | 
			
		||||
            {
 | 
			
		||||
                LOGGER.trace("Detected a WebScripts Home webscript: " + webScript);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return isWebScriptsHome;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected String getRemoteUserWithTimeout(boolean useTimeout) throws AuthenticationTimeoutException
 | 
			
		||||
        {
 | 
			
		||||
            if (!useTimeout)
 | 
			
		||||
@@ -417,7 +507,21 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
 | 
			
		||||
 | 
			
		||||
            if (isRemoteUserMapperActive())
 | 
			
		||||
            {
 | 
			
		||||
                userId = adminConsoleAuthenticator.getAdminConsoleUser(this.servletReq.getHttpServletRequest(), this.servletRes.getHttpServletResponse());
 | 
			
		||||
                userId = adminConsoleAuthenticator.getUserId(this.servletReq.getHttpServletRequest(), this.servletRes.getHttpServletResponse());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            logRemoteUserID(userId);
 | 
			
		||||
 | 
			
		||||
            return userId;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected String getWebScriptsHomeUser()
 | 
			
		||||
        {
 | 
			
		||||
            String userId = null;
 | 
			
		||||
 | 
			
		||||
            if (isRemoteUserMapperActive())
 | 
			
		||||
            {
 | 
			
		||||
                userId = webScriptsHomeAuthenticator.getUserId(this.servletReq.getHttpServletRequest(), this.servletRes.getHttpServletResponse());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            logRemoteUserID(userId);
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.fileupload2.core.FileItemInput;
 | 
			
		||||
import org.apache.commons.fileupload2.core.FileItemInputIterator;
 | 
			
		||||
import org.apache.commons.fileupload2.jakarta.JakartaServletFileUpload;
 | 
			
		||||
import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletFileUpload;
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
import org.springframework.extensions.webscripts.Status;
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.fileupload2.core.FileItemInput;
 | 
			
		||||
import org.apache.commons.fileupload2.core.FileItemInputIterator;
 | 
			
		||||
import org.apache.commons.fileupload2.jakarta.JakartaServletFileUpload;
 | 
			
		||||
import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletFileUpload;
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
import org.springframework.extensions.webscripts.Status;
 | 
			
		||||
 
 | 
			
		||||
@@ -712,7 +712,7 @@ public class PeopleImpl implements People
 | 
			
		||||
        Boolean isEnabled = person.isEnabled();
 | 
			
		||||
        if (isEnabled != null)
 | 
			
		||||
        {
 | 
			
		||||
            if (isAdminAuthority(personIdToUpdate))
 | 
			
		||||
            if (!isEnabled && isAdminAuthority(personIdToUpdate))
 | 
			
		||||
            {
 | 
			
		||||
                throw new PermissionDeniedException("Admin authority cannot be disabled.");
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,23 +2,23 @@
 | 
			
		||||
 * #%L
 | 
			
		||||
 * Alfresco Remote API
 | 
			
		||||
 * %%
 | 
			
		||||
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 | 
			
		||||
 * Copyright (C) 2005 - 2025 Alfresco Software Limited
 | 
			
		||||
 * %%
 | 
			
		||||
 * This file is part of the Alfresco software. 
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of 
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is 
 | 
			
		||||
 * This file is part of the Alfresco software.
 | 
			
		||||
 * If the software was purchased under a paid Alfresco license, the terms of
 | 
			
		||||
 * the paid license agreement will prevail.  Otherwise, the software is
 | 
			
		||||
 * provided under the following open source license terms:
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * Alfresco is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * Alfresco is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Lesser General Public License for more details.
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * #L%
 | 
			
		||||
@@ -38,14 +38,19 @@ import java.util.Collections;
 | 
			
		||||
import java.util.Comparator;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
import org.springframework.beans.factory.InitializingBean;
 | 
			
		||||
import org.springframework.extensions.surf.util.I18NUtil;
 | 
			
		||||
 | 
			
		||||
import org.alfresco.model.ContentModel;
 | 
			
		||||
import org.alfresco.query.PagingRequest;
 | 
			
		||||
import org.alfresco.repo.security.permissions.AccessDeniedException;
 | 
			
		||||
import org.alfresco.repo.site.SiteModel;
 | 
			
		||||
import org.alfresco.rest.api.Nodes;
 | 
			
		||||
import org.alfresco.rest.api.People;
 | 
			
		||||
@@ -89,6 +94,7 @@ import org.alfresco.util.SearchLanguageConversion;
 | 
			
		||||
 */
 | 
			
		||||
public class QueriesImpl implements Queries, InitializingBean
 | 
			
		||||
{
 | 
			
		||||
    private static final Log LOGGER = LogFactory.getLog(QueriesImpl.class);
 | 
			
		||||
    private final static Map<String, QName> NODE_SORT_PARAMS_TO_QNAMES = sortParamsToQNames(
 | 
			
		||||
            PARAM_NAME, ContentModel.PROP_NAME,
 | 
			
		||||
            PARAM_CREATEDAT, ContentModel.PROP_CREATED,
 | 
			
		||||
@@ -247,7 +253,7 @@ public class QueriesImpl implements Queries, InitializingBean
 | 
			
		||||
                            {
 | 
			
		||||
                                // first request for this namespace prefix, get and cache result
 | 
			
		||||
                                Collection<String> prefixes = namespaceService.getPrefixes(qname.getNamespaceURI());
 | 
			
		||||
                                prefix = prefixes.size() != 0 ? prefixes.iterator().next() : "";
 | 
			
		||||
                                prefix = !prefixes.isEmpty() ? prefixes.iterator().next() : "";
 | 
			
		||||
                                cache.put(qname.getNamespaceURI(), prefix);
 | 
			
		||||
                            }
 | 
			
		||||
                            buf.append('/').append(prefix).append(':').append(ISO9075.encode(qname.getLocalName()));
 | 
			
		||||
@@ -261,12 +267,6 @@ public class QueriesImpl implements Queries, InitializingBean
 | 
			
		||||
                return buf.toString();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected List<Node> newList(int capacity)
 | 
			
		||||
            {
 | 
			
		||||
                return new ArrayList<Node>(capacity);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected Node convert(NodeRef nodeRef, List<String> includeParam)
 | 
			
		||||
            {
 | 
			
		||||
@@ -303,18 +303,11 @@ public class QueriesImpl implements Queries, InitializingBean
 | 
			
		||||
                query.append("*\")");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected List<Person> newList(int capacity)
 | 
			
		||||
            {
 | 
			
		||||
                return new ArrayList<Person>(capacity);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected Person convert(NodeRef nodeRef, List<String> includeParam)
 | 
			
		||||
            {
 | 
			
		||||
                String personId = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME);
 | 
			
		||||
                Person person = people.getPerson(personId);
 | 
			
		||||
                return person;
 | 
			
		||||
                return people.getPerson(personId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // TODO Do the sort in the query on day. A comment in the code for the V0 API used for live people
 | 
			
		||||
@@ -341,28 +334,18 @@ public class QueriesImpl implements Queries, InitializingBean
 | 
			
		||||
                query.append("*\")");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected List<Site> newList(int capacity)
 | 
			
		||||
            {
 | 
			
		||||
                return new ArrayList<>(capacity);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected Site convert(NodeRef nodeRef, List<String> includeParam)
 | 
			
		||||
            {
 | 
			
		||||
                return getSite(siteService.getSite(nodeRef), true);
 | 
			
		||||
                return getSite(siteService.getSite(nodeRef));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // note: see also Sites.getSite
 | 
			
		||||
            private Site getSite(SiteInfo siteInfo, boolean includeRole)
 | 
			
		||||
            private Site getSite(SiteInfo siteInfo)
 | 
			
		||||
            {
 | 
			
		||||
                // set the site id to the short name (to deal with case sensitivity issues with using the siteId from the url)
 | 
			
		||||
                String siteId = siteInfo.getShortName();
 | 
			
		||||
                String role = null;
 | 
			
		||||
                if (includeRole)
 | 
			
		||||
                {
 | 
			
		||||
                    role = sites.getSiteRole(siteId);
 | 
			
		||||
                }
 | 
			
		||||
                String role = sites.getSiteRole(siteId);
 | 
			
		||||
                return new Site(siteInfo, role);
 | 
			
		||||
            }
 | 
			
		||||
        }.find(parameters, PARAM_TERM, MIN_TERM_LENGTH_SITES, "_SITE", POST_QUERY_SORT, SITE_SORT_PARAMS_TO_QNAMES, new SortColumn(PARAM_SITE_TITLE, true));
 | 
			
		||||
@@ -412,34 +395,38 @@ public class QueriesImpl implements Queries, InitializingBean
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ResultSet queryResults = null;
 | 
			
		||||
            List<T> collection = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                queryResults = searchService.query(sp);
 | 
			
		||||
 | 
			
		||||
                List<NodeRef> nodeRefs = queryResults.getNodeRefs();
 | 
			
		||||
 | 
			
		||||
                if (sort == POST_QUERY_SORT)
 | 
			
		||||
                {
 | 
			
		||||
                    nodeRefs = postQuerySort(parameters, sortParamsToQNames, defaultSortCols, nodeRefs);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                collection = newList(nodeRefs.size());
 | 
			
		||||
                Map<NodeRef, T> collection = new LinkedHashMap<>(nodeRefs.size());
 | 
			
		||||
                List<String> includeParam = parameters.getInclude();
 | 
			
		||||
 | 
			
		||||
                for (NodeRef nodeRef : nodeRefs)
 | 
			
		||||
                {
 | 
			
		||||
                    T t = convert(nodeRef, includeParam);
 | 
			
		||||
                    collection.add(t);
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        T t = convert(nodeRef, includeParam);
 | 
			
		||||
                        collection.put(nodeRef, t);
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (AccessDeniedException ade)
 | 
			
		||||
                    {
 | 
			
		||||
                        LOGGER.debug("Ignoring search result for nodeRef " + nodeRef + " due to access denied exception", ade);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (sort == POST_QUERY_SORT)
 | 
			
		||||
                {
 | 
			
		||||
                    return listPage(collection, paging);
 | 
			
		||||
                    List<T> postQuerySortedCollection = postQuerySort(parameters, sortParamsToQNames, defaultSortCols, collection.keySet())
 | 
			
		||||
                            .stream()
 | 
			
		||||
                            .map(collection::get)
 | 
			
		||||
                            .toList();
 | 
			
		||||
                    return listPage(postQuerySortedCollection, paging);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return CollectionWithPagingInfo.asPaged(paging, collection, queryResults.hasMore(), Long.valueOf(queryResults.getNumberFound()).intValue());
 | 
			
		||||
                    return CollectionWithPagingInfo.asPaged(paging, collection.values(), queryResults.hasMore(), Long.valueOf(queryResults.getNumberFound()).intValue());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
@@ -464,15 +451,6 @@ public class QueriesImpl implements Queries, InitializingBean
 | 
			
		||||
         */
 | 
			
		||||
        protected abstract void buildQuery(StringBuilder query, String term, SearchParameters sp, String queryTemplateName);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Returns a list of the correct type.
 | 
			
		||||
         * 
 | 
			
		||||
         * @param capacity
 | 
			
		||||
         *            of the list
 | 
			
		||||
         * @return a new list.
 | 
			
		||||
         */
 | 
			
		||||
        protected abstract List<T> newList(int capacity);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Converts a nodeRef into the an object of the required type.
 | 
			
		||||
         * 
 | 
			
		||||
@@ -551,7 +529,7 @@ public class QueriesImpl implements Queries, InitializingBean
 | 
			
		||||
        private List<SortColumn> getSorting(Parameters parameters, List<SortColumn> defaultSortCols)
 | 
			
		||||
        {
 | 
			
		||||
            List<SortColumn> sortCols = parameters.getSorting();
 | 
			
		||||
            if (sortCols == null || sortCols.size() == 0)
 | 
			
		||||
            if (sortCols == null || sortCols.isEmpty())
 | 
			
		||||
            {
 | 
			
		||||
                sortCols = defaultSortCols == null ? Collections.emptyList() : defaultSortCols;
 | 
			
		||||
            }
 | 
			
		||||
@@ -559,62 +537,66 @@ public class QueriesImpl implements Queries, InitializingBean
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected List<NodeRef> postQuerySort(Parameters parameters, Map<String, QName> sortParamsToQNames,
 | 
			
		||||
                List<SortColumn> defaultSortCols, List<NodeRef> nodeRefs)
 | 
			
		||||
                List<SortColumn> defaultSortCols, Set<NodeRef> unsortedNodeRefs)
 | 
			
		||||
        {
 | 
			
		||||
            final List<SortColumn> sortCols = getSorting(parameters, defaultSortCols);
 | 
			
		||||
            int sortColCount = sortCols.size();
 | 
			
		||||
            if (sortColCount > 0)
 | 
			
		||||
            {
 | 
			
		||||
                // make copy of nodeRefs because it can be unmodifiable list.
 | 
			
		||||
                nodeRefs = new ArrayList<NodeRef>(nodeRefs);
 | 
			
		||||
 | 
			
		||||
                List<QName> sortPropQNames = new ArrayList<>(sortColCount);
 | 
			
		||||
                for (SortColumn sortCol : sortCols)
 | 
			
		||||
            if (sortColCount == 0)
 | 
			
		||||
            {
 | 
			
		||||
                return new ArrayList<>(unsortedNodeRefs);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // make copy of nodeRefs because it can be unmodifiable list.
 | 
			
		||||
            List<NodeRef> sortedNodeRefs = new ArrayList<>(unsortedNodeRefs);
 | 
			
		||||
 | 
			
		||||
            List<QName> sortPropQNames = new ArrayList<>(sortColCount);
 | 
			
		||||
            for (SortColumn sortCol : sortCols)
 | 
			
		||||
            {
 | 
			
		||||
                QName sortPropQName = sortParamsToQNames.get(sortCol.column);
 | 
			
		||||
                if (sortPropQName == null)
 | 
			
		||||
                {
 | 
			
		||||
                    QName sortPropQName = sortParamsToQNames.get(sortCol.column);
 | 
			
		||||
                    if (sortPropQName == null)
 | 
			
		||||
                    throw new InvalidArgumentException("Invalid sort field: " + sortCol.column);
 | 
			
		||||
                }
 | 
			
		||||
                sortPropQNames.add(sortPropQName);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            final Collator col = AlfrescoCollator.getInstance(I18NUtil.getLocale());
 | 
			
		||||
            Collections.sort(sortedNodeRefs, new Comparator<NodeRef>() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public int compare(NodeRef n1, NodeRef n2)
 | 
			
		||||
                {
 | 
			
		||||
                    int result = 0;
 | 
			
		||||
                    for (int i = 0; i < sortCols.size(); i++)
 | 
			
		||||
                    {
 | 
			
		||||
                        throw new InvalidArgumentException("Invalid sort field: " + sortCol.column);
 | 
			
		||||
                        SortColumn sortCol = sortCols.get(i);
 | 
			
		||||
                        QName sortPropQName = sortPropQNames.get(i);
 | 
			
		||||
 | 
			
		||||
                        Serializable p1 = getProperty(n1, sortPropQName);
 | 
			
		||||
                        Serializable p2 = getProperty(n2, sortPropQName);
 | 
			
		||||
 | 
			
		||||
                        result = ((p1 instanceof Long) && (p2 instanceof Long)
 | 
			
		||||
                                ? Long.compare((Long) p1, (Long) p2)
 | 
			
		||||
                                : col.compare(p1.toString(), p2.toString()))
 | 
			
		||||
                                * (sortCol.asc ? 1 : -1);
 | 
			
		||||
 | 
			
		||||
                        if (result != 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    sortPropQNames.add(sortPropQName);
 | 
			
		||||
                    return result;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                final Collator col = AlfrescoCollator.getInstance(I18NUtil.getLocale());
 | 
			
		||||
                Collections.sort(nodeRefs, new Comparator<NodeRef>() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public int compare(NodeRef n1, NodeRef n2)
 | 
			
		||||
                    {
 | 
			
		||||
                        int result = 0;
 | 
			
		||||
                        for (int i = 0; i < sortCols.size(); i++)
 | 
			
		||||
                        {
 | 
			
		||||
                            SortColumn sortCol = sortCols.get(i);
 | 
			
		||||
                            QName sortPropQName = sortPropQNames.get(i);
 | 
			
		||||
                private Serializable getProperty(NodeRef nodeRef, QName sortPropQName)
 | 
			
		||||
                {
 | 
			
		||||
                    Serializable result = nodeService.getProperty(nodeRef, sortPropQName);
 | 
			
		||||
                    return result == null ? "" : result;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                            Serializable p1 = getProperty(n1, sortPropQName);
 | 
			
		||||
                            Serializable p2 = getProperty(n2, sortPropQName);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
                            result = ((p1 instanceof Long) && (p2 instanceof Long)
 | 
			
		||||
                                    ? Long.compare((Long) p1, (Long) p2)
 | 
			
		||||
                                    : col.compare(p1.toString(), p2.toString()))
 | 
			
		||||
                                    * (sortCol.asc ? 1 : -1);
 | 
			
		||||
 | 
			
		||||
                            if (result != 0)
 | 
			
		||||
                            {
 | 
			
		||||
                                break;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        return result;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    private Serializable getProperty(NodeRef nodeRef, QName sortPropQName)
 | 
			
		||||
                    {
 | 
			
		||||
                        Serializable result = nodeService.getProperty(nodeRef, sortPropQName);
 | 
			
		||||
                        return result == null ? "" : result;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            return nodeRefs;
 | 
			
		||||
            return sortedNodeRefs;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // note: see also AbstractNodeRelation
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user