Compare commits

..

21 Commits

Author SHA1 Message Date
alfresco-build
cbbb1f16e0 [maven-release-plugin][skip ci] prepare release 23.3.15.1 2025-09-15 12:26:28 +00:00
Eva Vasques
cf419ebee3 [skip tests] Prepare next version 23.3.15 (#3573) 2025-09-15 13:22:13 +01:00
alfresco-build
c1a06fd793 [maven-release-plugin][skip ci] prepare for next development iteration 2025-09-11 17:33:31 +00:00
alfresco-build
545ba61609 [maven-release-plugin][skip ci] prepare release 23.3.14.2 2025-09-11 17:33:30 +00:00
Eva Vasques
aedd04beb3 ACS-10042 - AGS Concurrent IPR Group Creation (#3566) (#3569) 2025-09-11 17:46:15 +01:00
alfresco-build
c6f271fc49 [maven-release-plugin][skip ci] prepare for next development iteration 2025-09-11 12:43:04 +00:00
alfresco-build
71b901d16f [maven-release-plugin][skip ci] prepare release 23.3.14.1 2025-09-11 12:43:02 +00:00
Eva Vasques
1819400ac0 [skip tests] Prepare version 23.3.14 (#3567) 2025-09-11 13:39:16 +01:00
alfresco-build
22d6f68c48 [maven-release-plugin][skip ci] prepare for next development iteration 2025-08-25 07:15:13 +00:00
alfresco-build
e18edd2c07 [maven-release-plugin][skip ci] prepare release 23.3.13.2 2025-08-25 07:15:10 +00:00
SatyamSah5
7229105b86 ACS-9991 Allow Content and Metadata extract even if thumbnails are di… (#3542) 2025-08-25 11:55:40 +05:30
alfresco-build
996809e3c2 [maven-release-plugin][skip ci] prepare for next development iteration 2025-08-12 13:12:37 +00:00
alfresco-build
bc76bedc4d [maven-release-plugin][skip ci] prepare release 23.3.13.1 2025-08-12 13:12:35 +00:00
Eva Vasques
179366c974 [skip tests] Prepare next version 23.3.13 (#3517) 2025-08-12 14:09:37 +01:00
alfresco-build
981b98ed88 [maven-release-plugin][skip ci] prepare for next development iteration 2025-08-07 20:45:10 +00:00
alfresco-build
b1469f4410 [maven-release-plugin][skip ci] prepare release 23.3.12.3 2025-08-07 20:45:08 +00:00
Eva Vasques
2e92dab995 MNT-24975 - Repeated IPR groups due to casing inconsistencies on creation (#3509) 2025-08-07 21:03:03 +01:00
alfresco-build
ffcd8973e3 [maven-release-plugin][skip ci] prepare for next development iteration 2025-08-07 13:35:05 +00:00
alfresco-build
b3dcb3ea35 [maven-release-plugin][skip ci] prepare release 23.3.12.2 2025-08-07 13:35:02 +00:00
Eva Vasques
7a49b4c331 ACS-9923 Removing an aspect needs to invoke onUpdateProperties (#3504) (#3505) 2025-08-07 13:50:57 +01:00
alfresco-build
dc0b1988ca [maven-release-plugin][skip ci] prepare for next development iteration 2025-07-30 12:48:19 +00:00
30 changed files with 3927 additions and 3537 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -36,6 +36,7 @@ import java.util.Set;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.extensions.webscripts.ui.common.StringUtils;
import org.alfresco.model.ContentModel;
@@ -47,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;
@@ -59,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.
@@ -73,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";
@@ -103,7 +101,8 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
private boolean enableUsernameNormalization;
/**
* @param filePlanService file plan service
* @param filePlanService
* file plan service
*/
public void setFilePlanService(FilePlanService filePlanService)
{
@@ -111,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)
{
@@ -119,7 +119,8 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
}
/**
* @param authorityService authority service
* @param authorityService
* authority service
*/
public void setAuthorityService(AuthorityService authorityService)
{
@@ -127,7 +128,8 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
}
/**
* @param permissionService permission service
* @param permissionService
* permission service
*/
public void setPermissionService(PermissionService permissionService)
{
@@ -135,7 +137,8 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
}
/**
* @param transactionService transaction service
* @param transactionService
* transaction service
*/
public void setTransactionService(TransactionService transactionService)
{
@@ -158,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;
}
@@ -190,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
@@ -240,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)
{
@@ -300,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)
{
@@ -337,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)
{
@@ -359,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
{
@@ -385,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)
{
@@ -400,13 +411,8 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
boolean hasMoreItems = true;
int pageCount = 0;
// 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);
// determine the short name prefix
String groupShortNamePrefix = getIPRGroupPrefixShortName(groupPrefix, authoritySet);
String groupShortNamePrefix = getIPRGroupPrefixShortName(groupPrefix, authorities);
// iterate over the authorities to find a match
while (hasMoreItems == true)
@@ -414,11 +420,11 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
// get matching authorities
PagingResults<String> results = authorityService.getAuthorities(
AuthorityType.GROUP,
RMAuthority.ZONE_APP_RM,
groupShortNamePrefix,
false,
false,
new PagingRequest(MAX_ITEMS*pageCount, MAX_ITEMS));
RMAuthority.ZONE_APP_RM,
groupShortNamePrefix,
false,
false,
new PagingRequest(MAX_ITEMS * pageCount, MAX_ITEMS));
// record the total count
nextGroupIndex = nextGroupIndex + results.getPage().size();
@@ -427,7 +433,7 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
for (String group : results.getPage())
{
// if exists and matches we have found our group
if (isIPRGroupTrueMatch(group, authoritySet))
if (isIPRGroupTrueMatch(group, authorities))
{
return new Pair<String, Integer>(group, nextGroupIndex);
}
@@ -435,7 +441,7 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
// determine if there are any more pages to inspect
hasMoreItems = results.hasMoreItems();
pageCount ++;
pageCount++;
}
return new Pair<>(iprGroup, nextGroupIndex);
@@ -501,20 +507,22 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
/**
* 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);
}
@@ -523,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();
}
@@ -543,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));
}
@@ -559,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();
}
@@ -577,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)
{
@@ -593,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)
{
@@ -626,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;
@@ -638,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)
{
@@ -677,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)
{
@@ -689,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);
}
@@ -697,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);
}
@@ -705,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);
}
@@ -713,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);
}
@@ -721,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);
}
@@ -729,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);
}
@@ -737,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);
}
@@ -745,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);
}

View File

@@ -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);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>alfresco-community-repo</artifactId>
<version>23.3.12.1</version>
<version>23.3.15.1</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>3</acs.version.minor>
<acs.version.revision>12</acs.version.revision>
<acs.version.revision>15</acs.version.revision>
<acs.version.label />
<amp.min.version>${acs.version.major}.0.0</amp.min.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.3.12.1</tag>
<tag>23.3.15.1</tag>
</scm>
<distributionManagement>

View File

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

View File

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

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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;
@@ -717,4 +718,57 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
renditionService2.setEnabled(true);
}
}
@Test
public void testTextExtractTransformAllowedWhenThumbnailDisabled()
{
// create a source node
NodeRef sourceNodeRef = createSource(ADMIN, "quick.pdf");
assertNotNull("Node not generated", sourceNodeRef);
String replyQueue = "org.test.queue";
String targetMimetype = MimetypeMap.MIMETYPE_TEXT_PLAIN;
TransformDefinition textExtractTransform = new TransformDefinition(
targetMimetype,
java.util.Collections.emptyMap(),
"clientData",
replyQueue,
"requestId");
renditionService2.setThumbnailsEnabled(false);
try
{
// Should NOT throw, as this is a text extract transform
AuthenticationUtil.runAs(() -> {
transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
renditionService2.transform(sourceNodeRef, textExtractTransform);
return null;
});
return null;
}, ADMIN);
}
finally
{
renditionService2.setThumbnailsEnabled(true);
}
}
@Test
public void testMetadataExtractTransformAllowedWhenThumbnailDisabled()
{
// create a source node
NodeRef sourceNodeRef = createSource(ADMIN, "quick.pdf");
assertNotNull("Node not generated", sourceNodeRef);
renditionService2.setThumbnailsEnabled(false);
try
{
// Should NOT throw, as this is a metadata extract transform
extract(ADMIN, sourceNodeRef);
waitForExtract(ADMIN, sourceNodeRef, true);
}
finally
{
renditionService2.setThumbnailsEnabled(true);
}
}
}