mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2026-09-16 18:13:17 +00:00
[ACS-12679] Implement mechanism to cleanup ACLs (#4384)
This commit is contained in:
+70
@@ -35,6 +35,7 @@ import org.springframework.dao.ConcurrencyFailureException;
|
|||||||
import org.springframework.extensions.surf.util.ParameterCheck;
|
import org.springframework.extensions.surf.util.ParameterCheck;
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.cache.SimpleCache;
|
import org.alfresco.repo.cache.SimpleCache;
|
||||||
import org.alfresco.repo.cache.TransactionalCache;
|
import org.alfresco.repo.cache.TransactionalCache;
|
||||||
import org.alfresco.repo.cache.lookup.EntityLookupCache;
|
import org.alfresco.repo.cache.lookup.EntityLookupCache;
|
||||||
@@ -42,6 +43,7 @@ import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAO;
|
|||||||
import org.alfresco.repo.domain.CrcHelper;
|
import org.alfresco.repo.domain.CrcHelper;
|
||||||
import org.alfresco.repo.domain.qname.QNameDAO;
|
import org.alfresco.repo.domain.qname.QNameDAO;
|
||||||
import org.alfresco.repo.security.permissions.ACEType;
|
import org.alfresco.repo.security.permissions.ACEType;
|
||||||
|
import org.alfresco.repo.security.permissions.ACLType;
|
||||||
import org.alfresco.repo.security.permissions.PermissionReference;
|
import org.alfresco.repo.security.permissions.PermissionReference;
|
||||||
import org.alfresco.repo.security.permissions.impl.SimplePermissionReference;
|
import org.alfresco.repo.security.permissions.impl.SimplePermissionReference;
|
||||||
import org.alfresco.service.cmr.security.AccessStatus;
|
import org.alfresco.service.cmr.security.AccessStatus;
|
||||||
@@ -252,6 +254,64 @@ public abstract class AbstractAclCrudDAOImpl implements AclCrudDAO
|
|||||||
return getLatestAclEntityByGuid(aclGuid);
|
return getLatestAclEntityByGuid(aclGuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Long> getUnusedAclIds(long afterAclId, int maxResults)
|
||||||
|
{
|
||||||
|
if (maxResults < 1)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("maxResults must be greater than zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
Long sharedAclToReplaceQNameId = getQNameId(ContentModel.PROP_SHARED_ACL_TO_REPLACE);
|
||||||
|
Long inheritFromAclQNameId = getQNameId(ContentModel.PROP_INHERIT_FROM_ACL);
|
||||||
|
return getUnusedAclEntityIds(afterAclId, sharedAclToReplaceQNameId, inheritFromAclQNameId,
|
||||||
|
ACLType.FIXED.getId(), ACLType.GLOBAL.getId(), maxResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteUnusedAcl(long aclEntityId)
|
||||||
|
{
|
||||||
|
Long sharedAclToReplaceQNameId = getQNameId(ContentModel.PROP_SHARED_ACL_TO_REPLACE);
|
||||||
|
Long inheritFromAclQNameId = getQNameId(ContentModel.PROP_INHERIT_FROM_ACL);
|
||||||
|
if (!isAclEntityUnused(aclEntityId, sharedAclToReplaceQNameId, inheritFromAclQNameId,
|
||||||
|
ACLType.FIXED.getId(), ACLType.GLOBAL.getId()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Acl acl = getAcl(aclEntityId);
|
||||||
|
if (acl == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AclMember> members = getAclMembersByAcl(aclEntityId);
|
||||||
|
List<Long> aceIds = new ArrayList<>(members.size());
|
||||||
|
for (AclMember member : members)
|
||||||
|
{
|
||||||
|
aceIds.add(member.getAceId());
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAclMembersByAcl(aclEntityId);
|
||||||
|
deleteAcl(aclEntityId);
|
||||||
|
|
||||||
|
for (Long aceId : aceIds)
|
||||||
|
{
|
||||||
|
deleteAceEntityIfUnused(aceId);
|
||||||
|
}
|
||||||
|
if (acl.getAclChangeSetId() != null)
|
||||||
|
{
|
||||||
|
deleteAclChangeSetEntityIfUnused(acl.getAclChangeSetId());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long getQNameId(QName qname)
|
||||||
|
{
|
||||||
|
Pair<Long, QName> qnamePair = qnameDAO.getQName(qname);
|
||||||
|
return qnamePair == null ? null : qnamePair.getFirst();
|
||||||
|
}
|
||||||
|
|
||||||
public List<Long> getADMNodesByAcl(long aclEntityId, int maxResults)
|
public List<Long> getADMNodesByAcl(long aclEntityId, int maxResults)
|
||||||
{
|
{
|
||||||
return getADMNodeEntityIdsByAcl(aclEntityId, maxResults);
|
return getADMNodeEntityIdsByAcl(aclEntityId, maxResults);
|
||||||
@@ -373,12 +433,20 @@ public abstract class AbstractAclCrudDAOImpl implements AclCrudDAO
|
|||||||
|
|
||||||
protected abstract Long getLatestAclEntityByGuid(String aclGuid);
|
protected abstract Long getLatestAclEntityByGuid(String aclGuid);
|
||||||
|
|
||||||
|
protected abstract List<Long> getUnusedAclEntityIds(long afterAclId, Long sharedAclToReplaceQNameId, Long inheritFromAclQNameId,
|
||||||
|
int fixedAclType, int globalAclType, int maxResults);
|
||||||
|
|
||||||
|
protected abstract boolean isAclEntityUnused(long aclEntityId, Long sharedAclToReplaceQNameId, Long inheritFromAclQNameId,
|
||||||
|
int fixedAclType, int globalAclType);
|
||||||
|
|
||||||
protected abstract int updateAclEntity(AclEntity entity);
|
protected abstract int updateAclEntity(AclEntity entity);
|
||||||
|
|
||||||
protected abstract int updateAceEntity(AceEntity updatedAceEntity);
|
protected abstract int updateAceEntity(AceEntity updatedAceEntity);
|
||||||
|
|
||||||
protected abstract int deleteAclEntity(long id);
|
protected abstract int deleteAclEntity(long id);
|
||||||
|
|
||||||
|
protected abstract int deleteAclChangeSetEntityIfUnused(long aclChangeSetEntityId);
|
||||||
|
|
||||||
protected abstract List<Long> getADMNodeEntityIdsByAcl(long aclEntityId, int maxResults);
|
protected abstract List<Long> getADMNodeEntityIdsByAcl(long aclEntityId, int maxResults);
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -681,6 +749,8 @@ public abstract class AbstractAclCrudDAOImpl implements AclCrudDAO
|
|||||||
|
|
||||||
protected abstract int deleteAceEntities(List<Long> aceIds);
|
protected abstract int deleteAceEntities(List<Long> aceIds);
|
||||||
|
|
||||||
|
protected abstract int deleteAceEntityIfUnused(long aceId);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Permission
|
// Permission
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -63,6 +63,10 @@ public interface AclCrudDAO
|
|||||||
|
|
||||||
public Long getLatestAclByGuid(String aclGuid);
|
public Long getLatestAclByGuid(String aclGuid);
|
||||||
|
|
||||||
|
List<Long> getUnusedAclIds(long afterAclId, int maxResults);
|
||||||
|
|
||||||
|
boolean deleteUnusedAcl(long aclEntityId);
|
||||||
|
|
||||||
public void updateAcl(AclUpdateEntity entity);
|
public void updateAcl(AclUpdateEntity entity);
|
||||||
|
|
||||||
public void deleteAcl(long aclEntityId);
|
public void deleteAcl(long aclEntityId);
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.domain.permissions;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
|
import org.alfresco.util.PropertyCheck;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a bounded batch of ACLs that have no repository references.
|
||||||
|
*/
|
||||||
|
public class UnusedAclCleaner
|
||||||
|
{
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(UnusedAclCleaner.class);
|
||||||
|
|
||||||
|
private AclCrudDAO aclCrudDAO;
|
||||||
|
private TransactionService transactionService;
|
||||||
|
private int batchSize = 1000;
|
||||||
|
private boolean enabled = true;
|
||||||
|
|
||||||
|
public void setAclCrudDAO(AclCrudDAO aclCrudDAO)
|
||||||
|
{
|
||||||
|
this.aclCrudDAO = aclCrudDAO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTransactionService(TransactionService transactionService)
|
||||||
|
{
|
||||||
|
this.transactionService = transactionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBatchSize(int batchSize)
|
||||||
|
{
|
||||||
|
if (batchSize < 1)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("batchSize must be greater than zero");
|
||||||
|
}
|
||||||
|
this.batchSize = batchSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled)
|
||||||
|
{
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init()
|
||||||
|
{
|
||||||
|
PropertyCheck.mandatory(this, "aclCrudDAO", aclCrudDAO);
|
||||||
|
PropertyCheck.mandatory(this, "transactionService", transactionService);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int execute()
|
||||||
|
{
|
||||||
|
if (!enabled)
|
||||||
|
{
|
||||||
|
LOGGER.debug("Unused ACL cleanup is disabled.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int deleted = transactionService.getRetryingTransactionHelper()
|
||||||
|
.doInTransaction(this::cleanupBatch, false, true);
|
||||||
|
LOGGER.info("Unused ACL cleanup removed {} ACLs.", deleted);
|
||||||
|
return deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cleanupBatch()
|
||||||
|
{
|
||||||
|
List<Long> aclIds = aclCrudDAO.getUnusedAclIds(0, batchSize);
|
||||||
|
int deleted = 0;
|
||||||
|
for (Long aclId : aclIds)
|
||||||
|
{
|
||||||
|
if (aclCrudDAO.deleteUnusedAcl(aclId))
|
||||||
|
{
|
||||||
|
deleted++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deleted;
|
||||||
|
}
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.domain.permissions;
|
||||||
|
|
||||||
|
import org.quartz.JobDataMap;
|
||||||
|
import org.quartz.JobExecutionContext;
|
||||||
|
import org.quartz.JobExecutionException;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
import org.alfresco.schedule.AbstractScheduledLockedJob;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cluster-safe scheduled entry point for unused ACL cleanup.
|
||||||
|
*/
|
||||||
|
public class UnusedAclCleanupJob extends AbstractScheduledLockedJob
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void executeJob(JobExecutionContext context) throws JobExecutionException
|
||||||
|
{
|
||||||
|
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
|
||||||
|
Object cleaner = jobDataMap.get("unusedAclCleaner");
|
||||||
|
if (!(cleaner instanceof UnusedAclCleaner))
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("UnusedAclCleanupJob must contain a valid 'unusedAclCleaner'");
|
||||||
|
}
|
||||||
|
((UnusedAclCleaner) cleaner).execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
+47
@@ -59,6 +59,8 @@ public class AclCrudDAOImpl extends AbstractAclCrudDAOImpl
|
|||||||
private static final String SELECT_ACL_BY_ID = "alfresco.permissions.select_AclById";
|
private static final String SELECT_ACL_BY_ID = "alfresco.permissions.select_AclById";
|
||||||
private static final String SELECT_ACLS_THAT_INHERIT_FROM_ACL = "alfresco.permissions.select_AclsThatInheritFromAcl";
|
private static final String SELECT_ACLS_THAT_INHERIT_FROM_ACL = "alfresco.permissions.select_AclsThatInheritFromAcl";
|
||||||
private static final String SELECT_LATEST_ACL_BY_GUID = "alfresco.permissions.select_LatestAclByGuid";
|
private static final String SELECT_LATEST_ACL_BY_GUID = "alfresco.permissions.select_LatestAclByGuid";
|
||||||
|
private static final String SELECT_UNUSED_ACL_IDS = "alfresco.permissions.select_UnusedAclIds";
|
||||||
|
private static final String SELECT_IS_ACL_UNUSED = "alfresco.permissions.select_IsAclUnused";
|
||||||
private static final String SELECT_ADM_NODES_BY_ACL = "alfresco.permissions.select_ADMNodesByAclId";
|
private static final String SELECT_ADM_NODES_BY_ACL = "alfresco.permissions.select_ADMNodesByAclId";
|
||||||
private static final String UPDATE_ACL = "alfresco.permissions.update_Acl";
|
private static final String UPDATE_ACL = "alfresco.permissions.update_Acl";
|
||||||
private static final String DELETE_ACL = "alfresco.permissions.delete_Acl";
|
private static final String DELETE_ACL = "alfresco.permissions.delete_Acl";
|
||||||
@@ -75,6 +77,7 @@ public class AclCrudDAOImpl extends AbstractAclCrudDAOImpl
|
|||||||
private static final String UPDATE_ACL_CHANGESET = "alfresco.permissions.update_AclChangeSet";
|
private static final String UPDATE_ACL_CHANGESET = "alfresco.permissions.update_AclChangeSet";
|
||||||
private static final String SELECT_ACL_CHANGESET_BY_ID = "alfresco.permissions.select_AclChangeSetById";
|
private static final String SELECT_ACL_CHANGESET_BY_ID = "alfresco.permissions.select_AclChangeSetById";
|
||||||
private static final String DELETE_ACL_CHANGESET = "alfresco.permissions.delete_AclChangeSet";
|
private static final String DELETE_ACL_CHANGESET = "alfresco.permissions.delete_AclChangeSet";
|
||||||
|
private static final String DELETE_UNUSED_ACL_CHANGESET = "alfresco.permissions.delete_UnusedAclChangeSet";
|
||||||
|
|
||||||
private static final String INSERT_ACE = "alfresco.permissions.insert.insert_Ace";
|
private static final String INSERT_ACE = "alfresco.permissions.insert.insert_Ace";
|
||||||
private static final String SELECT_ACE_BY_ID = "alfresco.permissions.select_AceById";
|
private static final String SELECT_ACE_BY_ID = "alfresco.permissions.select_AceById";
|
||||||
@@ -82,6 +85,7 @@ public class AclCrudDAOImpl extends AbstractAclCrudDAOImpl
|
|||||||
private static final String SELECT_ACES_AND_AUTHORIES_BY_ACL = "alfresco.permissions.select_AcesAndAuthoritiesByAclId";
|
private static final String SELECT_ACES_AND_AUTHORIES_BY_ACL = "alfresco.permissions.select_AcesAndAuthoritiesByAclId";
|
||||||
private static final String SELECT_ACE_WITH_NO_CONTEXT = "alfresco.permissions.select_AceWithNoContext";
|
private static final String SELECT_ACE_WITH_NO_CONTEXT = "alfresco.permissions.select_AceWithNoContext";
|
||||||
private static final String DELETE_ACES_LIST = "alfresco.permissions.delete_AcesList";
|
private static final String DELETE_ACES_LIST = "alfresco.permissions.delete_AcesList";
|
||||||
|
private static final String DELETE_UNUSED_ACE = "alfresco.permissions.delete_UnusedAce";
|
||||||
private static final String UPDATE_ACE = "alfresco.permissions.update_Ace";
|
private static final String UPDATE_ACE = "alfresco.permissions.update_Ace";
|
||||||
|
|
||||||
private static final String INSERT_ACE_CONTEXT = "alfresco.permissions.insert.insert_AceContext";
|
private static final String INSERT_ACE_CONTEXT = "alfresco.permissions.insert.insert_AceContext";
|
||||||
@@ -151,6 +155,33 @@ public class AclCrudDAOImpl extends AbstractAclCrudDAOImpl
|
|||||||
return template.selectOne(SELECT_LATEST_ACL_BY_GUID, params);
|
return template.selectOne(SELECT_LATEST_ACL_BY_GUID, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<Long> getUnusedAclEntityIds(long afterAclId, Long sharedAclToReplaceQNameId, Long inheritFromAclQNameId,
|
||||||
|
int fixedAclType, int globalAclType, int maxResults)
|
||||||
|
{
|
||||||
|
Map<String, Object> params = new HashMap<>(5);
|
||||||
|
params.put("afterAclId", afterAclId);
|
||||||
|
params.put("sharedAclToReplaceQNameId", sharedAclToReplaceQNameId);
|
||||||
|
params.put("inheritFromAclQNameId", inheritFromAclQNameId);
|
||||||
|
params.put("fixedAclType", fixedAclType);
|
||||||
|
params.put("globalAclType", globalAclType);
|
||||||
|
|
||||||
|
return template.selectList(SELECT_UNUSED_ACL_IDS, params, new RowBounds(0, maxResults));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isAclEntityUnused(long aclEntityId, Long sharedAclToReplaceQNameId, Long inheritFromAclQNameId,
|
||||||
|
int fixedAclType, int globalAclType)
|
||||||
|
{
|
||||||
|
Map<String, Object> params = new HashMap<>(5);
|
||||||
|
params.put("id", aclEntityId);
|
||||||
|
params.put("sharedAclToReplaceQNameId", sharedAclToReplaceQNameId);
|
||||||
|
params.put("inheritFromAclQNameId", inheritFromAclQNameId);
|
||||||
|
params.put("fixedAclType", fixedAclType);
|
||||||
|
params.put("globalAclType", globalAclType);
|
||||||
|
return template.selectOne(SELECT_IS_ACL_UNUSED, params) != null;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
protected List<Long> getADMNodeEntityIdsByAcl(long aclEntityId, int maxResults)
|
protected List<Long> getADMNodeEntityIdsByAcl(long aclEntityId, int maxResults)
|
||||||
@@ -284,6 +315,14 @@ public class AclCrudDAOImpl extends AbstractAclCrudDAOImpl
|
|||||||
return template.delete(DELETE_ACL_CHANGESET, params);
|
return template.delete(DELETE_ACL_CHANGESET, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int deleteAclChangeSetEntityIfUnused(long aclChangeSetEntityId)
|
||||||
|
{
|
||||||
|
Map<String, Object> params = new HashMap<>(1);
|
||||||
|
params.put("id", aclChangeSetEntityId);
|
||||||
|
return template.delete(DELETE_UNUSED_ACL_CHANGESET, params);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int updateChangeSetEntity(Long id, long commitTimeMs)
|
protected int updateChangeSetEntity(Long id, long commitTimeMs)
|
||||||
{
|
{
|
||||||
@@ -361,6 +400,14 @@ public class AclCrudDAOImpl extends AbstractAclCrudDAOImpl
|
|||||||
return template.delete(DELETE_ACES_LIST, aceEntityIds);
|
return template.delete(DELETE_ACES_LIST, aceEntityIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int deleteAceEntityIfUnused(long aceEntityId)
|
||||||
|
{
|
||||||
|
Map<String, Object> params = new HashMap<>(1);
|
||||||
|
params.put("id", aceEntityId);
|
||||||
|
return template.delete(DELETE_UNUSED_ACE, params);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected long createAceContextEntity(AceContextEntity entity)
|
protected long createAceContextEntity(AceContextEntity entity)
|
||||||
{
|
{
|
||||||
|
|||||||
+64
@@ -384,6 +384,52 @@
|
|||||||
where
|
where
|
||||||
acl.acl_id = ? and acl.latest = ?
|
acl.acl_id = ? and acl.latest = ?
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="select_UnusedAclIds" parameterType="map" resultType="long">
|
||||||
|
select
|
||||||
|
acl.id
|
||||||
|
from
|
||||||
|
alf_access_control_list acl
|
||||||
|
where
|
||||||
|
acl.id > #{afterAclId,jdbcType=BIGINT} and
|
||||||
|
acl.type not in (#{fixedAclType,jdbcType=INTEGER}, #{globalAclType,jdbcType=INTEGER}) and
|
||||||
|
not exists (select 1 from alf_node node where node.acl_id = acl.id) and
|
||||||
|
not exists (select 1 from alf_access_control_list inherited_acl where inherited_acl.inherited_acl = acl.id) and
|
||||||
|
not exists (select 1 from alf_access_control_list parent_acl where parent_acl.inherits_from = acl.id) and
|
||||||
|
not exists (
|
||||||
|
select 1
|
||||||
|
from alf_node_properties node_property
|
||||||
|
where node_property.long_value = acl.id and
|
||||||
|
node_property.qname_id in (
|
||||||
|
#{sharedAclToReplaceQNameId,jdbcType=BIGINT},
|
||||||
|
#{inheritFromAclQNameId,jdbcType=BIGINT}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
order by
|
||||||
|
acl.id
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="select_IsAclUnused" parameterType="map" resultType="long">
|
||||||
|
select
|
||||||
|
acl.id
|
||||||
|
from
|
||||||
|
alf_access_control_list acl
|
||||||
|
where
|
||||||
|
acl.id = #{id,jdbcType=BIGINT} and
|
||||||
|
acl.type not in (#{fixedAclType,jdbcType=INTEGER}, #{globalAclType,jdbcType=INTEGER}) and
|
||||||
|
not exists (select 1 from alf_node node where node.acl_id = acl.id) and
|
||||||
|
not exists (select 1 from alf_access_control_list inherited_acl where inherited_acl.inherited_acl = acl.id) and
|
||||||
|
not exists (select 1 from alf_access_control_list parent_acl where parent_acl.inherits_from = acl.id) and
|
||||||
|
not exists (
|
||||||
|
select 1
|
||||||
|
from alf_node_properties node_property
|
||||||
|
where node_property.long_value = acl.id and
|
||||||
|
node_property.qname_id in (
|
||||||
|
#{sharedAclToReplaceQNameId,jdbcType=BIGINT},
|
||||||
|
#{inheritFromAclQNameId,jdbcType=BIGINT}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
</select>
|
||||||
|
|
||||||
<select id="select_ADMNodesByAclId" parameterMap="parameter_IdMap" resultType="long">
|
<select id="select_ADMNodesByAclId" parameterMap="parameter_IdMap" resultType="long">
|
||||||
select
|
select
|
||||||
@@ -577,6 +623,15 @@
|
|||||||
#{item}
|
#{item}
|
||||||
</foreach>
|
</foreach>
|
||||||
</delete>
|
</delete>
|
||||||
|
|
||||||
|
<delete id="delete_UnusedAce" parameterMap="parameter_IdMap">
|
||||||
|
delete
|
||||||
|
from
|
||||||
|
alf_access_control_entry
|
||||||
|
where
|
||||||
|
id = ? and
|
||||||
|
not exists (select 1 from alf_acl_member member where member.ace_id = alf_access_control_entry.id)
|
||||||
|
</delete>
|
||||||
|
|
||||||
<!-- for patch -->
|
<!-- for patch -->
|
||||||
<delete id="delete_UnusedAces">
|
<delete id="delete_UnusedAces">
|
||||||
@@ -631,6 +686,15 @@
|
|||||||
where
|
where
|
||||||
id = ?
|
id = ?
|
||||||
</delete>
|
</delete>
|
||||||
|
|
||||||
|
<delete id="delete_UnusedAclChangeSet" parameterMap="parameter_IdMap">
|
||||||
|
delete
|
||||||
|
from
|
||||||
|
alf_acl_change_set
|
||||||
|
where
|
||||||
|
id = ? and
|
||||||
|
not exists (select 1 from alf_access_control_list acl where acl.acl_change_set = alf_acl_change_set.id)
|
||||||
|
</delete>
|
||||||
|
|
||||||
<delete id="delete_AceContext" parameterMap="parameter_IdMap">
|
<delete id="delete_AceContext" parameterMap="parameter_IdMap">
|
||||||
delete
|
delete
|
||||||
|
|||||||
@@ -127,6 +127,13 @@
|
|||||||
<property name="policyComponent" ref="policyComponent"/>
|
<property name="policyComponent" ref="policyComponent"/>
|
||||||
<property name="policyIgnoreUtil" ref="policyIgnoreUtil"/>
|
<property name="policyIgnoreUtil" ref="policyIgnoreUtil"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="unusedAclCleaner" class="org.alfresco.repo.domain.permissions.UnusedAclCleaner" init-method="init">
|
||||||
|
<property name="aclCrudDAO" ref="aclCrudDAO"/>
|
||||||
|
<property name="transactionService" ref="transactionService"/>
|
||||||
|
<property name="enabled" value="${system.aclCleanup.enabled}"/>
|
||||||
|
<property name="batchSize" value="${system.aclCleanup.batchSize}"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<!-- =================== -->
|
<!-- =================== -->
|
||||||
<!-- Dynamic Authorities -->
|
<!-- Dynamic Authorities -->
|
||||||
|
|||||||
@@ -1141,6 +1141,11 @@ system.fixedACLsUpdater.maxItems=-1
|
|||||||
# fixedACLsUpdater - Impose the order by in the query. If false, it may not process all the results but should do the queries faster
|
# fixedACLsUpdater - Impose the order by in the query. If false, it may not process all the results but should do the queries faster
|
||||||
system.fixedACLsUpdater.orderNodes=true
|
system.fixedACLsUpdater.orderNodes=true
|
||||||
|
|
||||||
|
# Unused ACL cleanup - enabled by default and limited to one batch per daily execution
|
||||||
|
system.aclCleanup.enabled=true
|
||||||
|
system.aclCleanup.cronExpression=0 0 2 * * ?
|
||||||
|
system.aclCleanup.batchSize=1000
|
||||||
|
|
||||||
cmis.disable.hidden.leading.period.files=false
|
cmis.disable.hidden.leading.period.files=false
|
||||||
|
|
||||||
#Smart Folders Config Properties
|
#Smart Folders Config Properties
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
<ref bean="upgradePasswordHashJobTrigger"/>
|
<ref bean="upgradePasswordHashJobTrigger"/>
|
||||||
<ref bean="patchAddUnmovableAspectTrigger"/>
|
<ref bean="patchAddUnmovableAspectTrigger"/>
|
||||||
<ref bean="fixedAclUpdaterTrigger"/>
|
<ref bean="fixedAclUpdaterTrigger"/>
|
||||||
|
<ref bean="unusedAclCleanupTrigger"/>
|
||||||
</list>
|
</list>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
@@ -227,4 +228,21 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="unusedAclCleanupTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
|
||||||
|
<property name="cronExpression" value="${system.aclCleanup.cronExpression}"/>
|
||||||
|
<property name="startDelay" value="${system.cronJob.startDelayMilliseconds}"/>
|
||||||
|
<property name="jobDetail">
|
||||||
|
<bean id="unusedAclCleanupJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
|
||||||
|
<property name="jobClass" value="org.alfresco.repo.domain.permissions.UnusedAclCleanupJob"/>
|
||||||
|
<property name="jobDataAsMap">
|
||||||
|
<map>
|
||||||
|
<entry key="unusedAclCleaner" value-ref="unusedAclCleaner"/>
|
||||||
|
<entry key="jobLockService" value-ref="jobLockService"/>
|
||||||
|
<entry key="name" value="unusedAclCleanupJob"/>
|
||||||
|
</map>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
|||||||
@@ -239,6 +239,8 @@ import org.alfresco.util.testing.category.NonBuildTests;
|
|||||||
org.alfresco.repo.content.caching.CachingContentStoreTest.class,
|
org.alfresco.repo.content.caching.CachingContentStoreTest.class,
|
||||||
org.alfresco.repo.content.caching.ContentCacheImplTest.class,
|
org.alfresco.repo.content.caching.ContentCacheImplTest.class,
|
||||||
org.alfresco.repo.domain.permissions.FixedAclUpdaterUnitTest.class,
|
org.alfresco.repo.domain.permissions.FixedAclUpdaterUnitTest.class,
|
||||||
|
org.alfresco.repo.domain.permissions.UnusedAclCleanerTest.class,
|
||||||
|
org.alfresco.repo.domain.permissions.UnusedAclCleanupJobTest.class,
|
||||||
org.alfresco.repo.domain.propval.PropertyTypeConverterTest.class,
|
org.alfresco.repo.domain.propval.PropertyTypeConverterTest.class,
|
||||||
org.alfresco.repo.domain.schema.script.ScriptBundleExecutorImplTest.class,
|
org.alfresco.repo.domain.schema.script.ScriptBundleExecutorImplTest.class,
|
||||||
org.alfresco.repo.search.MLAnaysisModeExpansionTest.class,
|
org.alfresco.repo.search.MLAnaysisModeExpansionTest.class,
|
||||||
|
|||||||
@@ -26,17 +26,25 @@
|
|||||||
package org.alfresco.repo.domain.permissions;
|
package org.alfresco.repo.domain.permissions;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
import org.junit.experimental.categories.Category;
|
import org.junit.experimental.categories.Category;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
|
import org.alfresco.repo.security.permissions.ACEType;
|
||||||
|
import org.alfresco.repo.security.permissions.ACLType;
|
||||||
import org.alfresco.repo.security.permissions.PermissionReference;
|
import org.alfresco.repo.security.permissions.PermissionReference;
|
||||||
|
import org.alfresco.repo.security.permissions.SimpleAccessControlListProperties;
|
||||||
import org.alfresco.repo.security.permissions.impl.SimplePermissionReference;
|
import org.alfresco.repo.security.permissions.impl.SimplePermissionReference;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
import org.alfresco.service.ServiceRegistry;
|
import org.alfresco.service.ServiceRegistry;
|
||||||
|
import org.alfresco.service.cmr.security.AccessStatus;
|
||||||
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.alfresco.test_category.OwnJVMTestsCategory;
|
import org.alfresco.test_category.OwnJVMTestsCategory;
|
||||||
@@ -57,7 +65,9 @@ public class AclCrudDAOTest extends TestCase
|
|||||||
private TransactionService transactionService;
|
private TransactionService transactionService;
|
||||||
private RetryingTransactionHelper txnHelper;
|
private RetryingTransactionHelper txnHelper;
|
||||||
private AclCrudDAO aclCrudDAO;
|
private AclCrudDAO aclCrudDAO;
|
||||||
|
private AclDAO aclDAO;
|
||||||
|
|
||||||
|
@Before
|
||||||
@Override
|
@Override
|
||||||
public void setUp() throws Exception
|
public void setUp() throws Exception
|
||||||
{
|
{
|
||||||
@@ -66,6 +76,75 @@ public class AclCrudDAOTest extends TestCase
|
|||||||
txnHelper = transactionService.getRetryingTransactionHelper();
|
txnHelper = transactionService.getRetryingTransactionHelper();
|
||||||
|
|
||||||
aclCrudDAO = (AclCrudDAO) ctx.getBean("aclCrudDAO");
|
aclCrudDAO = (AclCrudDAO) ctx.getBean("aclCrudDAO");
|
||||||
|
aclDAO = (AclDAO) ctx.getBean("aclDAO");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetUnusedAclIdsReturnsBoundedIsolatedAcls() throws Exception
|
||||||
|
{
|
||||||
|
List<Long> aclIds = txnHelper.doInTransaction(() -> {
|
||||||
|
SimpleAccessControlListProperties properties = new SimpleAccessControlListProperties();
|
||||||
|
properties.setAclType(ACLType.DEFINING);
|
||||||
|
properties.setInherits(true);
|
||||||
|
properties.setVersioned(false);
|
||||||
|
|
||||||
|
Long definingAclId = aclDAO.createAccessControlList(properties).getId();
|
||||||
|
Long sharedAclId = aclDAO.getInheritedAccessControlList(definingAclId);
|
||||||
|
properties.setInherits(false);
|
||||||
|
properties.setAclType(ACLType.FIXED);
|
||||||
|
aclDAO.createAccessControlList(properties);
|
||||||
|
properties.setAclType(ACLType.GLOBAL);
|
||||||
|
aclDAO.createAccessControlList(properties);
|
||||||
|
properties.setAclType(ACLType.DEFINING);
|
||||||
|
Long firstUnusedAclId = aclDAO.createAccessControlList(properties).getId();
|
||||||
|
Long secondUnusedAclId = aclDAO.createAccessControlList(properties).getId();
|
||||||
|
|
||||||
|
return Arrays.asList(sharedAclId, firstUnusedAclId, secondUnusedAclId);
|
||||||
|
});
|
||||||
|
|
||||||
|
List<Long> unusedAclIds = txnHelper.doInTransaction(() -> aclCrudDAO.getUnusedAclIds(aclIds.get(0), 1), true);
|
||||||
|
|
||||||
|
assertEquals(1, unusedAclIds.size());
|
||||||
|
assertEquals(aclIds.get(1), unusedAclIds.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteUnusedAclPreservesSharedAceUntilLastReferenceIsRemoved() throws Exception
|
||||||
|
{
|
||||||
|
List<Long> ids = txnHelper.doInTransaction(() -> {
|
||||||
|
SimpleAccessControlListProperties properties = new SimpleAccessControlListProperties();
|
||||||
|
properties.setAclType(ACLType.DEFINING);
|
||||||
|
properties.setInherits(false);
|
||||||
|
properties.setVersioned(false);
|
||||||
|
|
||||||
|
Long firstAclId = aclDAO.createAccessControlList(properties).getId();
|
||||||
|
Long secondAclId = aclDAO.createAccessControlList(properties).getId();
|
||||||
|
Authority authority = aclCrudDAO.getOrCreateAuthority("acl-cleanup-test-user");
|
||||||
|
Permission permission = aclCrudDAO.getOrCreatePermission(
|
||||||
|
SimplePermissionReference.getPermissionReference(
|
||||||
|
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "aclCleanupTest"), "Read"));
|
||||||
|
Ace ace = aclCrudDAO.getOrCreateAce(permission, authority, ACEType.ALL, AccessStatus.ALLOWED);
|
||||||
|
aclCrudDAO.addAclMembersToAcl(firstAclId, Arrays.asList(ace.getId()), 0);
|
||||||
|
aclCrudDAO.addAclMembersToAcl(secondAclId, Arrays.asList(ace.getId()), 0);
|
||||||
|
|
||||||
|
return Arrays.asList(firstAclId, secondAclId, ace.getId(), aclCrudDAO.getAcl(firstAclId).getAclChangeSetId());
|
||||||
|
});
|
||||||
|
|
||||||
|
assertTrue(txnHelper.doInTransaction(() -> aclCrudDAO.deleteUnusedAcl(ids.get(0))));
|
||||||
|
txnHelper.doInTransaction(() -> {
|
||||||
|
assertNull(aclCrudDAO.getAcl(ids.get(0)));
|
||||||
|
assertNotNull(aclCrudDAO.getAce(ids.get(2)));
|
||||||
|
assertNotNull(aclCrudDAO.getAclChangeSet(ids.get(3)));
|
||||||
|
return null;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
assertTrue(txnHelper.doInTransaction(() -> aclCrudDAO.deleteUnusedAcl(ids.get(1))));
|
||||||
|
txnHelper.doInTransaction(() -> {
|
||||||
|
assertNull(aclCrudDAO.getAcl(ids.get(1)));
|
||||||
|
assertNull(aclCrudDAO.getAce(ids.get(2)));
|
||||||
|
assertNull(aclCrudDAO.getAclChangeSet(ids.get(3)));
|
||||||
|
return null;
|
||||||
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - alf_access_control_list, alf_acl_member, alf_access_control_entry
|
// TODO - alf_access_control_list, alf_acl_member, alf_access_control_entry
|
||||||
|
|||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.domain.permissions;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
|
|
||||||
|
public class UnusedAclCleanerTest
|
||||||
|
{
|
||||||
|
private final AclCrudDAO aclCrudDAO = mock(AclCrudDAO.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cleanupBatchDeletesAtMostConfiguredCandidates()
|
||||||
|
{
|
||||||
|
UnusedAclCleaner cleaner = new UnusedAclCleaner();
|
||||||
|
cleaner.setAclCrudDAO(aclCrudDAO);
|
||||||
|
cleaner.setBatchSize(2);
|
||||||
|
when(aclCrudDAO.getUnusedAclIds(0, 2)).thenReturn(Arrays.asList(11L, 12L));
|
||||||
|
when(aclCrudDAO.deleteUnusedAcl(11L)).thenReturn(true);
|
||||||
|
when(aclCrudDAO.deleteUnusedAcl(12L)).thenReturn(false);
|
||||||
|
|
||||||
|
assertEquals(1, cleaner.cleanupBatch());
|
||||||
|
verify(aclCrudDAO).deleteUnusedAcl(11L);
|
||||||
|
verify(aclCrudDAO).deleteUnusedAcl(12L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void disabledCleanerDoesNothing()
|
||||||
|
{
|
||||||
|
UnusedAclCleaner cleaner = new UnusedAclCleaner();
|
||||||
|
cleaner.setAclCrudDAO(aclCrudDAO);
|
||||||
|
cleaner.setTransactionService(mock(TransactionService.class));
|
||||||
|
cleaner.setEnabled(false);
|
||||||
|
|
||||||
|
assertEquals(0, cleaner.execute());
|
||||||
|
verifyNoInteractions(aclCrudDAO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void rejectsInvalidBatchSize()
|
||||||
|
{
|
||||||
|
new UnusedAclCleaner().setBatchSize(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.domain.permissions;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.quartz.JobBuilder;
|
||||||
|
import org.quartz.JobDetail;
|
||||||
|
import org.quartz.JobExecutionContext;
|
||||||
|
import org.quartz.JobExecutionException;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
|
||||||
|
public class UnusedAclCleanupJobTest
|
||||||
|
{
|
||||||
|
private final JobExecutionContext context = mock(JobExecutionContext.class);
|
||||||
|
private final UnusedAclCleaner cleaner = mock(UnusedAclCleaner.class);
|
||||||
|
private JobDetail jobDetail;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp()
|
||||||
|
{
|
||||||
|
jobDetail = JobBuilder.newJob(UnusedAclCleanupJob.class).withIdentity("unusedAclCleanupJob").build();
|
||||||
|
jobDetail.getJobDataMap().put("unusedAclCleaner", cleaner);
|
||||||
|
when(context.getJobDetail()).thenReturn(jobDetail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void delegatesCleanup() throws JobExecutionException
|
||||||
|
{
|
||||||
|
new UnusedAclCleanupJob().executeJob(context);
|
||||||
|
|
||||||
|
verify(cleaner).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AlfrescoRuntimeException.class)
|
||||||
|
public void rejectsMissingCleaner() throws JobExecutionException
|
||||||
|
{
|
||||||
|
jobDetail.getJobDataMap().remove("unusedAclCleaner");
|
||||||
|
new UnusedAclCleanupJob().executeJob(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user