mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
DM Usages/Quotas fixes - protect system/admin-maintained properties, make store(s) configurable
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@7500 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
<import resource="classpath:alfresco/index-recovery-context.xml" />
|
||||
<import resource="classpath:alfresco/authority-services-context.xml" />
|
||||
<import resource="classpath:alfresco/authentication-services-context.xml" />
|
||||
<import resource="classpath:alfresco/usage-services-context.xml" />
|
||||
<import resource="classpath:alfresco/policy-context.xml" />
|
||||
<import resource="classpath:alfresco/import-export-context.xml" />
|
||||
<import resource="classpath:alfresco/bootstrap-context.xml" />
|
||||
|
@@ -201,9 +201,11 @@
|
||||
<!-- system maintained values -->
|
||||
<property name="cm:sizeCurrent">
|
||||
<type>d:long</type>
|
||||
<protected>true</protected>
|
||||
</property>
|
||||
<property name="cm:sizeQuota">
|
||||
<type>d:long</type>
|
||||
<protected>true</protected>
|
||||
</property>
|
||||
</properties>
|
||||
|
||||
|
@@ -197,26 +197,4 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="usageService" class="org.alfresco.repo.usage.UsageServiceImpl">
|
||||
<property name="usageDeltaDao">
|
||||
<ref bean="usageDeltaDao"/>
|
||||
</property>
|
||||
<property name="nodeDaoService">
|
||||
<ref bean="nodeDaoService" />
|
||||
</property>
|
||||
<property name="tenantService">
|
||||
<ref bean="tenantService"/>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="contentUsageImpl" class="org.alfresco.repo.usage.ContentUsageImpl" init-method="init">
|
||||
<property name="personService" ref="personService"/>
|
||||
<property name="nodeService" ref="nodeService"/>
|
||||
<property name="policyComponent" ref="policyComponent"/>
|
||||
<property name="usageService" ref="usageService"/>
|
||||
<property name="enabled">
|
||||
<value>${system.usages.enabled}</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
41
config/alfresco/usage-services-context.xml
Normal file
41
config/alfresco/usage-services-context.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
|
||||
|
||||
<!-- Beans pertinent to content usage / quota service -->
|
||||
<beans>
|
||||
|
||||
<bean id="usageService" class="org.alfresco.repo.usage.UsageServiceImpl">
|
||||
<property name="usageDeltaDao">
|
||||
<ref bean="usageDeltaDao"/>
|
||||
</property>
|
||||
<property name="nodeDaoService">
|
||||
<ref bean="nodeDaoService" />
|
||||
</property>
|
||||
<property name="tenantService">
|
||||
<ref bean="tenantService"/>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="contentUsageImpl" class="org.alfresco.repo.usage.ContentUsageImpl" init-method="init">
|
||||
<property name="personService" ref="personService"/>
|
||||
<property name="nodeService" ref="nodeService"/>
|
||||
<property name="policyComponent" ref="policyComponent"/>
|
||||
<property name="usageService" ref="usageService"/>
|
||||
<property name="enabled">
|
||||
<value>${system.usages.enabled}</value>
|
||||
</property>
|
||||
<property name="stores">
|
||||
<list>
|
||||
<value>workspace://SpacesStore</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="usageQuotaProtector" class="org.alfresco.repo.usage.UsageQuotaProtector" init-method="init">
|
||||
<property name="authorityService" ref="authorityService"/>
|
||||
<property name="authenticationService" ref="authenticationService"/>
|
||||
<property name="policyComponent" ref="policyComponent"/>
|
||||
<property name="contentUsageService" ref="ContentUsageService"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
@@ -25,6 +25,7 @@
|
||||
package org.alfresco.repo.usage;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
@@ -35,7 +36,6 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.ContentData;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.security.PersonService;
|
||||
import org.alfresco.service.cmr.usage.ContentQuotaException;
|
||||
import org.alfresco.service.cmr.usage.ContentUsageService;
|
||||
@@ -64,8 +64,7 @@ public class ContentUsageImpl implements ContentUsageService,
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public static final StoreRef SPACES_STOREREF = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
|
||||
|
||||
private List<String> stores;
|
||||
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
@@ -92,6 +91,15 @@ public class ContentUsageImpl implements ContentUsageService,
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public void setStores(List<String> stores)
|
||||
{
|
||||
this.stores = stores;
|
||||
}
|
||||
|
||||
public List<String> getStores()
|
||||
{
|
||||
return this.stores;
|
||||
}
|
||||
|
||||
/**
|
||||
* The initialise method
|
||||
@@ -129,7 +137,7 @@ public class ContentUsageImpl implements ContentUsageService,
|
||||
public void onCreateNode(ChildAssociationRef childAssocRef)
|
||||
{
|
||||
NodeRef nodeRef = childAssocRef.getChildRef();
|
||||
if (nodeRef.getStoreRef().equals(SPACES_STOREREF))
|
||||
if (stores.contains(nodeRef.getStoreRef().toString()))
|
||||
{
|
||||
// Get content size
|
||||
|
||||
@@ -165,7 +173,7 @@ public class ContentUsageImpl implements ContentUsageService,
|
||||
Map<QName, Serializable> before,
|
||||
Map<QName, Serializable> after)
|
||||
{
|
||||
if (nodeRef.getStoreRef().equals(SPACES_STOREREF))
|
||||
if (stores.contains(nodeRef.getStoreRef().toString()))
|
||||
{
|
||||
// Check for change in content size
|
||||
|
||||
@@ -258,7 +266,7 @@ public class ContentUsageImpl implements ContentUsageService,
|
||||
*/
|
||||
public void beforeDeleteNode(NodeRef nodeRef)
|
||||
{
|
||||
if (nodeRef.getStoreRef().equals(SPACES_STOREREF))
|
||||
if (stores.contains(nodeRef.getStoreRef().toString()))
|
||||
{
|
||||
// TODO use data dictionary to get content property
|
||||
ContentData contentData = (ContentData)nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
|
||||
|
118
source/java/org/alfresco/repo/usage/UsageQuotaProtector.java
Normal file
118
source/java/org/alfresco/repo/usage/UsageQuotaProtector.java
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.usage;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.node.NodeServicePolicies;
|
||||
import org.alfresco.repo.policy.JavaBehaviour;
|
||||
import org.alfresco.repo.policy.PolicyComponent;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.security.AuthenticationService;
|
||||
import org.alfresco.service.cmr.security.AuthorityService;
|
||||
import org.alfresco.service.cmr.usage.ContentUsageService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* Implements policies/behaviour for protecting system/admin-maintained person properties
|
||||
*
|
||||
*/
|
||||
public class UsageQuotaProtector implements NodeServicePolicies.OnUpdatePropertiesPolicy
|
||||
{
|
||||
private AuthorityService authorityService;
|
||||
private AuthenticationService authenticationService;
|
||||
private PolicyComponent policyComponent;
|
||||
private ContentUsageService contentUsageService;
|
||||
|
||||
public void setAuthorityService(AuthorityService authorityService)
|
||||
{
|
||||
this.authorityService = authorityService;
|
||||
}
|
||||
|
||||
public void setAuthenticationService(AuthenticationService authenticationService)
|
||||
{
|
||||
this.authenticationService = authenticationService;
|
||||
}
|
||||
|
||||
public void setContentUsageService(ContentUsageService contentUsageService)
|
||||
{
|
||||
this.contentUsageService = contentUsageService;
|
||||
}
|
||||
|
||||
public void setPolicyComponent(PolicyComponent policyComponent)
|
||||
{
|
||||
this.policyComponent = policyComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* The initialise method
|
||||
*/
|
||||
public void init()
|
||||
{
|
||||
if (contentUsageService.getEnabled())
|
||||
{
|
||||
// Register interest in the onUpdateProperties policy
|
||||
policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
|
||||
ContentModel.TYPE_PERSON,
|
||||
new JavaBehaviour(this, "onUpdateProperties"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a node's properties have been changed.
|
||||
*
|
||||
* @param nodeRef reference to the updated node
|
||||
* @param before the node's properties before the change
|
||||
* @param after the node's properties after the change
|
||||
*/
|
||||
public void onUpdateProperties(
|
||||
NodeRef nodeRef,
|
||||
Map<QName, Serializable> before,
|
||||
Map<QName, Serializable> after)
|
||||
{
|
||||
Long sizeCurrentBefore = (Long)before.get(ContentModel.PROP_SIZE_CURRENT);
|
||||
Long sizeCurrentAfter = (Long)after.get(ContentModel.PROP_SIZE_CURRENT);
|
||||
|
||||
Long sizeQuotaBefore = (Long)before.get(ContentModel.PROP_SIZE_QUOTA);
|
||||
Long sizeQuotaAfter = (Long)after.get(ContentModel.PROP_SIZE_QUOTA);
|
||||
|
||||
// Check for change in sizeCurrent
|
||||
if ((sizeCurrentBefore != sizeCurrentAfter) && (! (authorityService.hasAdminAuthority() || authenticationService.isCurrentUserTheSystemUser())))
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Update failed: protected property 'sizeCurrent'");
|
||||
}
|
||||
|
||||
// Check for change in sizeQuota
|
||||
if ((sizeQuotaBefore != sizeQuotaAfter) && (! authorityService.hasAdminAuthority()))
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Update failed: protected property 'sizeQuota'");
|
||||
}
|
||||
}
|
||||
}
|
@@ -26,11 +26,13 @@ package org.alfresco.repo.usage;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.domain.Node;
|
||||
import org.alfresco.repo.node.db.NodeDaoService;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.repo.transaction.TransactionServiceImpl;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
@@ -201,52 +203,56 @@ public class UserUsageTrackingComponent
|
||||
else
|
||||
{
|
||||
// Collapse usage deltas (if a person has initial usage set)
|
||||
RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
|
||||
final RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
|
||||
|
||||
// wrap to make the request in a transaction
|
||||
RetryingTransactionCallback<Object> collapseUsages = new RetryingTransactionCallback<Object>()
|
||||
// wrap to make the request in a transaction and run as System user
|
||||
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
|
||||
{
|
||||
public Object execute() throws Throwable
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
// Get distinct candidates
|
||||
Set<NodeRef> usageNodeRefs = usageService.getUsageDeltaNodes();
|
||||
|
||||
for(NodeRef usageNodeRef : usageNodeRefs)
|
||||
return txnHelper.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
QName nodeType = nodeService.getType(usageNodeRef);
|
||||
|
||||
if (nodeType.equals(ContentModel.TYPE_PERSON))
|
||||
public Object execute() throws Throwable
|
||||
{
|
||||
NodeRef personNodeRef = usageNodeRef;
|
||||
String userName = (String)nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME);
|
||||
// Get distinct candidates
|
||||
Set<NodeRef> usageNodeRefs = usageService.getUsageDeltaNodes();
|
||||
|
||||
long currentUsage = contentUsageImpl.getUserStoredUsage(personNodeRef);
|
||||
if (currentUsage != -1)
|
||||
for(NodeRef usageNodeRef : usageNodeRefs)
|
||||
{
|
||||
// collapse the usage deltas
|
||||
currentUsage = contentUsageImpl.getUserUsage(userName);
|
||||
usageService.deleteDeltas(personNodeRef);
|
||||
contentUsageImpl.setUserStoredUsage(personNodeRef, currentUsage);
|
||||
QName nodeType = nodeService.getType(usageNodeRef);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
if (nodeType.equals(ContentModel.TYPE_PERSON))
|
||||
{
|
||||
logger.debug("Collapsed usage: username=" + userName + ", usage=" + currentUsage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isWarnEnabled())
|
||||
{
|
||||
logger.warn("Initial usage for user has not yet been calculated: " + userName);
|
||||
NodeRef personNodeRef = usageNodeRef;
|
||||
String userName = (String)nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME);
|
||||
|
||||
long currentUsage = contentUsageImpl.getUserStoredUsage(personNodeRef);
|
||||
if (currentUsage != -1)
|
||||
{
|
||||
// collapse the usage deltas
|
||||
currentUsage = contentUsageImpl.getUserUsage(userName);
|
||||
usageService.deleteDeltas(personNodeRef);
|
||||
contentUsageImpl.setUserStoredUsage(personNodeRef, currentUsage);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Collapsed usage: username=" + userName + ", usage=" + currentUsage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isWarnEnabled())
|
||||
{
|
||||
logger.warn("Initial usage for user has not yet been calculated: " + userName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
txnHelper.doInTransaction(collapseUsages, false);
|
||||
}, AuthenticationUtil.getSystemUserName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,45 +271,50 @@ public class UserUsageTrackingComponent
|
||||
*/
|
||||
public void recalculateUsage(final String userName)
|
||||
{
|
||||
final StoreRef storeRef = ContentUsageImpl.SPACES_STOREREF;
|
||||
|
||||
RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
|
||||
final RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
|
||||
|
||||
// wrap to make the request in a transaction
|
||||
RetryingTransactionCallback<Long> calculatePersonCurrentUsage = new RetryingTransactionCallback<Long>()
|
||||
{
|
||||
public Long execute() throws Throwable
|
||||
{
|
||||
// get nodes for which user is owner
|
||||
Collection<Node> ownerNodes = nodeDaoService.getNodesWithPropertyStringValueForStore(storeRef, ContentModel.PROP_OWNER, userName);
|
||||
|
||||
List<String> stores = contentUsageImpl.getStores();
|
||||
long totalUsage = 0;
|
||||
for (Node ownerNode : ownerNodes)
|
||||
|
||||
for (String store : stores)
|
||||
{
|
||||
if (ownerNode.getTypeQName().equals(ContentModel.TYPE_CONTENT))
|
||||
StoreRef storeRef = new StoreRef(store);
|
||||
|
||||
// get nodes for which user is owner
|
||||
Collection<Node> ownerNodes = nodeDaoService.getNodesWithPropertyStringValueForStore(storeRef, ContentModel.PROP_OWNER, userName);
|
||||
|
||||
for (Node ownerNode : ownerNodes)
|
||||
{
|
||||
ContentData contentData = ContentData.createContentProperty(ownerNode.getProperties().get(ContentModel.PROP_CONTENT).getStringValue());
|
||||
totalUsage = totalUsage + contentData.getSize();
|
||||
if (ownerNode.getTypeQName().equals(ContentModel.TYPE_CONTENT))
|
||||
{
|
||||
ContentData contentData = ContentData.createContentProperty(ownerNode.getProperties().get(ContentModel.PROP_CONTENT).getStringValue());
|
||||
totalUsage = totalUsage + contentData.getSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get nodes for which user is creator, and then filter out those that have an owner
|
||||
Collection<Node> creatorNodes = nodeDaoService.getNodesWithPropertyStringValueForStore(storeRef, ContentModel.PROP_CREATOR, userName);
|
||||
// get nodes for which user is creator, and then filter out those that have an owner
|
||||
Collection<Node> creatorNodes = nodeDaoService.getNodesWithPropertyStringValueForStore(storeRef, ContentModel.PROP_CREATOR, userName);
|
||||
|
||||
for (Node creatorNode : creatorNodes)
|
||||
{
|
||||
if (creatorNode.getTypeQName().equals(ContentModel.TYPE_CONTENT) &&
|
||||
creatorNode.getProperties().get(ContentModel.PROP_OWNER) == null)
|
||||
for (Node creatorNode : creatorNodes)
|
||||
{
|
||||
ContentData contentData = ContentData.createContentProperty(creatorNode.getProperties().get(ContentModel.PROP_CONTENT).getStringValue());
|
||||
totalUsage = totalUsage + contentData.getSize();
|
||||
if (creatorNode.getTypeQName().equals(ContentModel.TYPE_CONTENT) &&
|
||||
creatorNode.getProperties().get(ContentModel.PROP_OWNER) == null)
|
||||
{
|
||||
ContentData contentData = ContentData.createContentProperty(creatorNode.getProperties().get(ContentModel.PROP_CONTENT).getStringValue());
|
||||
totalUsage = totalUsage + contentData.getSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
long quotaSize = contentUsageImpl.getUserQuota(userName);
|
||||
logger.debug("Recalc usage ("+ userName+") totalUsage="+totalUsage+", quota="+quotaSize);
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
long quotaSize = contentUsageImpl.getUserQuota(userName);
|
||||
logger.debug("Recalc usage ("+ userName+") totalUsage="+totalUsage+", quota="+quotaSize);
|
||||
}
|
||||
}
|
||||
|
||||
return totalUsage;
|
||||
@@ -312,17 +323,22 @@ public class UserUsageTrackingComponent
|
||||
// execute in READ-ONLY txn
|
||||
final Long currentUsage = txnHelper.doInTransaction(calculatePersonCurrentUsage, true);
|
||||
|
||||
// wrap to make the request in a transaction
|
||||
RetryingTransactionCallback<Object> setUserCurrentUsage = new RetryingTransactionCallback<Object>()
|
||||
// wrap to make the request in a transaction and run as System user
|
||||
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
|
||||
{
|
||||
public Object execute() throws Throwable
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
NodeRef personNodeRef = personService.getPerson(userName);
|
||||
contentUsageImpl.setUserStoredUsage(personNodeRef, currentUsage);
|
||||
usageService.deleteDeltas(personNodeRef);
|
||||
return null;
|
||||
return txnHelper.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
public Object execute() throws Throwable
|
||||
{
|
||||
NodeRef personNodeRef = personService.getPerson(userName);
|
||||
contentUsageImpl.setUserStoredUsage(personNodeRef, currentUsage);
|
||||
usageService.deleteDeltas(personNodeRef);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
txnHelper.doInTransaction(setUserCurrentUsage, false);
|
||||
}, AuthenticationUtil.getSystemUserName());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user