mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	Compare commits
	
		
			85 Commits
		
	
	
		
			23.6.0.7
			...
			release/23
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 31d5f25ead | ||
|  | ca385b3bbc | ||
|  | 19c1582f1e | ||
|  | 06a918b082 | ||
|  | f543de9959 | ||
|  | 8124279e6a | ||
|  | 4281fd5b2d | ||
|  | d10d88306b | ||
|  | 1d7a37cd8c | ||
|  | 4bcb795452 | ||
|  | 393b064918 | ||
|  | f741f2ca45 | ||
|  | ef676f11e4 | ||
|  | 478c81fee3 | ||
|  | cf9cc8042d | ||
|  | 8d790ed1cb | ||
|  | 87c7bd2877 | ||
|  | 9125f889b0 | ||
|  | 2fb74d2691 | ||
|  | d671162dae | ||
|  | bfaa629da7 | ||
|  | 719d73a558 | ||
|  | a2aa867f3f | ||
|  | 8d745c536a | ||
|  | b0f4c21ae3 | ||
|  | 72494e34fa | ||
|  | 792b7024ea | ||
|  | 40a1371f0d | ||
|  | c22c47e63f | ||
|  | 232299d42d | ||
|  | aca7969849 | ||
|  | 4ab2bbd3d6 | ||
|  | f68f02372d | ||
|  | 9b0eedc8c1 | ||
|  | f164dedcee | ||
|  | 9cdaa0a265 | ||
|  | ef034e596b | ||
|  | 1251081a69 | ||
|  | 2d16eb6f42 | ||
|  | e577134875 | ||
|  | 510eadd565 | ||
|  | 187646895c | ||
|  | f9515e336f | ||
|  | 828dd20576 | ||
|  | 3372e20c35 | ||
|  | 64b5cace27 | ||
|  | 83acf26cf4 | ||
|  | b3be0f2b7f | ||
|  | 7a6ebb9a05 | ||
|  | fa0f239618 | ||
|  | 43799408a8 | ||
|  | e7305006f0 | ||
|  | 40c30411af | ||
|  | 91f8b43237 | ||
|  | 6fccf828e1 | ||
|  | 3fac3373c9 | ||
|  | ee857ce1de | ||
|  | 483d7fab21 | ||
|  | 590209b299 | ||
|  | 376514df67 | ||
|  | 7144a2dd94 | ||
|  | b4da3d8c20 | ||
|  | 62de9ff0c0 | ||
|  | a11acce720 | ||
|  | 1128011e15 | ||
|  | d0cb45de0d | ||
|  | 2b48195896 | ||
|  | fbb95d6a7f | ||
|  | 502427e852 | ||
|  | 3ff2d79641 | ||
|  | f274b88ece | ||
|  | 21550ec30b | ||
|  | 8665267225 | ||
|  | 984b0bc719 | ||
|  | 5b89fc0be7 | ||
|  | bf3a3382fd | ||
|  | 14d007fae8 | ||
|  | 79317ddc9d | ||
|  | c0e762fe5e | ||
|  | 5109b99520 | ||
|  | dfc6306331 | ||
|  | 731f98921f | ||
|  | 0b21dbdc0a | ||
|  | dd928356b8 | ||
|  | 1844d8bdb9 | 
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-community-repo-amps</artifactId> | ||||
|       <version>23.6.0.7</version> | ||||
|       <version>23.6.1.1-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-parent</artifactId> | ||||
|       <version>23.6.0.7</version> | ||||
|       <version>23.6.1.1-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-automation-community-repo</artifactId> | ||||
|       <version>23.6.0.7</version> | ||||
|       <version>23.6.1.1-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <build> | ||||
|   | ||||
| @@ -45,7 +45,7 @@ import com.github.dockerjava.netty.NettyDockerCmdExecFactory; | ||||
| import lombok.Getter; | ||||
| import lombok.Setter; | ||||
| import org.alfresco.utility.Utility; | ||||
| 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; | ||||
|   | ||||
| @@ -42,7 +42,7 @@ import org.alfresco.rest.v0.RMRolesAndActionsAPI; | ||||
| import org.alfresco.rest.v0.RecordsAPI; | ||||
| import org.alfresco.rest.v0.RecordCategoriesAPI; | ||||
| import org.alfresco.test.AlfrescoTest; | ||||
| 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; | ||||
|   | ||||
| @@ -44,7 +44,7 @@ import org.alfresco.rest.v0.service.DispositionScheduleService; | ||||
| import org.alfresco.test.AlfrescoTest; | ||||
| import org.alfresco.utility.model.RepoTestModel; | ||||
| import org.alfresco.utility.model.UserModel; | ||||
| 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; | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-parent</artifactId> | ||||
|       <version>23.6.0.7</version> | ||||
|       <version>23.6.1.1-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| SOLR6_TAG=2.0.13 | ||||
| SOLR6_TAG=2.0.17 | ||||
| POSTGRES_TAG=15.4 | ||||
| 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" | ||||
|   | ||||
| @@ -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>23.6.0.7</version> | ||||
|       <version>23.6.1.1-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <properties> | ||||
|   | ||||
| @@ -27,7 +27,6 @@ | ||||
|  | ||||
| package org.alfresco.module.org_alfresco_module_rm.query; | ||||
|  | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.HashMap; | ||||
| @@ -36,6 +35,11 @@ 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.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; | ||||
| @@ -47,9 +51,6 @@ import org.alfresco.service.cmr.repository.InvalidNodeRefException; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.namespace.QName; | ||||
| import org.alfresco.util.Pair; | ||||
| import org.apache.commons.logging.Log; | ||||
| import org.apache.commons.logging.LogFactory; | ||||
| import org.mybatis.spring.SqlSessionTemplate; | ||||
|  | ||||
| /** | ||||
|  * Records management query DAO implementation | ||||
| @@ -89,7 +90,8 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, | ||||
|     protected TenantService tenantService; | ||||
|  | ||||
|     /** | ||||
|      * @param sqlSessionTemplate SQL session template | ||||
|      * @param sqlSessionTemplate | ||||
|      *            SQL session template | ||||
|      */ | ||||
|     public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) | ||||
|     { | ||||
| @@ -97,7 +99,8 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param qnameDAO qname DAO | ||||
|      * @param qnameDAO | ||||
|      *            qname DAO | ||||
|      */ | ||||
|     public final void setQnameDAO(QNameDAO qnameDAO) | ||||
|     { | ||||
| @@ -173,8 +176,9 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, | ||||
|     /** | ||||
|      * Get a set of node reference which reference the provided content URL | ||||
|      * | ||||
|      * @param String contentUrl	content URL | ||||
|      * @return Set<NodeRef>	set of nodes that reference the provided content URL | ||||
|      * @param String | ||||
|      *            contentUrl content URL | ||||
|      * @return Set<NodeRef> set of nodes that reference the provided content URL | ||||
|      */ | ||||
|     @Override | ||||
|     public Set<NodeRef> getNodeRefsWhichReferenceContentUrl(String contentUrl) | ||||
| @@ -188,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()) | ||||
|         { | ||||
| @@ -224,7 +234,7 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, | ||||
|                     if (logger.isDebugEnabled()) | ||||
|                     { | ||||
|                         logMessage.append(nodeRefToAdd) | ||||
|                             .append(" (from version)"); | ||||
|                                 .append(" (from version)"); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
| @@ -232,7 +242,7 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, | ||||
|                 else | ||||
|                 { | ||||
|                     nodeRefToAdd = nodeDAO.getNodeIdStatus(nodeId) | ||||
|                         .getNodeRef(); | ||||
|                             .getNodeRef(); | ||||
|                     if (logger.isDebugEnabled()) | ||||
|                     { | ||||
|                         logMessage.append(nodeRefToAdd); | ||||
| @@ -266,9 +276,9 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, | ||||
|     { | ||||
|         Map<String, Object> params = new HashMap<>(2); | ||||
|         params.put("processed", qnameDAO.getQName(ASPECT_DISPOSITION_PROCESSED) | ||||
|             .getFirst()); | ||||
|                 .getFirst()); | ||||
|         params.put("folderQnameId", qnameDAO.getQName(TYPE_RECORD_FOLDER) | ||||
|             .getFirst()); | ||||
|                 .getFirst()); | ||||
|         params.put("start", start); | ||||
|         params.put("end", end); | ||||
|  | ||||
| @@ -280,7 +290,7 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, | ||||
|         for (NodeRefEntity nodeRefEntity : entities) | ||||
|         { | ||||
|             results.add( | ||||
|                 new NodeRef(nodeRefEntity.getProtocol(), nodeRefEntity.getIdentifier(), nodeRefEntity.getUuid())); | ||||
|                     new NodeRef(nodeRefEntity.getProtocol(), nodeRefEntity.getIdentifier(), nodeRefEntity.getUuid())); | ||||
|         } | ||||
|  | ||||
|         return results; | ||||
| @@ -289,7 +299,8 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, | ||||
|     /** | ||||
|      * @see org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAO#getPropertyStringValueEntity(String stringValue) | ||||
|      */ | ||||
|     public PropertyStringValueEntity getPropertyStringValueEntity(String stringValue){ | ||||
|     public PropertyStringValueEntity getPropertyStringValueEntity(String stringValue) | ||||
|     { | ||||
|  | ||||
|         PropertyStringValueEntity propertyStringValueEntity = new PropertyStringValueEntity(); | ||||
|         propertyStringValueEntity.setValue(stringValue); | ||||
|   | ||||
| @@ -34,6 +34,12 @@ import java.util.HashSet; | ||||
| 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; | ||||
| @@ -42,7 +48,10 @@ import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService; | ||||
| import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl; | ||||
| 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.security.authority.RMAuthority; | ||||
| import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; | ||||
| import org.alfresco.service.cmr.repository.ChildAssociationRef; | ||||
| import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| @@ -54,12 +63,6 @@ import org.alfresco.service.namespace.RegexQNamePattern; | ||||
| import org.alfresco.service.transaction.TransactionService; | ||||
| import org.alfresco.util.Pair; | ||||
| import org.alfresco.util.ParameterCheck; | ||||
| import org.springframework.context.ApplicationListener; | ||||
| import org.springframework.context.event.ContextRefreshedEvent; | ||||
| import org.springframework.extensions.webscripts.ui.common.StringUtils; | ||||
| import org.alfresco.repo.security.authentication.AuthenticationUtil; | ||||
| import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; | ||||
| import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; | ||||
|  | ||||
| /** | ||||
|  * Extended security service implementation. | ||||
| @@ -68,9 +71,9 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransacti | ||||
|  * @since 2.1 | ||||
|  */ | ||||
| public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|                                          implements ExtendedSecurityService, | ||||
|                                                     RecordsManagementModel, | ||||
|                                                     ApplicationListener<ContextRefreshedEvent> | ||||
|         implements ExtendedSecurityService, | ||||
|         RecordsManagementModel, | ||||
|         ApplicationListener<ContextRefreshedEvent> | ||||
| { | ||||
|     /** ipr group names */ | ||||
|     static final String ROOT_IPR_GROUP = "INPLACE_RECORD_MANAGEMENT"; | ||||
| @@ -95,8 +98,11 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** transaction service */ | ||||
|     private TransactionService transactionService; | ||||
|  | ||||
|     private boolean enableUsernameNormalization; | ||||
|  | ||||
|     /** | ||||
|      * @param filePlanService   file plan service | ||||
|      * @param filePlanService | ||||
|      *            file plan service | ||||
|      */ | ||||
|     public void setFilePlanService(FilePlanService filePlanService) | ||||
|     { | ||||
| @@ -104,7 +110,8 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param filePlanRoleService   file plan role service | ||||
|      * @param filePlanRoleService | ||||
|      *            file plan role service | ||||
|      */ | ||||
|     public void setFilePlanRoleService(FilePlanRoleService filePlanRoleService) | ||||
|     { | ||||
| @@ -112,7 +119,8 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param authorityService  authority service | ||||
|      * @param authorityService | ||||
|      *            authority service | ||||
|      */ | ||||
|     public void setAuthorityService(AuthorityService authorityService) | ||||
|     { | ||||
| @@ -120,7 +128,8 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param permissionService permission service | ||||
|      * @param permissionService | ||||
|      *            permission service | ||||
|      */ | ||||
|     public void setPermissionService(PermissionService permissionService) | ||||
|     { | ||||
| @@ -128,13 +137,23 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param transactionService    transaction service | ||||
|      * @param transactionService | ||||
|      *            transaction service | ||||
|      */ | ||||
|     public void setTransactionService(TransactionService transactionService) | ||||
|     { | ||||
|         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 | ||||
|      */ | ||||
| @@ -142,19 +161,17 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) | ||||
|     { | ||||
|         // run as System on bootstrap | ||||
|         AuthenticationUtil.runAs(new RunAsWork<Object>() | ||||
|         { | ||||
|         AuthenticationUtil.runAs(new RunAsWork<Object>() { | ||||
|             public Object doWork() | ||||
|             { | ||||
|                 RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>() | ||||
|                 { | ||||
|                 RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>() { | ||||
|                     public Void execute() | ||||
|                     { | ||||
|                         // if the root group doesn't exist then create it | ||||
|                         if (!authorityService.authorityExists(getRootIRPGroup())) | ||||
|                         { | ||||
|                             authorityService.createAuthority(AuthorityType.GROUP, ROOT_IPR_GROUP, ROOT_IPR_GROUP, | ||||
|                                         Collections.singleton(RMAuthority.ZONE_APP_RM)); | ||||
|                                     Collections.singleton(RMAuthority.ZONE_APP_RM)); | ||||
|                         } | ||||
|                         return null; | ||||
|                     } | ||||
| @@ -174,7 +191,7 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|         return GROUP_PREFIX + ROOT_IPR_GROUP; | ||||
|     } | ||||
|  | ||||
| 	/** | ||||
|     /** | ||||
|      * @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService#hasExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef) | ||||
|      */ | ||||
|     @Override | ||||
| @@ -224,8 +241,9 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * Helper to get authorities for a given group | ||||
|      * | ||||
|      * @param group         group name | ||||
|      * @return Set<String>  immediate authorities | ||||
|      * @param group | ||||
|      *            group name | ||||
|      * @return Set<String> immediate authorities | ||||
|      */ | ||||
|     private Set<String> getAuthorities(String group) | ||||
|     { | ||||
| @@ -284,8 +302,9 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|      * <p> | ||||
|      * Return null if none found. | ||||
|      * | ||||
|      * @param nodeRef                node reference | ||||
|      * @return Pair<String, String>  where first is the read group and second if the write group, null if none found | ||||
|      * @param nodeRef | ||||
|      *            node reference | ||||
|      * @return Pair<String, String> where first is the read group and second if the write group, null if none found | ||||
|      */ | ||||
|     private Pair<String, String> getIPRGroups(NodeRef nodeRef) | ||||
|     { | ||||
| @@ -321,17 +340,17 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * Given a set of readers and writers find or create the appropriate IPR groups. | ||||
|      * <p> | ||||
|      * The IPR groups are named with hashes of the authority lists in order to reduce | ||||
|      * the set of groups that require exact match.  A further index is used to handle | ||||
|      * a situation where there is a hash clash, but a difference in the authority lists. | ||||
|      * The IPR groups are named with hashes of the authority lists in order to reduce the set of groups that require exact match. A further index is used to handle a situation where there is a hash clash, but a difference in the authority lists. | ||||
|      * <p> | ||||
|      * When no match is found the groups are created.  Once created | ||||
|      * When no match is found the groups are created. Once created | ||||
|      * | ||||
|      * @param filePlan              file plan | ||||
|      * @param readers               authorities with read | ||||
|      * @param writers               authorities with write | ||||
|      * @return Pair<String, String> where first is the full name of the read group and | ||||
|      *                              second is the full name of the write group | ||||
|      * @param filePlan | ||||
|      *            file plan | ||||
|      * @param readers | ||||
|      *            authorities with read | ||||
|      * @param writers | ||||
|      *            authorities with write | ||||
|      * @return Pair<String, String> where first is the full name of the read group and second is the full name of the write group | ||||
|      */ | ||||
|     private Pair<String, String> createOrFindIPRGroups(Set<String> readers, Set<String> writers) | ||||
|     { | ||||
| @@ -343,20 +362,28 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * Create or find an IPR group based on the provided prefix and authorities. | ||||
|      * | ||||
|      * @param groupPrefix   group prefix | ||||
|      * @param authorities   authorities | ||||
|      * @return String       full group name | ||||
|      * @param groupPrefix | ||||
|      *            group prefix | ||||
|      * @param authorities | ||||
|      *            authorities | ||||
|      * @return String full group name | ||||
|      */ | ||||
|     private String createOrFindIPRGroup(String groupPrefix, Set<String> authorities) | ||||
|     { | ||||
|         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 | ||||
|         { | ||||
| @@ -369,13 +396,13 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * Given a group name prefix and the authorities, finds the exact match existing group. | ||||
|      * <p> | ||||
|      * If the group does not exist then the group returned is null and the index shows the next available | ||||
|      * group index for creation. | ||||
|      * If the group does not exist then the group returned is null and the index shows the next available group index for creation. | ||||
|      * | ||||
|      * @param groupPrefix             group name prefix | ||||
|      * @param authorities             authorities | ||||
|      * @return Pair<String, Integer>  where first is the name of the found group, null if none found and second | ||||
|      *                                if the next available create index | ||||
|      * @param groupPrefix | ||||
|      *            group name prefix | ||||
|      * @param authorities | ||||
|      *            authorities | ||||
|      * @return Pair<String, Integer> where first is the name of the found group, null if none found and second if the next available create index | ||||
|      */ | ||||
|     private Pair<String, Integer> findIPRGroup(String groupPrefix, Set<String> authorities) | ||||
|     { | ||||
| @@ -391,12 +418,13 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|         while (hasMoreItems == true) | ||||
|         { | ||||
|             // get matching authorities | ||||
|             PagingResults<String> results = authorityService.getAuthorities(AuthorityType.GROUP, | ||||
|                         RMAuthority.ZONE_APP_RM, | ||||
|                         groupShortNamePrefix, | ||||
|                         false, | ||||
|                         false, | ||||
|                         new PagingRequest(MAX_ITEMS*pageCount, MAX_ITEMS)); | ||||
|             PagingResults<String> results = authorityService.getAuthorities( | ||||
|                     AuthorityType.GROUP, | ||||
|                     RMAuthority.ZONE_APP_RM, | ||||
|                     groupShortNamePrefix, | ||||
|                     false, | ||||
|                     false, | ||||
|                     new PagingRequest(MAX_ITEMS * pageCount, MAX_ITEMS)); | ||||
|  | ||||
|             // record the total count | ||||
|             nextGroupIndex = nextGroupIndex + results.getPage().size(); | ||||
| @@ -413,29 +441,88 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|  | ||||
|             // determine if there are any more pages to inspect | ||||
|             hasMoreItems = results.hasMoreItems(); | ||||
|             pageCount ++; | ||||
|             pageCount++; | ||||
|         } | ||||
|  | ||||
|         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. | ||||
|      * | ||||
|      * @param authorities           list of authorities | ||||
|      * @param group                 group | ||||
|      * @param authorities | ||||
|      *            list of authorities | ||||
|      * @param group | ||||
|      *            group | ||||
|      * @return | ||||
|      */ | ||||
|     private boolean isIPRGroupTrueMatch(String group, Set<String> authorities) | ||||
|     { | ||||
|         //Remove GROUP_EVERYONE for proper comparison as GROUP_EVERYONE is never included in an IPR group | ||||
|         // Remove GROUP_EVERYONE for proper comparison as GROUP_EVERYONE is never included in an IPR group | ||||
|         Set<String> plainAuthorities = new HashSet<String>(); | ||||
|         if (authorities != null) | ||||
|         { | ||||
|             plainAuthorities.addAll(authorities); | ||||
|             plainAuthorities.remove(PermissionService.ALL_AUTHORITIES); | ||||
|         } | ||||
|         Set<String> contained =  authorityService.getContainedAuthorities(null, group, true); | ||||
|         Set<String> contained = authorityService.getContainedAuthorities(null, group, true); | ||||
|         return contained.equals(plainAuthorities); | ||||
|     } | ||||
|  | ||||
| @@ -444,15 +531,17 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|      * <p> | ||||
|      * 'package' scope to help testing. | ||||
|      * | ||||
|      * @param prefix        prefix | ||||
|      * @param authorities   authorities | ||||
|      * @return String       group prefix short name | ||||
|      * @param prefix | ||||
|      *            prefix | ||||
|      * @param authorities | ||||
|      *            authorities | ||||
|      * @return String group prefix short name | ||||
|      */ | ||||
|     /*package*/ String getIPRGroupPrefixShortName(String prefix, Set<String> authorities) | ||||
|     /* package */ String getIPRGroupPrefixShortName(String prefix, Set<String> authorities) | ||||
|     { | ||||
|         StringBuilder builder = new StringBuilder(128) | ||||
|                .append(prefix) | ||||
|                .append(getAuthoritySetHashCode(authorities)); | ||||
|                 .append(prefix) | ||||
|                 .append(getAuthoritySetHashCode(authorities)); | ||||
|  | ||||
|         return builder.toString(); | ||||
|     } | ||||
| @@ -464,13 +553,17 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|      * <p> | ||||
|      * 'package' scope to help testing. | ||||
|      * | ||||
|      * @param prefix    prefix | ||||
|      * @param readers   read authorities | ||||
|      * @param writers   write authorities | ||||
|      * @param index     group index | ||||
|      * @return String   group short name | ||||
|      * @param prefix | ||||
|      *            prefix | ||||
|      * @param readers | ||||
|      *            read authorities | ||||
|      * @param writers | ||||
|      *            write authorities | ||||
|      * @param index | ||||
|      *            group index | ||||
|      * @return String group short name | ||||
|      */ | ||||
|     /*package*/ String getIPRGroupShortName(String prefix, Set<String> authorities, int index) | ||||
|     /* package */ String getIPRGroupShortName(String prefix, Set<String> authorities, int index) | ||||
|     { | ||||
|         return getIPRGroupShortName(prefix, authorities, Integer.toString(index)); | ||||
|     } | ||||
| @@ -480,17 +573,21 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|      * <p> | ||||
|      * Note this excludes the "GROUP_" prefix. | ||||
|      * | ||||
|      * @param prefix    prefix | ||||
|      * @param readers   read authorities | ||||
|      * @param writers   write authorities | ||||
|      * @param index     group index | ||||
|      * @return String   group short name | ||||
|      * @param prefix | ||||
|      *            prefix | ||||
|      * @param readers | ||||
|      *            read authorities | ||||
|      * @param writers | ||||
|      *            write authorities | ||||
|      * @param index | ||||
|      *            group index | ||||
|      * @return String group short name | ||||
|      */ | ||||
|     private String getIPRGroupShortName(String prefix, Set<String> authorities, String index) | ||||
|     { | ||||
|         StringBuilder builder = new StringBuilder(128) | ||||
|                .append(getIPRGroupPrefixShortName(prefix, authorities)) | ||||
|                .append(index); | ||||
|                 .append(getIPRGroupPrefixShortName(prefix, authorities)) | ||||
|                 .append(index); | ||||
|  | ||||
|         return builder.toString(); | ||||
|     } | ||||
| @@ -498,8 +595,9 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * Gets the hashcode value of a set of authorities. | ||||
|      * | ||||
|      * @param authorities   set of authorities | ||||
|      * @return int          hash code | ||||
|      * @param authorities | ||||
|      *            set of authorities | ||||
|      * @return int hash code | ||||
|      */ | ||||
|     private int getAuthoritySetHashCode(Set<String> authorities) | ||||
|     { | ||||
| @@ -514,10 +612,13 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * Creates a new IPR group. | ||||
|      * | ||||
|      * @param groupNamePrefix   group name prefix | ||||
|      * @param children          child authorities | ||||
|      * @param index             group index | ||||
|      * @return String           full name of created group | ||||
|      * @param groupNamePrefix | ||||
|      *            group name prefix | ||||
|      * @param children | ||||
|      *            child authorities | ||||
|      * @param index | ||||
|      *            group index | ||||
|      * @return String full name of created group | ||||
|      */ | ||||
|     private String createIPRGroup(String groupNamePrefix, Set<String> children, int index) | ||||
|     { | ||||
| @@ -547,10 +648,10 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         catch(DuplicateChildNodeNameException ex) | ||||
|         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; | ||||
| @@ -559,8 +660,10 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * Assign IPR groups to a node reference with the correct permissions. | ||||
|      * | ||||
|      * @param iprGroups iprGroups, first read and second write | ||||
|      * @param nodeRef   node reference | ||||
|      * @param iprGroups | ||||
|      *            iprGroups, first read and second write | ||||
|      * @param nodeRef | ||||
|      *            node reference | ||||
|      */ | ||||
|     private void assignIPRGroupsToNode(Pair<String, String> iprGroups, NodeRef nodeRef) | ||||
|     { | ||||
| @@ -598,7 +701,8 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * Clear the nodes IPR permissions | ||||
|      * | ||||
|      * @param nodeRef   node reference | ||||
|      * @param nodeRef | ||||
|      *            node reference | ||||
|      */ | ||||
|     private void clearPermissions(NodeRef nodeRef, Pair<String, String> iprGroups) | ||||
|     { | ||||
| @@ -610,7 +714,9 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#getExtendedReaders(org.alfresco.service.cmr.repository.NodeRef) | ||||
|      */ | ||||
|     @Override @Deprecated public Set<String> getExtendedReaders(NodeRef nodeRef) | ||||
|     @Override | ||||
|     @Deprecated | ||||
|     public Set<String> getExtendedReaders(NodeRef nodeRef) | ||||
|     { | ||||
|         return getReaders(nodeRef); | ||||
|     } | ||||
| @@ -618,7 +724,9 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#getExtendedWriters(org.alfresco.service.cmr.repository.NodeRef) | ||||
|      */ | ||||
|     @Override @Deprecated public Set<String> getExtendedWriters(NodeRef nodeRef) | ||||
|     @Override | ||||
|     @Deprecated | ||||
|     public Set<String> getExtendedWriters(NodeRef nodeRef) | ||||
|     { | ||||
|         return getWriters(nodeRef); | ||||
|     } | ||||
| @@ -626,7 +734,9 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#addExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, java.util.Set, java.util.Set) | ||||
|      */ | ||||
|     @Override @Deprecated public void addExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers) | ||||
|     @Override | ||||
|     @Deprecated | ||||
|     public void addExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers) | ||||
|     { | ||||
|         set(nodeRef, readers, writers); | ||||
|     } | ||||
| @@ -634,7 +744,9 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#addExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, java.util.Set, java.util.Set, boolean) | ||||
|      */ | ||||
|     @Override @Deprecated public void addExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers, boolean applyToParents) | ||||
|     @Override | ||||
|     @Deprecated | ||||
|     public void addExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers, boolean applyToParents) | ||||
|     { | ||||
|         set(nodeRef, readers, writers); | ||||
|     } | ||||
| @@ -642,7 +754,9 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#removeAllExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef) | ||||
|      */ | ||||
|     @Override @Deprecated public void removeAllExtendedSecurity(NodeRef nodeRef) | ||||
|     @Override | ||||
|     @Deprecated | ||||
|     public void removeAllExtendedSecurity(NodeRef nodeRef) | ||||
|     { | ||||
|         remove(nodeRef); | ||||
|     } | ||||
| @@ -650,7 +764,9 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#removeExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, java.util.Set, java.util.Set) | ||||
|      */ | ||||
|     @Override @Deprecated public void removeExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers) | ||||
|     @Override | ||||
|     @Deprecated | ||||
|     public void removeExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers) | ||||
|     { | ||||
|         remove(nodeRef); | ||||
|     } | ||||
| @@ -658,7 +774,9 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#removeExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, java.util.Set, java.util.Set, boolean) | ||||
|      */ | ||||
|     @Override @Deprecated public void removeExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String>writers, boolean applyToParents) | ||||
|     @Override | ||||
|     @Deprecated | ||||
|     public void removeExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers, boolean applyToParents) | ||||
|     { | ||||
|         remove(nodeRef); | ||||
|     } | ||||
| @@ -666,7 +784,9 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl | ||||
|     /** | ||||
|      * @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#removeAllExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, boolean) | ||||
|      */ | ||||
|     @Override @Deprecated public void removeAllExtendedSecurity(NodeRef nodeRef, boolean applyToParents) | ||||
|     @Override | ||||
|     @Deprecated | ||||
|     public void removeAllExtendedSecurity(NodeRef nodeRef, boolean applyToParents) | ||||
|     { | ||||
|         remove(nodeRef); | ||||
|     } | ||||
|   | ||||
| @@ -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; | ||||
| @@ -73,8 +82,7 @@ public class ExtendedSecurityServiceImplTest extends BaseRMTestCase | ||||
|  | ||||
|     private String createTestUser() | ||||
|     { | ||||
|         return doTestInTransaction(new Test<String>() | ||||
|         { | ||||
|         return doTestInTransaction(new Test<String>() { | ||||
|             public String run() | ||||
|             { | ||||
|                 String userName = GUID.generate(); | ||||
| @@ -90,8 +98,7 @@ public class ExtendedSecurityServiceImplTest extends BaseRMTestCase | ||||
|         final String elephant = createTestUser(); | ||||
|         final String snake = createTestUser(); | ||||
|  | ||||
|         doTestInTransaction(new Test<Void>() | ||||
|         { | ||||
|         doTestInTransaction(new Test<Void>() { | ||||
|             public Void run() | ||||
|             { | ||||
|                 assertFalse(extendedSecurityService.hasExtendedSecurity(filePlan)); | ||||
| @@ -118,7 +125,7 @@ public class ExtendedSecurityServiceImplTest extends BaseRMTestCase | ||||
|  | ||||
|                 // test remove | ||||
|                 extendedSecurityService.remove(recordToo); | ||||
|                  | ||||
|  | ||||
|                 assertFalse(extendedSecurityService.hasExtendedSecurity(recordToo)); | ||||
|                 assertTrue(extendedSecurityService.getReaders(recordToo).isEmpty()); | ||||
|                 assertTrue(extendedSecurityService.getWriters(recordToo).isEmpty()); | ||||
| @@ -133,8 +140,7 @@ public class ExtendedSecurityServiceImplTest extends BaseRMTestCase | ||||
|         final String monkey = createTestUser(); | ||||
|         final String elephant = createTestUser(); | ||||
|  | ||||
|         doTestInTransaction(new Test<Void>() | ||||
|         { | ||||
|         doTestInTransaction(new Test<Void>() { | ||||
|             Set<String> extendedReaders = new HashSet<>(2); | ||||
|  | ||||
|             public Void run() throws Exception | ||||
| @@ -184,112 +190,337 @@ public class ExtendedSecurityServiceImplTest extends BaseRMTestCase | ||||
|  | ||||
|     public void testDifferentUsersDifferentPermissions() | ||||
|     { | ||||
|     	final String userNone = createTestUser(); | ||||
|     	final String userRead = createTestUser(); | ||||
|     	final String userWrite = createTestUser(); | ||||
|     	final String siteShortName = GUID.generate(); | ||||
|         final String userNone = createTestUser(); | ||||
|         final String userRead = createTestUser(); | ||||
|         final String userWrite = createTestUser(); | ||||
|         final String siteShortName = GUID.generate(); | ||||
|  | ||||
|     	doTestInTransaction(new Test<Void>() | ||||
|         { | ||||
|         doTestInTransaction(new Test<Void>() { | ||||
|             public Void run() throws Exception | ||||
|             { | ||||
|             	siteService.createSite(null, siteShortName, "test", "test", SiteVisibility.PRIVATE); | ||||
|             	return null; | ||||
|                 siteService.createSite(null, siteShortName, "test", "test", SiteVisibility.PRIVATE); | ||||
|                 return null; | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|     	final NodeRef documentLibrary = doTestInTransaction(new Test<NodeRef>() | ||||
|         { | ||||
|         final NodeRef documentLibrary = doTestInTransaction(new Test<NodeRef>() { | ||||
|             public NodeRef run() throws Exception | ||||
|             { | ||||
|             	siteService.setMembership(siteShortName, userRead, SiteModel.SITE_CONSUMER); | ||||
|             	siteService.setMembership(siteShortName, userWrite, SiteModel.SITE_COLLABORATOR); | ||||
|             	return siteService.createContainer(siteShortName, SiteService.DOCUMENT_LIBRARY, null, null); | ||||
|                 siteService.setMembership(siteShortName, userRead, SiteModel.SITE_CONSUMER); | ||||
|                 siteService.setMembership(siteShortName, userWrite, SiteModel.SITE_COLLABORATOR); | ||||
|                 return siteService.createContainer(siteShortName, SiteService.DOCUMENT_LIBRARY, null, null); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|     	final NodeRef record = doTestInTransaction(new Test<NodeRef>() | ||||
|         { | ||||
|         final NodeRef record = doTestInTransaction(new Test<NodeRef>() { | ||||
|             public NodeRef run() throws Exception | ||||
|             { | ||||
|             	NodeRef record = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef(); | ||||
|             	recordService.createRecord(filePlan, record); | ||||
|             	return record; | ||||
|                 NodeRef record = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_CONTENT) | ||||
|                         .getNodeRef(); | ||||
|                 recordService.createRecord(filePlan, record); | ||||
|                 return record; | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|     	doTestInTransaction(new Test<Void>() | ||||
|         { | ||||
|         doTestInTransaction(new Test<Void>() { | ||||
|             public Void run() throws Exception | ||||
|             { | ||||
|             	AuthenticationUtil.runAs(new RunAsWork<Void>() | ||||
|             	{ | ||||
| 					public Void doWork() throws Exception | ||||
| 					{ | ||||
| 						// check permissions | ||||
| 		            	assertEquals(AccessStatus.DENIED, permissionService.hasPermission(record, READ_RECORDS)); | ||||
| 		            	assertEquals(AccessStatus.DENIED, permissionService.hasPermission(record, FILING)); | ||||
| 						return null; | ||||
| 					} | ||||
| 				}, userNone); | ||||
|                 AuthenticationUtil.runAs(new RunAsWork<Void>() { | ||||
|                     public Void doWork() throws Exception | ||||
|                     { | ||||
|                         // check permissions | ||||
|                         assertEquals(AccessStatus.DENIED, permissionService.hasPermission(record, READ_RECORDS)); | ||||
|                         assertEquals(AccessStatus.DENIED, permissionService.hasPermission(record, FILING)); | ||||
|                         return null; | ||||
|                     } | ||||
|                 }, userNone); | ||||
|  | ||||
|             	AuthenticationUtil.runAs(new RunAsWork<Void>() | ||||
|             	{ | ||||
| 					public Void doWork() throws Exception | ||||
| 					{ | ||||
| 						// check permissions | ||||
| 		            	assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(record, READ_RECORDS)); | ||||
| 		            	assertEquals(AccessStatus.DENIED, permissionService.hasPermission(record, FILING)); | ||||
| 						return null; | ||||
| 					} | ||||
| 				}, userRead); | ||||
|                 AuthenticationUtil.runAs(new RunAsWork<Void>() { | ||||
|                     public Void doWork() throws Exception | ||||
|                     { | ||||
|                         // check permissions | ||||
|                         assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(record, READ_RECORDS)); | ||||
|                         assertEquals(AccessStatus.DENIED, permissionService.hasPermission(record, FILING)); | ||||
|                         return null; | ||||
|                     } | ||||
|                 }, userRead); | ||||
|  | ||||
|             	AuthenticationUtil.runAs(new RunAsWork<Void>() | ||||
|             	{ | ||||
| 					public Void doWork() throws Exception | ||||
| 					{ | ||||
| 						// check permissions | ||||
| 		            	assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(record, READ_RECORDS)); | ||||
| 		            	assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(record, FILING)); | ||||
| 						return null; | ||||
| 					} | ||||
| 				}, userWrite); | ||||
|                 AuthenticationUtil.runAs(new RunAsWork<Void>() { | ||||
|                     public Void doWork() throws Exception | ||||
|                     { | ||||
|                         // check permissions | ||||
|                         assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(record, READ_RECORDS)); | ||||
|                         assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(record, FILING)); | ||||
|                         return null; | ||||
|                     } | ||||
|                 }, userWrite); | ||||
|  | ||||
|             	AuthenticationUtil.runAs(new RunAsWork<Void>() | ||||
|             	{ | ||||
| 					public Void doWork() throws Exception | ||||
| 					{ | ||||
| 						// check permissions | ||||
| 		            	assertEquals(AccessStatus.DENIED, permissionService.hasPermission(record, READ_RECORDS)); | ||||
| 		            	assertEquals(AccessStatus.DENIED, permissionService.hasPermission(record, FILING)); | ||||
| 						return null; | ||||
| 					} | ||||
| 				}, userNone); | ||||
|                 AuthenticationUtil.runAs(new RunAsWork<Void>() { | ||||
|                     public Void doWork() throws Exception | ||||
|                     { | ||||
|                         // check permissions | ||||
|                         assertEquals(AccessStatus.DENIED, permissionService.hasPermission(record, READ_RECORDS)); | ||||
|                         assertEquals(AccessStatus.DENIED, permissionService.hasPermission(record, FILING)); | ||||
|                         return null; | ||||
|                     } | ||||
|                 }, userNone); | ||||
|  | ||||
|             	AuthenticationUtil.runAs(new RunAsWork<Void>() | ||||
|             	{ | ||||
| 					public Void doWork() throws Exception | ||||
| 					{ | ||||
| 						// check permissions | ||||
| 		            	assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(record, READ_RECORDS)); | ||||
| 		            	assertEquals(AccessStatus.DENIED, permissionService.hasPermission(record, FILING)); | ||||
| 						return null; | ||||
| 					} | ||||
| 				}, userRead); | ||||
|                 AuthenticationUtil.runAs(new RunAsWork<Void>() { | ||||
|                     public Void doWork() throws Exception | ||||
|                     { | ||||
|                         // check permissions | ||||
|                         assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(record, READ_RECORDS)); | ||||
|                         assertEquals(AccessStatus.DENIED, permissionService.hasPermission(record, FILING)); | ||||
|                         return null; | ||||
|                     } | ||||
|                 }, userRead); | ||||
|  | ||||
|             	AuthenticationUtil.runAs(new RunAsWork<Void>() | ||||
|             	{ | ||||
| 					public Void doWork() throws Exception | ||||
| 					{ | ||||
| 						// check permissions | ||||
| 		            	assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(record, READ_RECORDS)); | ||||
| 		            	assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(record, FILING)); | ||||
| 						return null; | ||||
| 					} | ||||
| 				}, userWrite); | ||||
|                 AuthenticationUtil.runAs(new RunAsWork<Void>() { | ||||
|                     public Void doWork() throws Exception | ||||
|                     { | ||||
|                         // check permissions | ||||
|                         assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(record, READ_RECORDS)); | ||||
|                         assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(record, FILING)); | ||||
|                         return null; | ||||
|                     } | ||||
|                 }, userWrite); | ||||
|  | ||||
|             	return null; | ||||
|                 return null; | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| # Version label | ||||
| version.major=23 | ||||
| version.minor=6 | ||||
| version.revision=0 | ||||
| version.revision=1 | ||||
| version.label= | ||||
|  | ||||
| # Edition 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); | ||||
|          | ||||
|  | ||||
|     } | ||||
| } | ||||
| } | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-governance-services-community-repo-parent</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <build> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-amps</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</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> | ||||
|  | ||||
|   | ||||
| @@ -30,7 +30,7 @@ import java.util.regex.Pattern; | ||||
|  | ||||
| import org.alfresco.service.cmr.site.SiteInfo; | ||||
| import org.alfresco.service.cmr.wiki.WikiPageInfo; | ||||
| 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,28 @@ function doclist_main() | ||||
|     | ||||
|    if (logger.isLoggingEnabled()) | ||||
|       logger.log("doclist.lib.js - NodeRef: " + parsedArgs.nodeRef + " Query: " + query); | ||||
|  | ||||
|    favourites = sanitizeJunkFavouriteKeys(favourites); | ||||
|  | ||||
|    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; | ||||
|   | ||||
| @@ -45,7 +45,7 @@ import org.alfresco.service.cmr.wiki.WikiPageInfo; | ||||
| import org.alfresco.service.cmr.wiki.WikiService; | ||||
| import org.alfresco.service.transaction.TransactionService; | ||||
| import org.alfresco.util.PropertyMap; | ||||
| 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; | ||||
| @@ -996,7 +996,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); | ||||
|     		} | ||||
|        | ||||
| @@ -1006,4 +1006,4 @@ public class WikiRestApiTest extends BaseWebScriptTest | ||||
|     		 this.wikiService.deleteWikiPage(wikiPageNew);  | ||||
|     	} | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-community-repo</artifactId> | ||||
|       <version>23.6.0.7</version> | ||||
|       <version>23.6.1.1-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <dependencies> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
|   | ||||
| @@ -9,6 +9,6 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
| </project> | ||||
|   | ||||
| @@ -36,8 +36,7 @@ commons-email   http://jakarta.apache.org/commons/ | ||||
| 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-jxpath  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/ | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| SOLR6_TAG=2.0.13 | ||||
| SOLR6_TAG=2.0.17 | ||||
| POSTGRES_TAG=15.4 | ||||
| ACTIVEMQ_TAG=5.18.3-jre17-rockylinux8 | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <organization> | ||||
|   | ||||
| @@ -16,7 +16,7 @@ import org.alfresco.utility.testrail.annotation.TestRail; | ||||
| 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; | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</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> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
							
								
								
									
										40
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								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>23.6.0.7</version> | ||||
|     <version>23.6.1.1-SNAPSHOT</version> | ||||
|     <packaging>pom</packaging> | ||||
|     <name>Alfresco Community Repo Parent</name> | ||||
|  | ||||
| @@ -25,7 +25,7 @@ | ||||
|     <properties> | ||||
|         <acs.version.major>23</acs.version.major> | ||||
|         <acs.version.minor>6</acs.version.minor> | ||||
|         <acs.version.revision>0</acs.version.revision> | ||||
|         <acs.version.revision>1</acs.version.revision> | ||||
|         <acs.version.label /> | ||||
|         <amp.min.version>${acs.version.major}.0.0</amp.min.version> | ||||
|  | ||||
| @@ -51,17 +51,17 @@ | ||||
|         <dependency.alfresco-server-root.version>7.0.1</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.5</dependency.alfresco-transform-core.version> | ||||
|         <dependency.alfresco-transform-service.version>4.1.5</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.0</dependency.alfresco-greenmail.version> | ||||
|         <dependency.acs-event-model.version>0.0.33</dependency.acs-event-model.version> | ||||
|  | ||||
|         <dependency.aspectj.version>1.9.22.1</dependency.aspectj.version> | ||||
|         <dependency.spring.version>6.2.8</dependency.spring.version> | ||||
|         <dependency.spring-security.version>6.3.9</dependency.spring-security.version> | ||||
|         <dependency.spring.version>6.2.11</dependency.spring.version> | ||||
|         <dependency.spring-security.version>6.4.11</dependency.spring-security.version> | ||||
|         <dependency.antlr.version>3.5.3</dependency.antlr.version> | ||||
|         <dependency.jackson.version>2.17.2</dependency.jackson.version> | ||||
|         <dependency.cxf.version>4.1.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.2</dependency.webscripts.version> | ||||
|         <dependency.bouncycastle.version>1.78.1</dependency.bouncycastle.version> | ||||
| @@ -81,7 +81,7 @@ | ||||
|         <dependency.slf4j.version>2.0.16</dependency.slf4j.version> | ||||
|         <dependency.log4j.version>2.23.1</dependency.log4j.version> | ||||
|         <dependency.groovy.version>3.0.22</dependency.groovy.version> | ||||
|         <dependency.tika.version>2.9.2</dependency.tika.version> | ||||
|         <dependency.tika.version>3.2.3</dependency.tika.version> | ||||
|         <dependency.truezip.version>7.7.10</dependency.truezip.version> | ||||
|         <dependency.poi.version>5.4.1</dependency.poi.version> | ||||
|         <dependency.jboss.logging.version>3.5.0.Final</dependency.jboss.logging.version> | ||||
| @@ -113,7 +113,7 @@ | ||||
|         <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</alfresco.aos-module.version> | ||||
|         <alfresco.aos-module.version>3.4.0</alfresco.aos-module.version> | ||||
|         <alfresco.api-explorer.version>23.4.0</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share --> | ||||
|  | ||||
|         <alfresco.maven-plugin.version>2.2.0</alfresco.maven-plugin.version> | ||||
| @@ -154,7 +154,7 @@ | ||||
|         <connection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</connection> | ||||
|         <developerConnection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</developerConnection> | ||||
|         <url>https://github.com/Alfresco/alfresco-community-repo</url> | ||||
|         <tag>23.6.0.7</tag> | ||||
|         <tag>HEAD</tag> | ||||
|     </scm> | ||||
|  | ||||
|     <distributionManagement> | ||||
| @@ -170,6 +170,12 @@ | ||||
|  | ||||
|     <dependencyManagement> | ||||
|         <dependencies> | ||||
|             <!-- v1.10 has 0BSD license it must be consulted with Legal --> | ||||
|             <dependency> | ||||
|                 <groupId>org.tukaani</groupId> | ||||
|                 <artifactId>xz</artifactId> | ||||
|                 <version>1.9</version> | ||||
|             </dependency> | ||||
|             <!-- Jakarta... --> | ||||
|             <dependency> | ||||
|                 <groupId>jakarta.xml.bind</groupId> | ||||
| @@ -417,9 +423,9 @@ | ||||
|                 <version>1.17.1</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> | ||||
| @@ -1132,16 +1138,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>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>23.6.0.7</version> | ||||
|         <version>23.6.1.1-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
| @@ -94,7 +94,6 @@ | ||||
|         <dependency> | ||||
|             <groupId>org.apache.commons</groupId> | ||||
|             <artifactId>commons-lang3</artifactId> | ||||
|             <version>3.17.0</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>commons-codec</groupId> | ||||
| @@ -742,10 +741,6 @@ | ||||
|             <artifactId>reflections</artifactId> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>commons-lang</groupId> | ||||
|             <artifactId>commons-lang</artifactId> | ||||
|         </dependency> | ||||
|     </dependencies> | ||||
|  | ||||
|     <build> | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -2,7 +2,7 @@ | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited | ||||
|  * Copyright (C) 2005 - 2025 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software.  | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of  | ||||
| @@ -23,105 +23,102 @@ | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.repo.domain.node; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.util.HashSet; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; | ||||
| import org.alfresco.service.namespace.QName; | ||||
|  | ||||
| /** | ||||
|  * Class holding properties associated with the <b>sys:referenceable</b> aspect. | ||||
|  * This aspect is common enough to warrant direct inclusion on the <b>Node</b> entity. | ||||
|  *  | ||||
|  * @author Derek Hulley | ||||
|  * @since 3.4 | ||||
|  */ | ||||
| public class ReferenceablePropertiesEntity | ||||
| { | ||||
|     private static final Set<QName> REFERENCEABLE_PROP_QNAMES; | ||||
|     static | ||||
|     { | ||||
|         REFERENCEABLE_PROP_QNAMES = new HashSet<QName>(8); | ||||
|         REFERENCEABLE_PROP_QNAMES.add(ContentModel.PROP_STORE_PROTOCOL); | ||||
|         REFERENCEABLE_PROP_QNAMES.add(ContentModel.PROP_STORE_IDENTIFIER); | ||||
|         REFERENCEABLE_PROP_QNAMES.add(ContentModel.PROP_NODE_UUID); | ||||
|         REFERENCEABLE_PROP_QNAMES.add(ContentModel.PROP_NODE_DBID); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * @return          Returns <tt>true</tt> if the property belongs to the <b>sys:referenceable</b> aspect | ||||
|      */ | ||||
|     public static boolean isReferenceableProperty(QName qname) | ||||
|     { | ||||
|         return REFERENCEABLE_PROP_QNAMES.contains(qname); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Remove all {@link ContentModel#ASPECT_REFERENCEABLE referencable} properties | ||||
|      */ | ||||
|     public static void removeReferenceableProperties(Node node, Map<QName, Serializable> properties) | ||||
|     { | ||||
|         properties.keySet().removeAll(REFERENCEABLE_PROP_QNAMES); | ||||
|         String name = DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_NAME)); | ||||
|         if (name != null && name.equals(node.getUuid())) | ||||
|         { | ||||
|             // The cm:name matches the UUID, so drop it | ||||
|             properties.remove(ContentModel.PROP_NAME); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Remove all {@link ContentModel#ASPECT_REFERENCEABLE referencable} properties | ||||
|      */ | ||||
|     public static void removeReferenceableProperties(Set<QName> propertyQNames) | ||||
|     { | ||||
|         propertyQNames.removeAll(REFERENCEABLE_PROP_QNAMES); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Adds all {@link ContentModel#ASPECT_REFERENCEABLE referencable} properties. | ||||
|      */ | ||||
|     public static void addReferenceableProperties(Node node, Map<QName, Serializable> properties) | ||||
|     { | ||||
|         Long nodeId = node.getId(); | ||||
|         NodeRef nodeRef = node.getNodeRef(); | ||||
|         properties.put(ContentModel.PROP_STORE_PROTOCOL, nodeRef.getStoreRef().getProtocol()); | ||||
|         properties.put(ContentModel.PROP_STORE_IDENTIFIER, nodeRef.getStoreRef().getIdentifier()); | ||||
|         properties.put(ContentModel.PROP_NODE_UUID, nodeRef.getId()); | ||||
|         properties.put(ContentModel.PROP_NODE_DBID, nodeId); | ||||
|         // add the ID as the name, if required | ||||
|         String name = DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_NAME)); | ||||
|         if (name == null) | ||||
|         { | ||||
|             properties.put(ContentModel.PROP_NAME, nodeRef.getId()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static Serializable getReferenceableProperty(Node node, QName qname) | ||||
|     { | ||||
|         NodeRef nodeRef = node.getNodeRef(); | ||||
|         if (qname.equals(ContentModel.PROP_STORE_PROTOCOL)) | ||||
|         { | ||||
|             return nodeRef.getStoreRef().getProtocol(); | ||||
|         } | ||||
|         else if (qname.equals(ContentModel.PROP_STORE_IDENTIFIER)) | ||||
|         { | ||||
|             return nodeRef.getStoreRef().getIdentifier(); | ||||
|         } | ||||
|         else if (qname.equals(ContentModel.PROP_NODE_UUID)) | ||||
|         { | ||||
|             return nodeRef.getId(); | ||||
|         } | ||||
|         else if (qname.equals(ContentModel.PROP_NODE_DBID)) | ||||
|         { | ||||
|             return node.getId(); | ||||
|         } | ||||
|         throw new IllegalArgumentException("Not sys:referenceable property: " + qname); | ||||
|     } | ||||
| } | ||||
| package org.alfresco.repo.domain.node; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.util.HashSet; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; | ||||
| import org.alfresco.service.namespace.QName; | ||||
|  | ||||
| /** | ||||
|  * Class holding properties associated with the <b>sys:referenceable</b> aspect. This aspect is common enough to warrant direct inclusion on the <b>Node</b> entity. | ||||
|  *  | ||||
|  * @author Derek Hulley | ||||
|  * @since 3.4 | ||||
|  */ | ||||
| public class ReferenceablePropertiesEntity | ||||
| { | ||||
|     private static final Set<QName> REFERENCEABLE_PROP_QNAMES; | ||||
|     static | ||||
|     { | ||||
|         REFERENCEABLE_PROP_QNAMES = new HashSet<QName>(8); | ||||
|         REFERENCEABLE_PROP_QNAMES.add(ContentModel.PROP_STORE_PROTOCOL); | ||||
|         REFERENCEABLE_PROP_QNAMES.add(ContentModel.PROP_STORE_IDENTIFIER); | ||||
|         REFERENCEABLE_PROP_QNAMES.add(ContentModel.PROP_NODE_UUID); | ||||
|         REFERENCEABLE_PROP_QNAMES.add(ContentModel.PROP_NODE_DBID); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Returns <tt>true</tt> if the property belongs to the <b>sys:referenceable</b> aspect | ||||
|      */ | ||||
|     public static boolean isReferenceableProperty(QName qname) | ||||
|     { | ||||
|         return REFERENCEABLE_PROP_QNAMES.contains(qname); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove all {@link ContentModel#ASPECT_REFERENCEABLE referencable} properties | ||||
|      */ | ||||
|     public static void removeReferenceableProperties(Node node, Map<QName, Serializable> properties) | ||||
|     { | ||||
|         properties.keySet().removeAll(REFERENCEABLE_PROP_QNAMES); | ||||
|         String name = DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_NAME)); | ||||
|         if (name != null && name.equals(node.getUuid())) | ||||
|         { | ||||
|             // The cm:name matches the UUID, so drop it | ||||
|             properties.remove(ContentModel.PROP_NAME); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove all {@link ContentModel#ASPECT_REFERENCEABLE referencable} properties | ||||
|      */ | ||||
|     public static void removeReferenceableProperties(Set<QName> propertyQNames) | ||||
|     { | ||||
|         propertyQNames.removeAll(REFERENCEABLE_PROP_QNAMES); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adds all {@link ContentModel#ASPECT_REFERENCEABLE referencable} properties. | ||||
|      */ | ||||
|     public static void addReferenceableProperties(Long nodeId, NodeRef nodeRef, Map<QName, Serializable> properties) | ||||
|     { | ||||
|         properties.put(ContentModel.PROP_STORE_PROTOCOL, nodeRef.getStoreRef().getProtocol()); | ||||
|         properties.put(ContentModel.PROP_STORE_IDENTIFIER, nodeRef.getStoreRef().getIdentifier()); | ||||
|         properties.put(ContentModel.PROP_NODE_UUID, nodeRef.getId()); | ||||
|         properties.put(ContentModel.PROP_NODE_DBID, nodeId); | ||||
|         // add the ID as the name, if required | ||||
|         String name = DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_NAME)); | ||||
|         if (name == null) | ||||
|         { | ||||
|             properties.put(ContentModel.PROP_NAME, nodeRef.getId()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static Serializable getReferenceableProperty(Node node, QName qname) | ||||
|     { | ||||
|         NodeRef nodeRef = node.getNodeRef(); | ||||
|         if (qname.equals(ContentModel.PROP_STORE_PROTOCOL)) | ||||
|         { | ||||
|             return nodeRef.getStoreRef().getProtocol(); | ||||
|         } | ||||
|         else if (qname.equals(ContentModel.PROP_STORE_IDENTIFIER)) | ||||
|         { | ||||
|             return nodeRef.getStoreRef().getIdentifier(); | ||||
|         } | ||||
|         else if (qname.equals(ContentModel.PROP_NODE_UUID)) | ||||
|         { | ||||
|             return nodeRef.getId(); | ||||
|         } | ||||
|         else if (qname.equals(ContentModel.PROP_NODE_DBID)) | ||||
|         { | ||||
|             return node.getId(); | ||||
|         } | ||||
|         throw new IllegalArgumentException("Not sys:referenceable property: " + qname); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -116,7 +116,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl | ||||
|     private static final String SELECT_NODE_MAX_ID = "alfresco.node.select_NodeMaxId"; | ||||
|     private static final String SELECT_NODE_INTERVAL_BY_TYPE = "alfresco.node.select_MinMaxNodeIdForNodeType"; | ||||
|     private static final String SELECT_NODES_WITH_ASPECT_IDS = "alfresco.node.select_NodesWithAspectIds"; | ||||
|     private static final String SELECT_NODES_WITH_ASPECT_IDS_LIMITED = "alfresco.node.select_NodesWithAspectIds_Limited"; | ||||
|     private static final String SELECT_NODES_WITH_ASPECT_IDS_LIMITED = "alfresco.node.select.select_NodesWithAspectIds_Limited"; | ||||
|     private static final String INSERT_NODE_ASSOC = "alfresco.node.insert.insert_NodeAssoc"; | ||||
|     private static final String UPDATE_NODE_ASSOC = "alfresco.node.update_NodeAssoc"; | ||||
|     private static final String DELETE_NODE_ASSOC = "alfresco.node.delete_NodeAssoc"; | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2023 Alfresco Software Limited | ||||
|  * Copyright (C) 2005 - 2025 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
| @@ -39,8 +39,10 @@ import org.alfresco.service.namespace.QName; | ||||
| /** | ||||
|  * Encapsulates events occurred in a single transaction. | ||||
|  * | ||||
|  * @param <REF> entity (e.g. node, child association, peer association) reference type | ||||
|  * @param <RES> entity resource type | ||||
|  * @param <REF> | ||||
|  *            entity (e.g. node, child association, peer association) reference type | ||||
|  * @param <RES> | ||||
|  *            entity resource type | ||||
|  */ | ||||
| public abstract class EventConsolidator<REF extends EntityRef, RES extends Resource> | ||||
| { | ||||
| @@ -90,23 +92,31 @@ public abstract class EventConsolidator<REF extends EntityRef, RES extends Resou | ||||
|     /** | ||||
|      * Builds and returns the {@link RepoEvent} instance. | ||||
|      * | ||||
|      * @param eventInfo the object holding the event information | ||||
|      * @param eventInfo | ||||
|      *            the object holding the event information | ||||
|      * @return the {@link RepoEvent} instance | ||||
|      */ | ||||
|     public RepoEvent<DataAttributes<RES>> getRepoEvent(EventInfo eventInfo) | ||||
|     { | ||||
|         final RepoEvent.Builder<DataAttributes<RES>> builder = RepoEvent.builder(); | ||||
|  | ||||
|         configureRepoEventBuilder(builder, eventInfo); | ||||
|  | ||||
|         return builder.build(); | ||||
|     } | ||||
|  | ||||
|     protected void configureRepoEventBuilder(RepoEvent.Builder<DataAttributes<RES>> builder, EventInfo eventInfo) | ||||
|     { | ||||
|         EventType eventType = getDerivedEvent(); | ||||
|  | ||||
|         DataAttributes<RES> eventData = buildEventData(eventInfo, resource, eventType); | ||||
|  | ||||
|         return RepoEvent.<DataAttributes<RES>>builder() | ||||
|             .setId(eventInfo.getId()) | ||||
|             .setSource(eventInfo.getSource()) | ||||
|             .setTime(eventInfo.getTimestamp()) | ||||
|             .setType(eventType.getType()) | ||||
|             .setData(eventData) | ||||
|             .setDataschema(EventJSONSchema.getSchemaV1(eventType)) | ||||
|             .build(); | ||||
|         builder.setId(eventInfo.getId()) | ||||
|                 .setSource(eventInfo.getSource()) | ||||
|                 .setTime(eventInfo.getTimestamp()) | ||||
|                 .setType(eventType.getType()) | ||||
|                 .setData(eventData) | ||||
|                 .setDataschema(EventJSONSchema.getSchemaV1(eventType)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -114,9 +124,9 @@ public abstract class EventConsolidator<REF extends EntityRef, RES extends Resou | ||||
|      */ | ||||
|     protected DataAttributes<RES> buildEventData(EventInfo eventInfo, RES resource, EventType eventType) | ||||
|     { | ||||
|         return EventData.<RES>builder() | ||||
|             .setEventGroupId(eventInfo.getTxnId()) | ||||
|             .setResource(resource) | ||||
|             .build(); | ||||
|         return EventData.<RES> builder() | ||||
|                 .setEventGroupId(eventInfo.getTxnId()) | ||||
|                 .setResource(resource) | ||||
|                 .build(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited | ||||
|  * Copyright (C) 2005 - 2025 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software.  | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of  | ||||
| @@ -125,6 +125,9 @@ public class RhinoScriptProcessor extends BaseProcessor implements ScriptProcess | ||||
|     /** Number of (bytecode) instructions that will trigger the observer */ | ||||
|     private int observerInstructionCount = 100; | ||||
|  | ||||
|     /** Flag to enable or disable scope cleaning at the end of each script execution */ | ||||
|     private boolean cleanScope = true; | ||||
|  | ||||
|     /** Custom context factory */ | ||||
|     public static AlfrescoContextFactory contextFactory; | ||||
|  | ||||
| @@ -209,6 +212,15 @@ public class RhinoScriptProcessor extends BaseProcessor implements ScriptProcess | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param cleanScope | ||||
|      *            true to enable scope cleaning at the end of each script execution - set to false to disable this feature. | ||||
|      */ | ||||
|     public void setCleanScope(boolean cleanScope) | ||||
|     { | ||||
|         this.cleanScope = cleanScope; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @see org.alfresco.service.cmr.repository.ScriptProcessor#reset() | ||||
|      */ | ||||
|     public void reset() | ||||
| @@ -614,7 +626,7 @@ public class RhinoScriptProcessor extends BaseProcessor implements ScriptProcess | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             if (!secure) | ||||
|             if (!secure && cleanScope) | ||||
|             { | ||||
|                 unsetScope(model, scope); | ||||
|             } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -2,7 +2,7 @@ | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited | ||||
|  * Copyright (C) 2005 - 2025 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software.  | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of  | ||||
| @@ -23,239 +23,290 @@ | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.repo.node.getchildren; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| import org.alfresco.repo.domain.node.NodeEntity; | ||||
| import org.alfresco.repo.domain.node.NodePropertyEntity; | ||||
|  | ||||
| /** | ||||
|  * Filterable/Sortable Node Entity | ||||
|  * | ||||
|  * Can be optionally filtered/sorted by (up to) three properties - note: sort properties are applied in order | ||||
|  *  | ||||
|  * @author jan | ||||
|  * @since 4.0 | ||||
|  */ | ||||
| public class FilterSortNodeEntity | ||||
| { | ||||
|     private Long id; // node id | ||||
|      | ||||
|     private NodeEntity node; | ||||
|     private NodePropertyEntity prop1; | ||||
|     private NodePropertyEntity prop2; | ||||
|     private NodePropertyEntity prop3; | ||||
|      | ||||
|     // Supplemental query-related parameters | ||||
|     private Long parentNodeId; | ||||
|     private Long prop1qnameId; | ||||
|     private Long prop2qnameId; | ||||
|     private Long prop3qnameId; | ||||
|     private List<Long> childNodeTypeQNameIds; | ||||
|     private Set<Long> assocTypeQNameIds; | ||||
|     private String pattern; | ||||
|     private Long namePropertyQNameId; | ||||
|     private boolean auditableProps; | ||||
|     private boolean nodeType; | ||||
|  | ||||
|     private Boolean isPrimary; | ||||
|      | ||||
|     /** | ||||
|      * Default constructor | ||||
|      */ | ||||
|     public FilterSortNodeEntity() | ||||
|     { | ||||
|         auditableProps = false; | ||||
|     } | ||||
|      | ||||
|     public Long getId() | ||||
|     { | ||||
|         return id; | ||||
|     } | ||||
|      | ||||
|     public void setId(Long id) | ||||
|     { | ||||
|         this.id = id; | ||||
|     } | ||||
|      | ||||
|     public String getPattern() | ||||
|     { | ||||
|         return pattern; | ||||
|     } | ||||
|  | ||||
|     protected String escape(String s, char escapeChar) | ||||
|     { | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         int idx = -1; | ||||
|         int offset = 0; | ||||
|         do | ||||
|         { | ||||
|             idx = s.indexOf(escapeChar, offset); | ||||
|             if(idx != -1) | ||||
|             { | ||||
|                 sb.append(s.substring(offset, idx)); | ||||
|                 sb.append("\\"); | ||||
|                 sb.append(escapeChar); | ||||
|                 offset = idx + 1; | ||||
|             } | ||||
|         } | ||||
|         while(idx != -1); | ||||
|         sb.append(s.substring(offset)); | ||||
|         return sb.toString(); | ||||
|     } | ||||
|      | ||||
|     public void setPattern(String pattern) | ||||
|     { | ||||
|         if(pattern != null) | ||||
|         { | ||||
|             // escape the '%' character with '\' (standard SQL escape character) | ||||
|             pattern = escape(pattern, '%'); | ||||
|             // replace the wildcard character '*' with the one used in database queries i.e. '%' | ||||
|             this.pattern = pattern.replace('*', '%'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void setAssocTypeQNameIds(Set<Long> assocTypeQNameIds) | ||||
|     { | ||||
|         this.assocTypeQNameIds = assocTypeQNameIds; | ||||
|     } | ||||
|      | ||||
|     public Set<Long> getAssocTypeQNameIds() | ||||
|     { | ||||
|         return assocTypeQNameIds; | ||||
|     } | ||||
|  | ||||
|     public Long getNamePropertyQNameId() | ||||
|     { | ||||
|         return namePropertyQNameId; | ||||
|     } | ||||
|  | ||||
|     public void setNamePropertyQNameId(Long namePropertyQNameId) | ||||
|     { | ||||
|         this.namePropertyQNameId = namePropertyQNameId; | ||||
|     } | ||||
|  | ||||
|     public NodePropertyEntity getProp1() | ||||
|     { | ||||
|         return prop1; | ||||
|     } | ||||
|      | ||||
|     public void setProp1(NodePropertyEntity prop1) | ||||
|     { | ||||
|         this.prop1 = prop1; | ||||
|     } | ||||
|      | ||||
|     public NodePropertyEntity getProp2() | ||||
|     { | ||||
|         return prop2; | ||||
|     } | ||||
|      | ||||
|     public void setProp2(NodePropertyEntity prop2) | ||||
|     { | ||||
|         this.prop2 = prop2; | ||||
|     } | ||||
|      | ||||
|     public NodePropertyEntity getProp3() | ||||
|     { | ||||
|         return prop3; | ||||
|     } | ||||
|      | ||||
|     public void setProp3(NodePropertyEntity prop3) | ||||
|     { | ||||
|         this.prop3 = prop3; | ||||
|     } | ||||
|      | ||||
|     public NodeEntity getNode() | ||||
|     { | ||||
|         return node; | ||||
|     } | ||||
|      | ||||
|     public void setNode(NodeEntity childNode) | ||||
|     { | ||||
|         this.node = childNode; | ||||
|     } | ||||
|      | ||||
|     // Supplemental query-related parameters | ||||
|      | ||||
|     public Long getParentNodeId() | ||||
|     { | ||||
|         return parentNodeId; | ||||
|     } | ||||
|      | ||||
|     public void setParentNodeId(Long parentNodeId) | ||||
|     { | ||||
|         this.parentNodeId = parentNodeId; | ||||
|     } | ||||
|      | ||||
|     public Long getProp1qnameId() | ||||
|     { | ||||
|         return prop1qnameId; | ||||
|     } | ||||
|      | ||||
|     public void setProp1qnameId(Long prop1qnameId) | ||||
|     { | ||||
|         this.prop1qnameId = prop1qnameId; | ||||
|     } | ||||
|      | ||||
|     public Long getProp2qnameId() | ||||
|     { | ||||
|         return prop2qnameId; | ||||
|     } | ||||
|      | ||||
|     public void setProp2qnameId(Long prop2qnameId) | ||||
|     { | ||||
|         this.prop2qnameId = prop2qnameId; | ||||
|     } | ||||
|      | ||||
|     public Long getProp3qnameId() | ||||
|     { | ||||
|         return prop3qnameId; | ||||
|     } | ||||
|      | ||||
|     public void setProp3qnameId(Long prop3qnameId) | ||||
|     { | ||||
|         this.prop3qnameId = prop3qnameId; | ||||
|     } | ||||
|      | ||||
|     public List<Long> getChildNodeTypeQNameIds() | ||||
|     { | ||||
|         return childNodeTypeQNameIds; | ||||
|     } | ||||
|      | ||||
|     public void setChildNodeTypeQNameIds(List<Long> childNodeTypeQNameIds) | ||||
|     { | ||||
|         this.childNodeTypeQNameIds = childNodeTypeQNameIds; | ||||
|     } | ||||
|      | ||||
|     public boolean isAuditableProps() | ||||
|     { | ||||
|         return auditableProps; | ||||
|     } | ||||
|      | ||||
|     public void setAuditableProps(boolean auditableProps) | ||||
|     { | ||||
|         this.auditableProps = auditableProps; | ||||
|     } | ||||
|      | ||||
|     public boolean isNodeType() | ||||
|     { | ||||
|         return nodeType; | ||||
|     } | ||||
|      | ||||
|     public void setNodeType(boolean nodeType) | ||||
|     { | ||||
|         this.nodeType = nodeType; | ||||
|     } | ||||
|  | ||||
|     public Boolean isPrimary() | ||||
|     { | ||||
|         return isPrimary; | ||||
|     } | ||||
|  | ||||
|     public void setIsPrimary(Boolean isPrimary) | ||||
|     { | ||||
|         this.isPrimary = isPrimary; | ||||
|     } | ||||
| } | ||||
| package org.alfresco.repo.node.getchildren; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| import org.alfresco.repo.domain.node.AuditablePropertiesEntity; | ||||
| import org.alfresco.repo.domain.node.NodePropertyEntity; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.StoreRef; | ||||
|  | ||||
| /** | ||||
|  * Filterable/Sortable Node Entity | ||||
|  * | ||||
|  * Can be optionally filtered/sorted by (up to) three properties - note: sort properties are applied in order | ||||
|  *  | ||||
|  * @author jan | ||||
|  * @since 4.0 | ||||
|  */ | ||||
| public class FilterSortNodeEntity | ||||
| { | ||||
|     private Long id; // node id | ||||
|     private String nodeUuid; | ||||
|     private Long typeQNameId; | ||||
|  | ||||
|     private AuditablePropertiesEntity auditablePropertiesEntity; | ||||
|     private NodePropertyEntity prop1; | ||||
|     private NodePropertyEntity prop2; | ||||
|     private NodePropertyEntity prop3; | ||||
|  | ||||
|     private String storeProtocol; | ||||
|     private String storeIdentifier; | ||||
|  | ||||
|     // Supplemental query-related parameters | ||||
|     private Long parentNodeId; | ||||
|     private Long prop1qnameId; | ||||
|     private Long prop2qnameId; | ||||
|     private Long prop3qnameId; | ||||
|     private List<Long> childNodeTypeQNameIds; | ||||
|     private Set<Long> assocTypeQNameIds; | ||||
|     private String pattern; | ||||
|     private Long namePropertyQNameId; | ||||
|     private boolean auditableProps; | ||||
|     private boolean nodeType; | ||||
|  | ||||
|     private Boolean isPrimary; | ||||
|  | ||||
|     /** | ||||
|      * Default constructor | ||||
|      */ | ||||
|     public FilterSortNodeEntity() | ||||
|     { | ||||
|         auditableProps = false; | ||||
|     } | ||||
|  | ||||
|     public Long getId() | ||||
|     { | ||||
|         return id; | ||||
|     } | ||||
|  | ||||
|     public void setId(Long id) | ||||
|     { | ||||
|         this.id = id; | ||||
|     } | ||||
|  | ||||
|     public String getNodeUuid() | ||||
|     { | ||||
|         return nodeUuid; | ||||
|     } | ||||
|  | ||||
|     public void setNodeUuid(String nodeUuid) | ||||
|     { | ||||
|         this.nodeUuid = nodeUuid; | ||||
|     } | ||||
|  | ||||
|     public Long getTypeQNameId() | ||||
|     { | ||||
|         return typeQNameId; | ||||
|     } | ||||
|  | ||||
|     public void setTypeQNameId(Long typeQNameId) | ||||
|     { | ||||
|         this.typeQNameId = typeQNameId; | ||||
|     } | ||||
|  | ||||
|     public String getPattern() | ||||
|     { | ||||
|         return pattern; | ||||
|     } | ||||
|  | ||||
|     protected String escape(String s, char escapeChar) | ||||
|     { | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         int idx = -1; | ||||
|         int offset = 0; | ||||
|         do | ||||
|         { | ||||
|             idx = s.indexOf(escapeChar, offset); | ||||
|             if (idx != -1) | ||||
|             { | ||||
|                 sb.append(s.substring(offset, idx)); | ||||
|                 sb.append("\\"); | ||||
|                 sb.append(escapeChar); | ||||
|                 offset = idx + 1; | ||||
|             } | ||||
|         } while (idx != -1); | ||||
|         sb.append(s.substring(offset)); | ||||
|         return sb.toString(); | ||||
|     } | ||||
|  | ||||
|     public void setPattern(String pattern) | ||||
|     { | ||||
|         if (pattern != null) | ||||
|         { | ||||
|             // escape the '%' character with '\' (standard SQL escape character) | ||||
|             pattern = escape(pattern, '%'); | ||||
|             // replace the wildcard character '*' with the one used in database queries i.e. '%' | ||||
|             this.pattern = pattern.replace('*', '%'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void setAssocTypeQNameIds(Set<Long> assocTypeQNameIds) | ||||
|     { | ||||
|         this.assocTypeQNameIds = assocTypeQNameIds; | ||||
|     } | ||||
|  | ||||
|     public Set<Long> getAssocTypeQNameIds() | ||||
|     { | ||||
|         return assocTypeQNameIds; | ||||
|     } | ||||
|  | ||||
|     public Long getNamePropertyQNameId() | ||||
|     { | ||||
|         return namePropertyQNameId; | ||||
|     } | ||||
|  | ||||
|     public void setNamePropertyQNameId(Long namePropertyQNameId) | ||||
|     { | ||||
|         this.namePropertyQNameId = namePropertyQNameId; | ||||
|     } | ||||
|  | ||||
|     public AuditablePropertiesEntity getAuditablePropertiesEntity() | ||||
|     { | ||||
|         return auditablePropertiesEntity; | ||||
|     } | ||||
|  | ||||
|     public void setAuditablePropertiesEntity(AuditablePropertiesEntity auditablePropertiesEntity) | ||||
|     { | ||||
|         this.auditablePropertiesEntity = auditablePropertiesEntity; | ||||
|     } | ||||
|  | ||||
|     public NodePropertyEntity getProp1() | ||||
|     { | ||||
|         return prop1; | ||||
|     } | ||||
|  | ||||
|     public void setProp1(NodePropertyEntity prop1) | ||||
|     { | ||||
|         this.prop1 = prop1; | ||||
|     } | ||||
|  | ||||
|     public NodePropertyEntity getProp2() | ||||
|     { | ||||
|         return prop2; | ||||
|     } | ||||
|  | ||||
|     public void setProp2(NodePropertyEntity prop2) | ||||
|     { | ||||
|         this.prop2 = prop2; | ||||
|     } | ||||
|  | ||||
|     public NodePropertyEntity getProp3() | ||||
|     { | ||||
|         return prop3; | ||||
|     } | ||||
|  | ||||
|     public void setProp3(NodePropertyEntity prop3) | ||||
|     { | ||||
|         this.prop3 = prop3; | ||||
|     } | ||||
|  | ||||
|     public String getStoreProtocol() | ||||
|     { | ||||
|         return storeProtocol; | ||||
|     } | ||||
|  | ||||
|     public void setStoreProtocol(String storeProtocol) | ||||
|     { | ||||
|         this.storeProtocol = storeProtocol; | ||||
|     } | ||||
|  | ||||
|     public String getStoreIdentifier() | ||||
|     { | ||||
|         return storeIdentifier; | ||||
|     } | ||||
|  | ||||
|     public void setStoreIdentifier(String storeIdentifier) | ||||
|     { | ||||
|         this.storeIdentifier = storeIdentifier; | ||||
|     } | ||||
|  | ||||
|     // Supplemental query-related parameters | ||||
|  | ||||
|     public Long getParentNodeId() | ||||
|     { | ||||
|         return parentNodeId; | ||||
|     } | ||||
|  | ||||
|     public void setParentNodeId(Long parentNodeId) | ||||
|     { | ||||
|         this.parentNodeId = parentNodeId; | ||||
|     } | ||||
|  | ||||
|     public Long getProp1qnameId() | ||||
|     { | ||||
|         return prop1qnameId; | ||||
|     } | ||||
|  | ||||
|     public void setProp1qnameId(Long prop1qnameId) | ||||
|     { | ||||
|         this.prop1qnameId = prop1qnameId; | ||||
|     } | ||||
|  | ||||
|     public Long getProp2qnameId() | ||||
|     { | ||||
|         return prop2qnameId; | ||||
|     } | ||||
|  | ||||
|     public void setProp2qnameId(Long prop2qnameId) | ||||
|     { | ||||
|         this.prop2qnameId = prop2qnameId; | ||||
|     } | ||||
|  | ||||
|     public Long getProp3qnameId() | ||||
|     { | ||||
|         return prop3qnameId; | ||||
|     } | ||||
|  | ||||
|     public void setProp3qnameId(Long prop3qnameId) | ||||
|     { | ||||
|         this.prop3qnameId = prop3qnameId; | ||||
|     } | ||||
|  | ||||
|     public List<Long> getChildNodeTypeQNameIds() | ||||
|     { | ||||
|         return childNodeTypeQNameIds; | ||||
|     } | ||||
|  | ||||
|     public void setChildNodeTypeQNameIds(List<Long> childNodeTypeQNameIds) | ||||
|     { | ||||
|         this.childNodeTypeQNameIds = childNodeTypeQNameIds; | ||||
|     } | ||||
|  | ||||
|     public boolean isAuditableProps() | ||||
|     { | ||||
|         return auditableProps; | ||||
|     } | ||||
|  | ||||
|     public void setAuditableProps(boolean auditableProps) | ||||
|     { | ||||
|         this.auditableProps = auditableProps; | ||||
|     } | ||||
|  | ||||
|     public boolean isNodeType() | ||||
|     { | ||||
|         return nodeType; | ||||
|     } | ||||
|  | ||||
|     public void setNodeType(boolean nodeType) | ||||
|     { | ||||
|         this.nodeType = nodeType; | ||||
|     } | ||||
|  | ||||
|     public Boolean isPrimary() | ||||
|     { | ||||
|         return isPrimary; | ||||
|     } | ||||
|  | ||||
|     public void setIsPrimary(Boolean isPrimary) | ||||
|     { | ||||
|         this.isPrimary = isPrimary; | ||||
|     } | ||||
|  | ||||
|     public NodeRef createNodeRef() | ||||
|     { | ||||
|         return new NodeRef(new StoreRef(storeProtocol, storeIdentifier), nodeUuid); | ||||
|     } | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -81,11 +81,19 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea | ||||
|  | ||||
|     public static final QName DEFAULT_RENDITION_CONTENT_PROP = ContentModel.PROP_CONTENT; | ||||
|     public static final String DEFAULT_MIMETYPE = MimetypeMap.MIMETYPE_TEXT_PLAIN; | ||||
|     public static final String MIMETYPE_METADATA_EXTRACT = "alfresco-metadata-extract"; | ||||
|     public static final String MIMETYPE_METADATA_EMBED = "alfresco-metadata-embed"; | ||||
|     public static final String DEFAULT_ENCODING = "UTF-8"; | ||||
|  | ||||
|     public static final int SOURCE_HAS_NO_CONTENT = -1; | ||||
|     public static final int RENDITION2_DOES_NOT_EXIST = -2; | ||||
|  | ||||
|     // Allowed mimetypes to support text or metadata extract transforms when thumbnails are disabled. | ||||
|     private static final Set<String> ALLOWED_MIMETYPES = Set.of( | ||||
|             MimetypeMap.MIMETYPE_TEXT_PLAIN, | ||||
|             MIMETYPE_METADATA_EXTRACT, | ||||
|             MIMETYPE_METADATA_EMBED); | ||||
|  | ||||
|     private static Log logger = LogFactory.getLog(RenditionService2Impl.class); | ||||
|  | ||||
|     // As Async transforms and renditions are so similar, this class provides a way to provide the code that is different. | ||||
| @@ -288,7 +296,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (!isEnabled()) | ||||
|             if (!isAsyncAllowed(renderOrTransform)) | ||||
|             { | ||||
|                 throw new RenditionService2Exception("Async transforms and renditions are disabled " + | ||||
|                         "(system.thumbnail.generate=false or renditionService2.enabled=false)."); | ||||
| @@ -967,4 +975,23 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Checks if the given transform callback is a text extract transform for content indexing or metadata extract/embed. | ||||
|     private boolean isTextOrMetadataExtractTransform(RenderOrTransformCallBack renderOrTransform) | ||||
|     { | ||||
|         RenditionDefinition2 renditionDefinition = renderOrTransform.getRenditionDefinition(); | ||||
|         return renditionDefinition != null && ALLOWED_MIMETYPES.contains(renditionDefinition.getTargetMimetype()); | ||||
|     } | ||||
|  | ||||
|     private boolean isAsyncAllowed(RenderOrTransformCallBack renderOrTransform) | ||||
|     { | ||||
|         // If enabled is false, all async transforms/renditions must be blocked | ||||
|         if (!enabled) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // If thumbnails are disabled, allow only text extract or metadata extract/embed transforms | ||||
|         return thumbnailsEnabled || isTextOrMetadataExtractTransform(renderOrTransform); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -74,7 +74,7 @@ import com.nimbusds.oauth2.sdk.id.Identifier; | ||||
| import com.nimbusds.oauth2.sdk.id.Issuer; | ||||
| import com.nimbusds.openid.connect.sdk.claims.PersonClaims; | ||||
| import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata; | ||||
| import org.apache.commons.lang.StringUtils; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.apache.commons.logging.Log; | ||||
| import org.apache.commons.logging.LogFactory; | ||||
| import org.apache.hc.client5.http.classic.HttpClient; | ||||
|   | ||||
| @@ -42,7 +42,7 @@ import jakarta.servlet.http.HttpServletResponse; | ||||
| import com.nimbusds.oauth2.sdk.Scope; | ||||
| import com.nimbusds.oauth2.sdk.id.Identifier; | ||||
| import com.nimbusds.oauth2.sdk.id.State; | ||||
| import org.apache.commons.lang.StringUtils; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.security.oauth2.client.registration.ClientRegistration; | ||||
|   | ||||
| @@ -133,7 +133,15 @@ | ||||
|     <resultMap id="result_FilterSortNode" type="FilterSortNode"> | ||||
|          | ||||
|         <id property="id" column="id" jdbcType="BIGINT" javaType="java.lang.Long"/> | ||||
|          | ||||
|         <result property="nodeUuid" column="uuid" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|         <result property="typeQNameId" column="type_qname_id" jdbcType="BIGINT" javaType="java.lang.Long"/> | ||||
|  | ||||
|         <result property="auditablePropertiesEntity.auditCreator" column="audit_creator" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|         <result property="auditablePropertiesEntity.auditCreated" column="audit_created" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|         <result property="auditablePropertiesEntity.auditModifier" column="audit_modifier" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|         <result property="auditablePropertiesEntity.auditModified" column="audit_modified" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|         <result property="auditablePropertiesEntity.auditAccessed" column="audit_accessed" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|  | ||||
|         <result property="prop1.nodeId" column="prop1_node_id" jdbcType="BIGINT" javaType="java.lang.Long"/> | ||||
|         <result property="prop1.key.qnameId" column="prop1_qname_id" jdbcType="BIGINT" javaType="java.lang.Long"/> | ||||
|         <result property="prop1.key.localeId" column="prop1_locale_id" jdbcType="BIGINT" javaType="java.lang.Long"/> | ||||
| @@ -169,8 +177,9 @@ | ||||
|         <result property="prop3.value.floatValue" column="prop3_float_value" jdbcType="FLOAT" javaType="java.lang.Float"/> | ||||
|         <result property="prop3.value.doubleValue" column="prop3_double_value" jdbcType="FLOAT" javaType="java.lang.Double"/> | ||||
|         <result property="prop3.value.stringValue" column="prop3_string_value" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|          | ||||
|         <association property="node" resultMap="alfresco.node.result_Node"/> | ||||
|  | ||||
|         <result property="storeProtocol" column="protocol" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|         <result property="storeIdentifier" column="identifier" jdbcType="VARCHAR" javaType="java.lang.String"/> | ||||
|          | ||||
|     </resultMap> | ||||
|      | ||||
| @@ -782,25 +791,6 @@ | ||||
|         <if test="ordered == true">order by node.id ASC</if> | ||||
|     </select> | ||||
|  | ||||
|     <select id="select_NodesWithAspectIds_Limited" parameterType="Ids" resultMap="result_NodeRef" > | ||||
|         select | ||||
|             node.id             as id, | ||||
|             store.protocol      as protocol, | ||||
|             store.identifier    as identifier, | ||||
|             node.uuid           as uuid | ||||
|         from | ||||
|             alf_node_aspects na | ||||
|             join alf_node node on (na.node_id = node.id) | ||||
|             left join alf_store store on (store.id = node.store_id) | ||||
|         where | ||||
|             <![CDATA[na.node_id >= #{idOne}]]> | ||||
|             <if test="idTwo != null"><![CDATA[and na.node_id < #{idTwo}]]></if> | ||||
|             and na.qname_id in | ||||
|                 <foreach item="item" index="i" collection="ids" open="(" separator="," close=")">#{item}</foreach> | ||||
|         <if test="ordered == true">order by node.id ASC</if> | ||||
|         <if test="maxResults != null"><![CDATA[limit #{maxResults}]]></if> | ||||
|     </select> | ||||
|  | ||||
|     <!-- Common results for result_NodeAssoc --> | ||||
|     <sql id="select_NodeAssoc_Results"> | ||||
|         select | ||||
| @@ -991,8 +981,8 @@ | ||||
|     </select> | ||||
|  | ||||
|     <!-- GetChildren - with explicit prop filtering and/or sorting --> | ||||
|     <select id="select_GetChildrenCannedQueryWithProps" parameterType="FilterSortNode" resultMap="result_FilterSortNode"> | ||||
|        select | ||||
|     <select id="select_GetChildrenCannedQueryWithProps" parameterType="FilterSortNode" resultMap="result_FilterSortNode" flushCache="true"> | ||||
|        select distinct | ||||
|             childNode.id             as id, | ||||
|             childNode.version        as version, | ||||
|             childStore.id            as store_id, | ||||
| @@ -1008,7 +998,7 @@ | ||||
|             childNode.audit_created  as audit_created, | ||||
|             childNode.audit_modifier as audit_modifier, | ||||
|             childNode.audit_modified as audit_modified, | ||||
|             childNode.audit_accessed  as audit_accessed | ||||
|             childNode.audit_accessed as audit_accessed | ||||
|             <if test="prop1qnameId != null"> | ||||
|           , prop1.node_id            as prop1_node_id, | ||||
|             prop1.qname_id           as prop1_qname_id, | ||||
| @@ -1086,9 +1076,6 @@ | ||||
|                     #{item} | ||||
|                 </foreach> | ||||
|             </if> | ||||
|         <if test="prop1qnameId == null and auditableProps == false"> | ||||
|             <include refid="alfresco.node.select_ChildAssoc_OrderBy"/> | ||||
|         </if> | ||||
|     </select> | ||||
|      | ||||
|     <!-- GetChildren - with no explicit sorting (or prop filtering) - note: still filtered by child type (and optionally primary or secondary) --> | ||||
| @@ -1566,4 +1553,4 @@ | ||||
|         </foreach> | ||||
|     </delete> | ||||
|          | ||||
| </mapper> | ||||
| </mapper> | ||||
|   | ||||
| @@ -30,4 +30,23 @@ | ||||
|       <![CDATA[and commit_time_ms <= #{maxCommitTime}]]> | ||||
|     </select> | ||||
|  | ||||
| </mapper> | ||||
|     <select id="select_NodesWithAspectIds_Limited" parameterType="Ids" resultMap="alfresco.node.result_NodeRef" > | ||||
|         select | ||||
|             node.id             as id, | ||||
|             store.protocol      as protocol, | ||||
|             store.identifier    as identifier, | ||||
|             node.uuid           as uuid | ||||
|         from | ||||
|             alf_node_aspects na | ||||
|             join alf_node node on (na.node_id = node.id) | ||||
|             left join alf_store store on (store.id = node.store_id) | ||||
|         where | ||||
|             <![CDATA[na.node_id >= #{idOne}]]> | ||||
|             <if test="idTwo != null"><![CDATA[and na.node_id < #{idTwo}]]></if> | ||||
|             and na.qname_id in | ||||
|                 <foreach item="item" index="i" collection="ids" open="(" separator="," close=")">#{item}</foreach> | ||||
|         <if test="ordered == true">order by node.id ASC</if> | ||||
|         <if test="maxResults != null"><![CDATA[limit #{maxResults}]]></if> | ||||
|     </select> | ||||
|  | ||||
| </mapper> | ||||
|   | ||||
| @@ -30,4 +30,23 @@ | ||||
|       <![CDATA[and commit_time_ms <= #{maxCommitTime}]]> | ||||
|     </select> | ||||
|  | ||||
| </mapper> | ||||
|     <select id="select_NodesWithAspectIds_Limited" parameterType="Ids" resultMap="alfresco.node.result_NodeRef" > | ||||
|         select | ||||
|             node.id             as id, | ||||
|             store.protocol      as protocol, | ||||
|             store.identifier    as identifier, | ||||
|             node.uuid           as uuid | ||||
|         from | ||||
|             alf_node_aspects na | ||||
|             join alf_node node on (na.node_id = node.id) | ||||
|             left join alf_store store on (store.id = node.store_id) | ||||
|         where | ||||
|             <![CDATA[na.node_id >= #{idOne}]]> | ||||
|             <if test="idTwo != null"><![CDATA[and na.node_id < #{idTwo}]]></if> | ||||
|             and na.qname_id in | ||||
|                 <foreach item="item" index="i" collection="ids" open="(" separator="," close=")">#{item}</foreach> | ||||
|         <if test="ordered == true">order by node.id ASC</if> | ||||
|         <if test="maxResults != null"><![CDATA[limit #{maxResults}]]></if> | ||||
|     </select> | ||||
|  | ||||
| </mapper> | ||||
|   | ||||
| @@ -1394,6 +1394,9 @@ scripts.execution.maxMemoryUsedInBytes=-1 | ||||
| # Number of instructions that will trigger the observer | ||||
| scripts.execution.observerInstructionCount=5000 | ||||
|  | ||||
| # Flag to control if the scope is cleaned at the end of script execution | ||||
| scripts.execution.clean.scope=true | ||||
|  | ||||
| # Default value being used in POST/size-details endpoint to partition a huge folder into smaller chunks | ||||
| # so that we can compute more efficiently and consolidate all sizes into a single unit. | ||||
| default.async.folder.items=1000 | ||||
|   | ||||
| @@ -60,6 +60,9 @@ | ||||
|         <property name="observerInstructionCount"> | ||||
|             <value>${scripts.execution.observerInstructionCount}</value> | ||||
|         </property> | ||||
|         <property name="cleanScope"> | ||||
|             <value>${scripts.execution.clean.scope}</value> | ||||
|         </property> | ||||
|     </bean> | ||||
|  | ||||
|     <!-- base config implementation that script extension beans extend from - for auto registration | ||||
|   | ||||
| @@ -26,6 +26,12 @@ | ||||
|  | ||||
| package org.alfresco.repo.event2; | ||||
|  | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.Assert.assertFalse; | ||||
| import static org.junit.Assert.assertNotNull; | ||||
| import static org.junit.Assert.assertNull; | ||||
| import static org.junit.Assert.assertTrue; | ||||
|  | ||||
| import static org.alfresco.model.ContentModel.PROP_DESCRIPTION; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| @@ -35,6 +41,9 @@ import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
|  | ||||
| import org.junit.Test; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.repo.content.MimetypeMap; | ||||
| import org.alfresco.repo.dictionary.M2Model; | ||||
| @@ -53,7 +62,6 @@ import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.namespace.QName; | ||||
| import org.alfresco.util.GUID; | ||||
| import org.alfresco.util.Pair; | ||||
| import org.junit.Test; | ||||
|  | ||||
| /** | ||||
|  * @author Iulian Aftene | ||||
| @@ -66,20 +74,20 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|     public void testUpdateNodeResourceContent() | ||||
|     { | ||||
|         ContentService contentService = (ContentService) applicationContext.getBean( | ||||
|             "contentService"); | ||||
|                 "contentService"); | ||||
|  | ||||
|         final NodeRef nodeRef = createNode(ContentModel.TYPE_CONTENT); | ||||
|  | ||||
|         RepoEvent<EventData<NodeResource>> resultRepoEvent = getRepoEvent(1); | ||||
|         assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), | ||||
|             resultRepoEvent.getType()); | ||||
|                 resultRepoEvent.getType()); | ||||
|  | ||||
|         NodeResource resource = getNodeResource(resultRepoEvent); | ||||
|         assertNull("Content should have been null.", resource.getContent()); | ||||
|  | ||||
|         retryingTransactionHelper.doInTransaction(() -> { | ||||
|             ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.TYPE_CONTENT, | ||||
|                 true); | ||||
|                     true); | ||||
|             writer.setMimetype(MimetypeMap.MIMETYPE_PDF); | ||||
|             writer.setEncoding("UTF-8"); | ||||
|             writer.putContent("test content."); | ||||
| @@ -90,7 +98,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|  | ||||
|         resultRepoEvent = getRepoEvent(2); | ||||
|         assertEquals("Wrong repo event type.", EventType.NODE_UPDATED.getType(), | ||||
|             resultRepoEvent.getType()); | ||||
|                 resultRepoEvent.getType()); | ||||
|  | ||||
|         resource = getNodeResource(resultRepoEvent); | ||||
|         ContentInfo content = resource.getContent(); | ||||
| @@ -105,7 +113,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         // Update the content again | ||||
|         retryingTransactionHelper.doInTransaction(() -> { | ||||
|             ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.TYPE_CONTENT, | ||||
|                 true); | ||||
|                     true); | ||||
|             writer.setMimetype(MimetypeMap.MIMETYPE_PDF); | ||||
|             writer.setEncoding("UTF-8"); | ||||
|             writer.putContent("A quick brown fox jumps over the lazy dog."); | ||||
| @@ -370,7 +378,6 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         assertEquals("new test title", title); | ||||
|         assertEquals("new test title", getLocalizedProperty(resource, "cm:title", defaultLocale)); | ||||
|  | ||||
|  | ||||
|         resourceBefore = getNodeResourceBefore(3); | ||||
|         title = getProperty(resourceBefore, "cm:title"); | ||||
|         assertEquals("Wrong old property.", "test title", title); | ||||
| @@ -490,14 +497,14 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         resource = getNodeResource(2); | ||||
|         assertNotNull(resource.getAspectNames()); | ||||
|         assertTrue(resource.getAspectNames().contains("cm:versionable")); | ||||
|         //Check all aspects | ||||
|         // Check all aspects | ||||
|         Set<String> expectedAspects = new HashSet<>(originalAspects); | ||||
|         expectedAspects.add("cm:versionable"); | ||||
|         assertEquals(expectedAspects, resource.getAspectNames()); | ||||
|         // Check properties | ||||
|         assertFalse(resource.getProperties().isEmpty()); | ||||
|  | ||||
|         //Check resourceBefore | ||||
|         // Check resourceBefore | ||||
|         NodeResource resourceBefore = getNodeResourceBefore(2); | ||||
|         assertNotNull(resourceBefore.getAspectNames()); | ||||
|         assertEquals(originalAspects, resourceBefore.getAspectNames()); | ||||
| @@ -544,21 +551,64 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         assertEquals(aspectsBeforeRemove, resourceBefore.getAspectNames()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRemoveAspectPropertiesTest() | ||||
|     { | ||||
|         final NodeRef nodeRef = createNode(ContentModel.TYPE_CONTENT); | ||||
|         NodeResource resource = getNodeResource(1); | ||||
|         final Set<String> originalAspects = resource.getAspectNames(); | ||||
|         assertNotNull(originalAspects); | ||||
|  | ||||
|         // Add cm:geographic aspect with properties | ||||
|         retryingTransactionHelper.doInTransaction(() -> { | ||||
|             Map<QName, Serializable> aspectProperties = new HashMap<>(); | ||||
|             aspectProperties.put(ContentModel.PROP_LATITUDE, "12.345678"); | ||||
|             aspectProperties.put(ContentModel.PROP_LONGITUDE, "12.345678"); | ||||
|             nodeService.addAspect(nodeRef, ContentModel.ASPECT_GEOGRAPHIC, aspectProperties); | ||||
|             return null; | ||||
|         }); | ||||
|         resource = getNodeResource(2); | ||||
|         Set<String> aspectsBeforeRemove = resource.getAspectNames(); | ||||
|         assertNotNull(aspectsBeforeRemove); | ||||
|         assertTrue(aspectsBeforeRemove.contains("cm:geographic")); | ||||
|  | ||||
|         // Remove cm:geographic aspect - this automatically removes the properties from the node | ||||
|         retryingTransactionHelper.doInTransaction(() -> { | ||||
|             nodeService.removeAspect(nodeRef, ContentModel.ASPECT_GEOGRAPHIC); | ||||
|             return null; | ||||
|         }); | ||||
|  | ||||
|         resource = getNodeResource(3); | ||||
|         assertEquals(originalAspects, resource.getAspectNames()); | ||||
|  | ||||
|         NodeResource resourceBefore = getNodeResourceBefore(3); | ||||
|         assertNotNull(resourceBefore.getAspectNames()); | ||||
|         assertEquals(aspectsBeforeRemove, resourceBefore.getAspectNames()); | ||||
|         // Resource before should contain cm:latitude and cm:longitude properties | ||||
|         assertNotNull(resourceBefore.getProperties()); | ||||
|         assertTrue(resourceBefore.getProperties().containsKey("cm:latitude")); | ||||
|         assertTrue(resourceBefore.getProperties().containsKey("cm:longitude")); | ||||
|         // Resource after should NOT contain cm:latitude and cm:longitude properties | ||||
|         assertNotNull(resource.getProperties()); | ||||
|         assertFalse(resource.getProperties().containsKey("cm:latitude")); | ||||
|         assertFalse(resource.getProperties().containsKey("cm:longitude")); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateAndUpdateInTheSameTransaction() | ||||
|     { | ||||
|         retryingTransactionHelper.doInTransaction(() -> { | ||||
|  | ||||
|             NodeRef node1 = nodeService.createNode( | ||||
|                 rootNodeRef, | ||||
|                 ContentModel.ASSOC_CHILDREN, | ||||
|                 QName.createQName(TEST_NAMESPACE, GUID.generate()), | ||||
|                 ContentModel.TYPE_CONTENT).getChildRef(); | ||||
|                     rootNodeRef, | ||||
|                     ContentModel.ASSOC_CHILDREN, | ||||
|                     QName.createQName(TEST_NAMESPACE, GUID.generate()), | ||||
|                     ContentModel.TYPE_CONTENT).getChildRef(); | ||||
|  | ||||
|             nodeService.setProperty(node1, PROP_DESCRIPTION, "test description"); | ||||
|             return null; | ||||
|         }); | ||||
|         //Create and update node are done in the same transaction so one event is expected | ||||
|         // Create and update node are done in the same transaction so one event is expected | ||||
|         // to be generated | ||||
|         checkNumOfEvents(1); | ||||
|     } | ||||
| @@ -593,8 +643,8 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         assertEquals("Incorrect node type was found", "cm:folder", nodeResource.getNodeType()); | ||||
|  | ||||
|         NodeResource resourceBefore = getNodeResourceBefore(2); | ||||
|         assertEquals("Incorrect node type was found","cm:content", resourceBefore.getNodeType()); | ||||
|         // assertNotNull(resourceBefore.getModifiedAt());  uncomment this when the issue will be fixed | ||||
|         assertEquals("Incorrect node type was found", "cm:content", resourceBefore.getNodeType()); | ||||
|         // assertNotNull(resourceBefore.getModifiedAt()); uncomment this when the issue will be fixed | ||||
|         assertNull(resourceBefore.getId()); | ||||
|         assertNull(resourceBefore.getContent()); | ||||
|         assertNull(resourceBefore.isFile()); | ||||
| @@ -624,8 +674,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         m2Type.setTitle("Test type title"); | ||||
|  | ||||
|         // Create active model | ||||
|         CustomModelDefinition modelDefinition = | ||||
|             retryingTransactionHelper.doInTransaction(() -> customModelService.createCustomModel(model, true)); | ||||
|         CustomModelDefinition modelDefinition = retryingTransactionHelper.doInTransaction(() -> customModelService.createCustomModel(model, true)); | ||||
|  | ||||
|         assertNotNull(modelDefinition); | ||||
|         assertEquals(modelName, modelDefinition.getName().getLocalName()); | ||||
| @@ -655,7 +704,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); | ||||
|         assertEquals("cm:content node type was not found", "cm:content", nodeResource.getNodeType()); | ||||
|  | ||||
|         QName typeQName = QName.createQName("{" + namespacePair.getFirst()+ "}" + typeName); | ||||
|         QName typeQName = QName.createQName("{" + namespacePair.getFirst() + "}" + typeName); | ||||
|         retryingTransactionHelper.doInTransaction(() -> { | ||||
|             nodeService.setType(nodeRef, typeQName); | ||||
|  | ||||
| @@ -757,7 +806,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         // we should have only 1 event, node.Created | ||||
|         checkNumOfEvents(1); | ||||
|  | ||||
|         RepoEvent<EventData<NodeResource>>  resultRepoEvent = getRepoEvent(1); | ||||
|         RepoEvent<EventData<NodeResource>> resultRepoEvent = getRepoEvent(1); | ||||
|         assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); | ||||
|         NodeResource nodeResource = getNodeResource(resultRepoEvent); | ||||
|         assertEquals("Incorrect node type was found", "cm:folder", nodeResource.getNodeType()); | ||||
| @@ -783,10 +832,10 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|  | ||||
|         retryingTransactionHelper.doInTransaction(() -> { | ||||
|             nodeService.moveNode( | ||||
|                 moveFile, | ||||
|                 folder2, | ||||
|                 ContentModel.ASSOC_CONTAINS, | ||||
|                 QName.createQName(TEST_NAMESPACE)); | ||||
|                     moveFile, | ||||
|                     folder2, | ||||
|                     ContentModel.ASSOC_CONTAINS, | ||||
|                     QName.createQName(TEST_NAMESPACE)); | ||||
|             return null; | ||||
|         }); | ||||
|  | ||||
| @@ -801,7 +850,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         assertEquals("Wrong node parent.", folder1ID, moveFileParentBeforeMove); | ||||
|         assertEquals("Wrong node parent.", folder2ID, moveFileParentAfterMove); | ||||
|         assertEquals("Wrong repo event type.", EventType.NODE_UPDATED.getType(), | ||||
|             getRepoEvent(4).getType()); | ||||
|                 getRepoEvent(4).getType()); | ||||
|  | ||||
|         assertNull(resourceBefore.getId()); | ||||
|         assertNull(resourceBefore.getName()); | ||||
| @@ -833,10 +882,10 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|  | ||||
|         retryingTransactionHelper.doInTransaction(() -> { | ||||
|             nodeService.moveNode( | ||||
|                 moveFolder, | ||||
|                 grandParent, | ||||
|                 ContentModel.ASSOC_CONTAINS, | ||||
|                 QName.createQName(TEST_NAMESPACE)); | ||||
|                     moveFolder, | ||||
|                     grandParent, | ||||
|                     ContentModel.ASSOC_CONTAINS, | ||||
|                     QName.createQName(TEST_NAMESPACE)); | ||||
|             return null; | ||||
|         }); | ||||
|  | ||||
| @@ -845,15 +894,13 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         final String grandParentID = getNodeResource(1).getId(); | ||||
|         final String parentID = getNodeResource(2).getId(); | ||||
|  | ||||
|         final String moveFolderParentBeforeMove = | ||||
|             getNodeResourceBefore(4).getPrimaryHierarchy().get(0); | ||||
|         final String moveFolderParentAfterMove = | ||||
|             getNodeResource(4).getPrimaryHierarchy().get(0); | ||||
|         final String moveFolderParentBeforeMove = getNodeResourceBefore(4).getPrimaryHierarchy().get(0); | ||||
|         final String moveFolderParentAfterMove = getNodeResource(4).getPrimaryHierarchy().get(0); | ||||
|  | ||||
|         assertEquals("Wrong node parent.", parentID, moveFolderParentBeforeMove); | ||||
|         assertEquals("Wrong node parent.", grandParentID, moveFolderParentAfterMove); | ||||
|         assertEquals("Wrong repo event type.", EventType.NODE_UPDATED.getType(), | ||||
|             getRepoEventWithoutWait(4).getType()); | ||||
|                 getRepoEventWithoutWait(4).getType()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
| @@ -867,28 +914,25 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|  | ||||
|         retryingTransactionHelper.doInTransaction(() -> { | ||||
|             nodeService.moveNode( | ||||
|                 grandParent, | ||||
|                 root2, | ||||
|                 ContentModel.ASSOC_CONTAINS, | ||||
|                 QName.createQName(TEST_NAMESPACE)); | ||||
|                     grandParent, | ||||
|                     root2, | ||||
|                     ContentModel.ASSOC_CONTAINS, | ||||
|                     QName.createQName(TEST_NAMESPACE)); | ||||
|             return null; | ||||
|         }); | ||||
|  | ||||
|         checkNumOfEvents(6); | ||||
|  | ||||
|         final String root2ID = getNodeResource(2).getId(); | ||||
|         final String grandParentParentAfterMove = | ||||
|             getNodeResource(6).getPrimaryHierarchy().get(0); | ||||
|         final String grandParentParentAfterMove = getNodeResource(6).getPrimaryHierarchy().get(0); | ||||
|         assertEquals("Wrong node parent.", root2ID, grandParentParentAfterMove); | ||||
|  | ||||
|         final String grandParentID = getNodeResource(3).getId(); | ||||
|         final String parentIDOfTheParentFolder = | ||||
|             getNodeResource(4).getPrimaryHierarchy().get(0); | ||||
|         final String parentIDOfTheParentFolder = getNodeResource(4).getPrimaryHierarchy().get(0); | ||||
|         assertEquals("Wrong node parent.", grandParentID, parentIDOfTheParentFolder); | ||||
|  | ||||
|         final String parentID = getNodeResource(4).getId(); | ||||
|         final String contentParentID = | ||||
|             getNodeResource(5).getPrimaryHierarchy().get(0); | ||||
|         final String contentParentID = getNodeResource(5).getPrimaryHierarchy().get(0); | ||||
|         assertEquals("Wrong node parent.", parentID, contentParentID); | ||||
|     } | ||||
|  | ||||
| @@ -906,10 +950,10 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|  | ||||
|         retryingTransactionHelper.doInTransaction(() -> { | ||||
|             nodeService.moveNode( | ||||
|                 moveFile, | ||||
|                 folder2, | ||||
|                 ContentModel.ASSOC_CONTAINS, | ||||
|                 QName.createQName(TEST_NAMESPACE)); | ||||
|                     moveFile, | ||||
|                     folder2, | ||||
|                     ContentModel.ASSOC_CONTAINS, | ||||
|                     QName.createQName(TEST_NAMESPACE)); | ||||
|             return null; | ||||
|         }); | ||||
|  | ||||
| @@ -918,8 +962,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         assertTrue("Wrong aspect.", resource.getAspectNames().contains("cm:versionable")); | ||||
|  | ||||
|         final String folder2ID = getNodeResource(2).getId(); | ||||
|         final String moveFileParentAfterMove = | ||||
|             getNodeResource(5).getPrimaryHierarchy().get(0); | ||||
|         final String moveFileParentAfterMove = getNodeResource(5).getPrimaryHierarchy().get(0); | ||||
|  | ||||
|         assertEquals("Wrong node parent.", folder2ID, moveFileParentAfterMove); | ||||
|     } | ||||
| @@ -935,10 +978,10 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|             nodeService.setProperty(moveFile, ContentModel.PROP_NAME, "test_new_name"); | ||||
|  | ||||
|             nodeService.moveNode( | ||||
|                 moveFile, | ||||
|                 folder2, | ||||
|                 ContentModel.ASSOC_CONTAINS, | ||||
|                 QName.createQName(TEST_NAMESPACE)); | ||||
|                     moveFile, | ||||
|                     folder2, | ||||
|                     ContentModel.ASSOC_CONTAINS, | ||||
|                     QName.createQName(TEST_NAMESPACE)); | ||||
|             return null; | ||||
|         }); | ||||
|  | ||||
| @@ -946,8 +989,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         assertEquals("test_new_name", resource.getName()); | ||||
|  | ||||
|         final String folder2ID = getNodeResource(2).getId(); | ||||
|         final String moveFileParentAfterMove = | ||||
|             getNodeResource(4).getPrimaryHierarchy().get(0); | ||||
|         final String moveFileParentAfterMove = getNodeResource(4).getPrimaryHierarchy().get(0); | ||||
|  | ||||
|         assertEquals("Wrong node parent.", folder2ID, moveFileParentAfterMove); | ||||
|     } | ||||
| @@ -958,28 +1000,28 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         retryingTransactionHelper.doInTransaction(() -> { | ||||
|  | ||||
|             NodeRef folder1 = nodeService.createNode( | ||||
|                 rootNodeRef, | ||||
|                 ContentModel.ASSOC_CHILDREN, | ||||
|                 QName.createQName(TEST_NAMESPACE), | ||||
|                 ContentModel.TYPE_FOLDER).getChildRef(); | ||||
|                     rootNodeRef, | ||||
|                     ContentModel.ASSOC_CHILDREN, | ||||
|                     QName.createQName(TEST_NAMESPACE), | ||||
|                     ContentModel.TYPE_FOLDER).getChildRef(); | ||||
|  | ||||
|             NodeRef folder2 = nodeService.createNode( | ||||
|                 rootNodeRef, | ||||
|                 ContentModel.ASSOC_CHILDREN, | ||||
|                 QName.createQName(TEST_NAMESPACE), | ||||
|                 ContentModel.TYPE_FOLDER).getChildRef(); | ||||
|                     rootNodeRef, | ||||
|                     ContentModel.ASSOC_CHILDREN, | ||||
|                     QName.createQName(TEST_NAMESPACE), | ||||
|                     ContentModel.TYPE_FOLDER).getChildRef(); | ||||
|  | ||||
|             NodeRef fileToMove = nodeService.createNode( | ||||
|                 folder1, | ||||
|                 ContentModel.ASSOC_CONTAINS, | ||||
|                 QName.createQName(TEST_NAMESPACE), | ||||
|                 ContentModel.TYPE_CONTENT).getChildRef(); | ||||
|                     folder1, | ||||
|                     ContentModel.ASSOC_CONTAINS, | ||||
|                     QName.createQName(TEST_NAMESPACE), | ||||
|                     ContentModel.TYPE_CONTENT).getChildRef(); | ||||
|  | ||||
|             nodeService.moveNode( | ||||
|                 fileToMove, | ||||
|                 folder2, | ||||
|                 ContentModel.ASSOC_CONTAINS, | ||||
|                 QName.createQName(TEST_NAMESPACE)); | ||||
|                     fileToMove, | ||||
|                     folder2, | ||||
|                     ContentModel.ASSOC_CONTAINS, | ||||
|                     QName.createQName(TEST_NAMESPACE)); | ||||
|  | ||||
|             assertEquals(folder2, nodeService.getPrimaryParent(fileToMove).getParentRef()); | ||||
|  | ||||
| @@ -989,8 +1031,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         checkNumOfEvents(3); | ||||
|  | ||||
|         final String folder2ID = getNodeResource(2).getId(); | ||||
|         final String moveFileParentAfterMove = | ||||
|             getNodeResource(3).getPrimaryHierarchy().get(0); | ||||
|         final String moveFileParentAfterMove = getNodeResource(3).getPrimaryHierarchy().get(0); | ||||
|  | ||||
|         assertEquals("Wrong node parent.", folder2ID, moveFileParentAfterMove); | ||||
|     } | ||||
| @@ -1003,7 +1044,6 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent | ||||
|         final Set<String> originalAspects = resource.getAspectNames(); | ||||
|         assertNotNull(originalAspects); | ||||
|  | ||||
|  | ||||
|         retryingTransactionHelper.doInTransaction(() -> { | ||||
|             // Add cm:geographic aspect with default value | ||||
|             nodeService.addAspect(nodeRef, ContentModel.ASPECT_GEOGRAPHIC, null); | ||||
|   | ||||
| @@ -39,6 +39,7 @@ import org.junit.Test; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.model.RenditionModel; | ||||
| import org.alfresco.repo.content.MimetypeMap; | ||||
| import org.alfresco.repo.security.authentication.AuthenticationUtil; | ||||
| import org.alfresco.repo.security.permissions.AccessDeniedException; | ||||
| import org.alfresco.service.cmr.repository.ChildAssociationRef; | ||||
| @@ -776,4 +777,57 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testTextExtractTransformAllowedWhenThumbnailDisabled() | ||||
|     { | ||||
|         // create a source node | ||||
|         NodeRef sourceNodeRef = createSource(ADMIN, "quick.pdf"); | ||||
|         assertNotNull("Node not generated", sourceNodeRef); | ||||
|         String replyQueue = "org.test.queue"; | ||||
|         String targetMimetype = MimetypeMap.MIMETYPE_TEXT_PLAIN; | ||||
|  | ||||
|         TransformDefinition textExtractTransform = new TransformDefinition( | ||||
|                 targetMimetype, | ||||
|                 java.util.Collections.emptyMap(), | ||||
|                 "clientData", | ||||
|                 replyQueue, | ||||
|                 "requestId"); | ||||
|  | ||||
|         renditionService2.setThumbnailsEnabled(false); | ||||
|         try | ||||
|         { | ||||
|             // Should NOT throw, as this is a text extract transform | ||||
|             AuthenticationUtil.runAs(() -> { | ||||
|                 transactionService.getRetryingTransactionHelper().doInTransaction(() -> { | ||||
|                     renditionService2.transform(sourceNodeRef, textExtractTransform); | ||||
|                     return null; | ||||
|                 }); | ||||
|                 return null; | ||||
|             }, ADMIN); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             renditionService2.setThumbnailsEnabled(true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testMetadataExtractTransformAllowedWhenThumbnailDisabled() | ||||
|     { | ||||
|         // create a source node | ||||
|         NodeRef sourceNodeRef = createSource(ADMIN, "quick.pdf"); | ||||
|         assertNotNull("Node not generated", sourceNodeRef); | ||||
|         renditionService2.setThumbnailsEnabled(false); | ||||
|         try | ||||
|         { | ||||
|             // Should NOT throw, as this is a metadata extract transform | ||||
|             extract(ADMIN, sourceNodeRef); | ||||
|             waitForExtract(ADMIN, sourceNodeRef, true); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             renditionService2.setThumbnailsEnabled(true); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user