diff --git a/config/alfresco/activities/activities-feed-context.xml b/config/alfresco/activities/activities-feed-context.xml index 99f0882ed4..2e10391e66 100644 --- a/config/alfresco/activities/activities-feed-context.xml +++ b/config/alfresco/activities/activities-feed-context.xml @@ -8,6 +8,7 @@ + @@ -59,6 +60,7 @@ + diff --git a/config/alfresco/application-context.xml b/config/alfresco/application-context.xml index c4a54b0294..6d36653d8d 100644 --- a/config/alfresco/application-context.xml +++ b/config/alfresco/application-context.xml @@ -56,6 +56,7 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${user.name.caseSensitive} - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + ${user.name.caseSensitive} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - org.alfresco.repo.security.authentication.AuthenticationComponent - - - - - - - - - - ${server.transaction.mode.default} - - - + + + + + + + + org.alfresco.repo.security.authentication.AuthenticationComponent + + + + + + + + + + ${server.transaction.mode.default} + + + @@ -160,301 +160,288 @@ - + - - - - - - - - true - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - ${spaces.store} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${spaces.store} - - - - - - - - - - - ${server.transaction.allow-writes} - - - ${user.name.caseSensitive} - - - - true - - - - SPLIT - - - true - - - false - - - - - - - - - - - - - - - - - - - - - /${spaces.company_home.childname} - - - ${spaces.store} - - - - - - - - - - - - /${spaces.company_home.childname}/${spaces.guest_home.childname} - - - ${spaces.store} - - - - - - - Consumer - - - - - - - - - - - - - - - - /${spaces.company_home.childname} - - - ${spaces.store} - - - - - - false - - - - All - - - - - All - - - - - - - - - - /${spaces.company_home.childname}/${spaces.user_homes.childname} - - - ${spaces.store} - - - - - - false - - - - All - - - - - All - - - - - - - - - - - - - - - PT1H - - - - false - - - - false - - - - - - AFTER_FIXED_TIME - - - - - - - 6 - - - - - - - 8 - - + parent="authenticationComponentBase"> + + true + + + + + + + + + + + + + --> + + + + + + + + + + + + + + + + + + + ${spaces.store} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${spaces.store} + + + + + + + + + + + ${server.transaction.allow-writes} + + + ${user.name.caseSensitive} + + + + true + + + + SPLIT + + + true + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /${spaces.company_home.childname} + + + ${spaces.store} + + + + + + /${spaces.company_home.childname}/${spaces.guest_home.childname} + + + ${spaces.store} + + + + Consumer + + + + + + + + + /${spaces.company_home.childname} + + + ${spaces.store} + + + false + + + + All + + + + + All + + + + + + + /${spaces.company_home.childname}/${spaces.user_homes.childname} + + + ${spaces.store} + + + false + + + + All + + + + + All + + + + + + + + + + + + + + + PT1H + + + + false + + + + false + + + + + + AFTER_FIXED_TIME + + + + + + + 6 + + + + + + + + + + 8 + + \ No newline at end of file diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index 5f824881bd..760895acb9 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -383,7 +383,7 @@ jbpm alfresco/workflow/invitation-moderated_processdefinition.xml text/xml - true + false diff --git a/config/alfresco/extension/mt/mt-admin-context.xml.sample b/config/alfresco/extension/mt/mt-admin-context.xml.sample index 318d3bee71..2a2604ac25 100644 --- a/config/alfresco/extension/mt/mt-admin-context.xml.sample +++ b/config/alfresco/extension/mt/mt-admin-context.xml.sample @@ -7,26 +7,7 @@ - - - - - - - - - - - - - - - - - - + diff --git a/config/alfresco/mt/mt-base-context.xml b/config/alfresco/mt/mt-base-context.xml new file mode 100644 index 0000000000..9901bd2966 --- /dev/null +++ b/config/alfresco/mt/mt-base-context.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index 457b0ff937..a8e9ab3390 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -828,7 +828,7 @@ org.alfresco.service.cmr.avm.AVMService - + @@ -837,6 +837,15 @@ + + + + + + + + + diff --git a/config/alfresco/site-services-context.xml b/config/alfresco/site-services-context.xml index 0acaff9506..5fba8a6718 100644 --- a/config/alfresco/site-services-context.xml +++ b/config/alfresco/site-services-context.xml @@ -74,6 +74,7 @@ ./${spaces.company_home.childname}/st:sites + diff --git a/config/alfresco/workflow-context.xml b/config/alfresco/workflow-context.xml index d67de37e67..363691b49b 100644 --- a/config/alfresco/workflow-context.xml +++ b/config/alfresco/workflow-context.xml @@ -18,6 +18,8 @@ + + diff --git a/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java b/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java index 6373ecf6f2..09730df654 100644 --- a/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java +++ b/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Alfresco Software Limited. + * Copyright (C) 2005-2009 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 @@ -38,6 +38,7 @@ import org.alfresco.repo.activities.feed.control.FeedControlDaoService; import org.alfresco.repo.activities.post.ActivityPostDAO; import org.alfresco.repo.activities.post.ActivityPostDaoService; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.cmr.activities.ActivityService; import org.alfresco.service.cmr.activities.FeedControl; import org.alfresco.service.cmr.repository.NodeRef; @@ -69,6 +70,8 @@ public class ActivityServiceImpl implements ActivityService private AuthorityService authorityService; private FeedGenerator feedGenerator; + private TenantService tenantService; + private int maxFeedItems = 100; private boolean userNamesAreCaseSensitive = false; @@ -108,32 +111,37 @@ public class ActivityServiceImpl implements ActivityService this.feedGenerator = feedGenerator; } + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + /* (non-Javadoc) * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, java.lang.String) */ - public void postActivity(String activityType, String network, String appTool, String activityData) + public void postActivity(String activityType, String siteId, String appTool, String activityData) { - postActivity(activityType, network, appTool, activityData, ActivityPostDAO.STATUS.PENDING); + postActivity(activityType, siteId, appTool, activityData, ActivityPostDAO.STATUS.PENDING); } /* (non-Javadoc) * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef) */ - public void postActivity(String activityType, String network, String appTool, NodeRef nodeRef) + public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef) { ParameterCheck.mandatory("nodeRef", nodeRef); StringBuffer sb = new StringBuffer(); sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append("}"); - postActivity(activityType, network, appTool, sb.toString(), ActivityPostDAO.STATUS.PENDING); + postActivity(activityType, siteId, appTool, sb.toString(), ActivityPostDAO.STATUS.PENDING); } /* (non-Javadoc) * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String) */ - public void postActivity(String activityType, String network, String appTool, NodeRef nodeRef, String name) + public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef, String name) { ParameterCheck.mandatory("nodeRef", nodeRef); @@ -142,13 +150,13 @@ public class ActivityServiceImpl implements ActivityService .append("\"name\":\"").append(name).append("\"") .append("}"); - postActivity(activityType, network, appTool, sb.toString(), ActivityPostDAO.STATUS.PENDING); + postActivity(activityType, siteId, appTool, sb.toString(), ActivityPostDAO.STATUS.PENDING); } /* (non-Javadoc) * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef) */ - public void postActivity(String activityType, String network, String appTool, NodeRef nodeRef, String name, QName typeQName, NodeRef parentNodeRef) + public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef, String name, QName typeQName, NodeRef parentNodeRef) { // primarily for delete node activities - eg. delete document, delete folder @@ -163,23 +171,23 @@ public class ActivityServiceImpl implements ActivityService .append("\"parentNodeRef\":\"").append(parentNodeRef.toString()).append("\"") .append("}"); - postActivity(activityType, network, appTool, sb.toString(), ActivityPostDAO.STATUS.PENDING); + postActivity(activityType, siteId, appTool, sb.toString(), ActivityPostDAO.STATUS.PENDING); } - private void postActivity(String activityType, String siteNetwork, String appTool, String activityData, ActivityPostDAO.STATUS status) + private void postActivity(String activityType, String siteId, String appTool, String activityData, ActivityPostDAO.STATUS status) { String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); try { // optional - default to empty string - if (siteNetwork == null) + if (siteId == null) { - siteNetwork = ""; + siteId = ""; } - else if (siteNetwork.length() > MAX_LEN_SITE_ID) + else if (siteId.length() > MAX_LEN_SITE_ID) { - throw new AlfrescoRuntimeException("Invalid site network - exceeds " + MAX_LEN_SITE_ID + " chars: " + siteNetwork); + throw new AlfrescoRuntimeException("Invalid siteId - exceeds " + MAX_LEN_SITE_ID + " chars: " + siteId); } // optional - default to empty string @@ -235,7 +243,9 @@ public class ActivityServiceImpl implements ActivityService Date postDate = new Date(); ActivityPostDAO activityPost = new ActivityPostDAO(); activityPost.setUserId(currentUser); - activityPost.setSiteNetwork(siteNetwork); + + activityPost.setSiteNetwork(tenantService.getName(siteId)); + activityPost.setAppTool(appTool); activityPost.setActivityData(activityData); activityPost.setActivityType(activityType); @@ -297,6 +307,8 @@ public class ActivityServiceImpl implements ActivityService List activityFeeds = null; if (siteId != null) { + siteId = tenantService.getName(siteId); + activityFeeds = feedDaoService.selectUserFeedEntries(feedUserId, format, siteId); } else @@ -312,6 +324,8 @@ public class ActivityServiceImpl implements ActivityService { break; } + + activityFeed.setSiteNetwork(tenantService.getBaseName(activityFeed.getSiteNetwork())); activityFeedEntries.add(activityFeed.getJSONString()); } } @@ -343,6 +357,8 @@ public class ActivityServiceImpl implements ActivityService try { + siteId = tenantService.getName(siteId); + List activityFeeds = feedDaoService.selectSiteFeedEntries(siteId, format); int count = 0; @@ -353,6 +369,8 @@ public class ActivityServiceImpl implements ActivityService { break; } + + activityFeed.setSiteNetwork(tenantService.getBaseName(activityFeed.getSiteNetwork())); activityFeedEntries.add(activityFeed.getJSONString()); } } @@ -504,4 +522,16 @@ public class ActivityServiceImpl implements ActivityService throw are; } } + + private FeedControl getTenantFeedControl(FeedControl feedControl) + { + // TODO + return null; + } + + private FeedControl getBaseFeedControl(FeedControl feedControl) + { + // TODO + return null; + } } diff --git a/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java b/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java index 6adef304ca..d822d486a5 100644 --- a/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java +++ b/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java @@ -1,338 +1,351 @@ -/* - * Copyright (C) 2005-2008 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.activities.post.lookup; - -import java.sql.SQLException; -import java.util.Date; -import java.util.List; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.activities.post.ActivityPostDAO; -import org.alfresco.repo.activities.post.ActivityPostDaoService; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.Path; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.Pair; -import org.alfresco.util.PropertyCheck; -import org.alfresco.util.VmShutdownListener; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; -import org.quartz.JobExecutionException; - -/** - * The post lookup component is responsible for updating posts that require a secondary lookup (to get additional activity data) - */ -public class PostLookup -{ - private static Log logger = LogFactory.getLog(PostLookup.class); - - private static VmShutdownListener vmShutdownListener = new VmShutdownListener(PostLookup.class.getName()); - - private ActivityPostDaoService postDaoService; - private NodeService nodeService; - private PermissionService permissionService; - private TransactionService transactionService; - private PersonService personService; - - public void setPostDaoService(ActivityPostDaoService postDaoService) - { - this.postDaoService = postDaoService; - } - - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - public void setPermissionService(PermissionService permissionService) - { - this.permissionService = permissionService; - } - - public void setTransactionService(TransactionService transactionService) - { - this.transactionService = transactionService; - } - - public void setPersonService(PersonService personService) - { - this.personService = personService; - } - - /** - * Perform basic checks to ensure that the necessary dependencies were injected. - */ - private void checkProperties() - { - PropertyCheck.mandatory(this, "postDaoService", postDaoService); - PropertyCheck.mandatory(this, "nodeService", nodeService); - PropertyCheck.mandatory(this, "permissionService", permissionService); - PropertyCheck.mandatory(this, "transactionService", transactionService); - PropertyCheck.mandatory(this, "personService", personService); - } - - public void execute() throws JobExecutionException - { - checkProperties(); - try - { - ActivityPostDAO params = new ActivityPostDAO(); - params.setStatus(ActivityPostDAO.STATUS.PENDING.toString()); - - List activityPosts = postDaoService.selectPosts(params); - - if (activityPosts.size() > 0) - { - logger.info("Update: " + activityPosts.size() + " activity posts"); - } - - for (ActivityPostDAO activityPost : activityPosts) - { - try - { - postDaoService.startTransaction(); - - JSONObject jo = new JSONObject(new JSONTokener(activityPost.getActivityData())); - String postUserId = activityPost.getUserId(); - - if (! jo.isNull("nodeRef")) - { - String nodeRefStr = jo.getString("nodeRef"); - NodeRef nodeRef = new NodeRef(nodeRefStr); - - // lookup additional node data - JSONObject activityData = lookupNode(nodeRef, postUserId, jo); - - activityPost.setActivityData(activityData.toString()); - } - else - { - // lookup additional person data - Pair firstLastName = lookupPerson(postUserId); - if (firstLastName != null) - { - jo.put("firstName", firstLastName.getFirst()); - jo.put("lastName", firstLastName.getSecond()); - - activityPost.setActivityData(jo.toString()); - } - } - - activityPost.setLastModified(new Date()); - - postDaoService.updatePost(activityPost.getId(), activityPost.getSiteNetwork(), activityPost.getActivityData(), ActivityPostDAO.STATUS.POSTED); - if (logger.isDebugEnabled()) - { - activityPost.setStatus(ActivityPostDAO.STATUS.POSTED.toString()); // for debug output - logger.debug("Updated: " + activityPost); - } - - postDaoService.commitTransaction(); - } - catch (JSONException e) - { - // log error, but consume exception (skip this post) - logger.error("Skipping activity post " + activityPost.getId() + ": " + e); - postDaoService.updatePostStatus(activityPost.getId(), ActivityPostDAO.STATUS.ERROR); - - postDaoService.commitTransaction(); - } - catch (SQLException e) - { - logger.error("Exception during update of post", e); - throw new JobExecutionException(e); - } - finally - { - postDaoService.endTransaction(); - } - } - } - catch (SQLException e) - { - logger.error("Exception during select of posts", e); - throw new JobExecutionException(e); - } - catch (Throwable e) - { - // If the VM is shutting down, then ignore - if (vmShutdownListener.isVmShuttingDown()) - { - // Ignore - } - else - { - logger.error("Exception during update of posts", e); - } - } - } - - private Pair lookupPerson(final String postUserId) throws JSONException - { - return AuthenticationUtil.runAs(new RunAsWork>() - { - public Pair doWork() throws Exception - { - RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); - - // wrap to make the request in a transaction - RetryingTransactionCallback> lookup = new RetryingTransactionCallback>() - { - public Pair execute() throws Throwable - { - String firstName = ""; - String lastName = ""; - - if (personService.personExists(postUserId)) - { - NodeRef personRef = personService.getPerson(postUserId); - - firstName = (String)nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME); - lastName = (String)nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME); - - return new Pair(firstName, lastName); - } - - return null; - } - }; - - // execute in txn - return txnHelper.doInTransaction(lookup, true); - } - }, AuthenticationUtil.getSystemUserName()); - } - - private JSONObject lookupNode(final NodeRef nodeRef, final String postUserId, final JSONObject jo) throws JSONException - { - return AuthenticationUtil.runAs(new RunAsWork() - { - public JSONObject doWork() throws Exception - { - RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); - - // wrap to make the request in a transaction - RetryingTransactionCallback lookup = new RetryingTransactionCallback() - { - public JSONObject execute() throws Throwable - { - String name = ""; - if (! jo.isNull("name")) - { - name = jo.getString("name"); - } - - NodeRef parentNodeRef = null; - if (! jo.isNull("parentNodeRef")) - { - parentNodeRef = new NodeRef(jo.getString("parentNodeRef")); - } - - - String typeQName = ""; - if (! jo.isNull("typeQName")) - { - typeQName = jo.getString("typeQName"); - } - - String displayPath = ""; - Path path = null; - String firstName = ""; - String lastName = ""; - - if (personService.personExists(postUserId)) - { - // lookup firstname, lastname - NodeRef personRef = personService.getPerson(postUserId); - - firstName = (String)nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME); - lastName = (String)nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME); - } - - if ((nodeRef != null) && (nodeService.exists(nodeRef))) - { - if (name.length() == 0) - { - // lookup node name - name = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); - } - - if (typeQName.length() == 0) - { - // lookup type - typeQName = nodeService.getType(nodeRef).toPrefixString(); // TODO: missing the prefix ? - } - - if (parentNodeRef == null) - { - // lookup parent node - parentNodeRef = nodeService.getPrimaryParent(nodeRef).getParentRef(); - } - } - - if ((parentNodeRef != null) && (nodeService.exists(parentNodeRef))) - { - // parent node exists, lookup parent node path - path = nodeService.getPath(parentNodeRef); - } - - if (path != null) - { - // lookup display path - displayPath = path.toDisplayPath(nodeService, permissionService); - - // note: for now, also tack on the node name - displayPath += "/" + name; - } - - // merge with existing activity data - jo.put("name", name); - jo.put("nodeRef", nodeRef.toString()); - jo.put("typeQName", typeQName); - jo.put("parentNodeRef", (parentNodeRef != null ? parentNodeRef.toString() : null)); - jo.put("displayPath", displayPath); - jo.put("firstName", firstName); - jo.put("lastName", lastName); - - return jo; - } - }; - - // execute in txn - return txnHelper.doInTransaction(lookup, true); - } - }, AuthenticationUtil.getSystemUserName()); - } +/* + * Copyright (C) 2005-2009 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.activities.post.lookup; + +import java.sql.SQLException; +import java.util.Date; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.activities.post.ActivityPostDAO; +import org.alfresco.repo.activities.post.ActivityPostDaoService; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.Pair; +import org.alfresco.util.PropertyCheck; +import org.alfresco.util.VmShutdownListener; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.quartz.JobExecutionException; + +/** + * The post lookup component is responsible for updating posts that require a secondary lookup (to get additional activity data) + */ +public class PostLookup +{ + private static Log logger = LogFactory.getLog(PostLookup.class); + + private static VmShutdownListener vmShutdownListener = new VmShutdownListener(PostLookup.class.getName()); + + private ActivityPostDaoService postDaoService; + private NodeService nodeService; + private PermissionService permissionService; + private TransactionService transactionService; + private PersonService personService; + private TenantService tenantService; + + public void setPostDaoService(ActivityPostDaoService postDaoService) + { + this.postDaoService = postDaoService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + /** + * Perform basic checks to ensure that the necessary dependencies were injected. + */ + private void checkProperties() + { + PropertyCheck.mandatory(this, "postDaoService", postDaoService); + PropertyCheck.mandatory(this, "nodeService", nodeService); + PropertyCheck.mandatory(this, "permissionService", permissionService); + PropertyCheck.mandatory(this, "transactionService", transactionService); + PropertyCheck.mandatory(this, "personService", personService); + PropertyCheck.mandatory(this, "tenantService", tenantService); + } + + public void execute() throws JobExecutionException + { + checkProperties(); + try + { + ActivityPostDAO params = new ActivityPostDAO(); + params.setStatus(ActivityPostDAO.STATUS.PENDING.toString()); + + List activityPosts = postDaoService.selectPosts(params); + + if (activityPosts.size() > 0) + { + logger.info("Update: " + activityPosts.size() + " activity posts"); + } + + for (final ActivityPostDAO activityPost : activityPosts) + { + try + { + postDaoService.startTransaction(); + + final JSONObject jo = new JSONObject(new JSONTokener(activityPost.getActivityData())); + final String postUserId = activityPost.getUserId(); + + // MT share + String tenantDomain = tenantService.getUserDomain(postUserId); + + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + if (! jo.isNull("nodeRef")) + { + String nodeRefStr = jo.getString("nodeRef"); + NodeRef nodeRef = new NodeRef(nodeRefStr); + + // lookup additional node data + JSONObject activityData = lookupNode(nodeRef, postUserId, jo); + + activityPost.setActivityData(activityData.toString()); + } + else + { + // lookup additional person data + Pair firstLastName = lookupPerson(postUserId); + if (firstLastName != null) + { + jo.put("firstName", firstLastName.getFirst()); + jo.put("lastName", firstLastName.getSecond()); + + activityPost.setActivityData(jo.toString()); + } + } + + activityPost.setLastModified(new Date()); + + postDaoService.updatePost(activityPost.getId(), activityPost.getSiteNetwork(), activityPost.getActivityData(), ActivityPostDAO.STATUS.POSTED); + if (logger.isDebugEnabled()) + { + activityPost.setStatus(ActivityPostDAO.STATUS.POSTED.toString()); // for debug output + logger.debug("Updated: " + activityPost); + } + + return null; + } + }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + + postDaoService.commitTransaction(); + } + catch (JSONException e) + { + // log error, but consume exception (skip this post) + logger.error("Skipping activity post " + activityPost.getId() + ": " + e); + postDaoService.updatePostStatus(activityPost.getId(), ActivityPostDAO.STATUS.ERROR); + + postDaoService.commitTransaction(); + } + catch (SQLException e) + { + logger.error("Exception during update of post", e); + throw new JobExecutionException(e); + } + finally + { + postDaoService.endTransaction(); + } + } + } + catch (SQLException e) + { + logger.error("Exception during select of posts", e); + throw new JobExecutionException(e); + } + catch (Throwable e) + { + // If the VM is shutting down, then ignore + if (vmShutdownListener.isVmShuttingDown()) + { + // Ignore + } + else + { + logger.error("Exception during update of posts", e); + } + } + } + + private Pair lookupPerson(final String postUserId) throws JSONException + { + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + + // wrap to make the request in a transaction + RetryingTransactionCallback> lookup = new RetryingTransactionCallback>() + { + public Pair execute() throws Throwable + { + String firstName = ""; + String lastName = ""; + + if (personService.personExists(postUserId)) + { + NodeRef personRef = personService.getPerson(postUserId); + + firstName = (String)nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME); + lastName = (String)nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME); + + return new Pair(firstName, lastName); + } + + return null; + } + }; + + // execute in txn + return txnHelper.doInTransaction(lookup, true); + } + + private JSONObject lookupNode(final NodeRef nodeRef, final String postUserId, final JSONObject jo) throws JSONException + { + return AuthenticationUtil.runAs(new RunAsWork() + { + public JSONObject doWork() throws Exception + { + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + + // wrap to make the request in a transaction + RetryingTransactionCallback lookup = new RetryingTransactionCallback() + { + public JSONObject execute() throws Throwable + { + String name = ""; + if (! jo.isNull("name")) + { + name = jo.getString("name"); + } + + NodeRef parentNodeRef = null; + if (! jo.isNull("parentNodeRef")) + { + parentNodeRef = new NodeRef(jo.getString("parentNodeRef")); + } + + + String typeQName = ""; + if (! jo.isNull("typeQName")) + { + typeQName = jo.getString("typeQName"); + } + + String displayPath = ""; + Path path = null; + String firstName = ""; + String lastName = ""; + + if (personService.personExists(postUserId)) + { + // lookup firstname, lastname + NodeRef personRef = personService.getPerson(postUserId); + + firstName = (String)nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME); + lastName = (String)nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME); + } + + if ((nodeRef != null) && (nodeService.exists(nodeRef))) + { + if (name.length() == 0) + { + // lookup node name + name = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + } + + if (typeQName.length() == 0) + { + // lookup type + typeQName = nodeService.getType(nodeRef).toPrefixString(); // TODO: missing the prefix ? + } + + if (parentNodeRef == null) + { + // lookup parent node + parentNodeRef = nodeService.getPrimaryParent(nodeRef).getParentRef(); + } + } + + if ((parentNodeRef != null) && (nodeService.exists(parentNodeRef))) + { + // parent node exists, lookup parent node path + path = nodeService.getPath(parentNodeRef); + } + + if (path != null) + { + // lookup display path + displayPath = path.toDisplayPath(nodeService, permissionService); + + // note: for now, also tack on the node name + displayPath += "/" + name; + } + + // merge with existing activity data + jo.put("name", name); + jo.put("nodeRef", nodeRef.toString()); + jo.put("typeQName", typeQName); + jo.put("parentNodeRef", (parentNodeRef != null ? parentNodeRef.toString() : null)); + jo.put("displayPath", displayPath); + jo.put("firstName", firstName); + jo.put("lastName", lastName); + + return jo; + } + }; + + // execute in txn + return txnHelper.doInTransaction(lookup, true); + } + }, AuthenticationUtil.getSystemUserName()); + } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/avm/MultiTAVMService.java b/source/java/org/alfresco/repo/avm/MultiTAVMService.java new file mode 100644 index 0000000000..dea84e8a0b --- /dev/null +++ b/source/java/org/alfresco/repo/avm/MultiTAVMService.java @@ -0,0 +1,1032 @@ +/* + * Copyright (C) 2005-2009 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.avm; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.alfresco.repo.domain.PropertyValue; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.avm.AVMBadArgumentException; +import org.alfresco.service.cmr.avm.AVMNodeDescriptor; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avm.AVMStoreDescriptor; +import org.alfresco.service.cmr.avm.LayeringDescriptor; +import org.alfresco.service.cmr.avm.VersionDescriptor; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; + +/** + * An MT-aware wrapper of AVMService + * + * @author janv + */ +public class MultiTAVMService implements AVMService +{ + private AVMService fService; + private TenantService tenantService; + + Boolean enabled = null; + + public void setAvmService(AVMService fService) + { + this.fService = fService; + } + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#addAspect(java.lang.String, org.alfresco.service.namespace.QName) + */ + public void addAspect(String path, QName aspectName) + { + fService.addAspect(getTenantPath(path), aspectName); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#copy(int, java.lang.String, java.lang.String, java.lang.String) + */ + public void copy(int srcVersion, String srcPath, String dstPath, String name) + { + fService.copy(srcVersion, getTenantPath(srcPath), getTenantPath(dstPath), name); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#createBranch(int, java.lang.String, java.lang.String, java.lang.String) + */ + public void createBranch(int version, String srcPath, String dstPath, String name) + { + fService.createBranch(version, getTenantPath(srcPath), getTenantPath(dstPath), name); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#createDirectory(java.lang.String, java.lang.String) + */ + public void createDirectory(String path, String name) + { + fService.createDirectory(getTenantPath(path), name); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#createFile(java.lang.String, java.lang.String) + */ + public OutputStream createFile(String path, String name) + { + return fService.createFile(getTenantPath(path), name); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#createFile(java.lang.String, java.lang.String, java.io.InputStream) + */ + public void createFile(String path, String name, InputStream in) + { + fService.createFile(getTenantPath(path), name, in); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#createLayeredDirectory(java.lang.String, java.lang.String, java.lang.String) + */ + public void createLayeredDirectory(String targetPath, String parentPath, String name) + { + fService.createLayeredDirectory(getTenantPath(targetPath), getTenantPath(parentPath), name); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#createLayeredFile(java.lang.String, java.lang.String, java.lang.String) + */ + public void createLayeredFile(String targetPath, String parentPath, String name) + { + fService.createLayeredFile(getTenantPath(targetPath), getTenantPath(parentPath), name); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#createSnapshot(java.lang.String, java.lang.String, java.lang.String) + */ + public Map createSnapshot(String storeName, String tag, String description) + { + // TODO + return fService.createSnapshot(getTenantStoreName(storeName), tag, description); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#createStore(java.lang.String) + */ + public void createStore(String storeName) + { + fService.createStore(getTenantStoreName(storeName)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#deleteNodeProperties(java.lang.String) + */ + public void deleteNodeProperties(String path) + { + fService.deleteNodeProperties(getTenantPath(path)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#deleteNodeProperty(java.lang.String, org.alfresco.service.namespace.QName) + */ + public void deleteNodeProperty(String path, QName name) + { + fService.deleteNodeProperty(getTenantPath(path), name); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#deleteStoreProperty(java.lang.String, org.alfresco.service.namespace.QName) + */ + public void deleteStoreProperty(String storeName, QName name) + { + fService.deleteStoreProperty(getTenantStoreName(storeName), name); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#forceCopy(java.lang.String) + */ + public AVMNodeDescriptor forceCopy(String path) + { + return getBaseNode(fService.forceCopy(getTenantPath(path))); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getAPath(org.alfresco.service.cmr.avm.AVMNodeDescriptor) + */ + public Pair getAPath(AVMNodeDescriptor desc) + { + return getBaseVPath(fService.getAPath(getTenantNode(desc))); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getPathsInStoreVersion(org.alfresco.service.cmr.avm.AVMNodeDescriptor, java.lang.String, int) + */ + public List getPathsInStoreVersion(AVMNodeDescriptor desc, String storeName, int version) + { + return getBasePaths(fService.getPathsInStoreVersion(getTenantNode(desc), getTenantStoreName(storeName), version)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getAspects(int, java.lang.String) + */ + public Set getAspects(int version, String path) + { + return fService.getAspects(version, getTenantPath(path)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getAspects(org.alfresco.service.cmr.avm.AVMNodeDescriptor) + */ + public Set getAspects(AVMNodeDescriptor desc) + { + return fService.getAspects(getTenantNode(desc)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getCommonAncestor(org.alfresco.service.cmr.avm.AVMNodeDescriptor, org.alfresco.service.cmr.avm.AVMNodeDescriptor) + */ + public AVMNodeDescriptor getCommonAncestor(AVMNodeDescriptor left, AVMNodeDescriptor right) + { + return getBaseNode(fService.getCommonAncestor(getTenantNode(left), getTenantNode(right))); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getContentDataForRead(int, java.lang.String) + */ + public ContentData getContentDataForRead(int version, String path) + { + return fService.getContentDataForRead(version, getTenantPath(path)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getContentDataForRead(org.alfresco.service.cmr.avm.AVMNodeDescriptor) + */ + public ContentData getContentDataForRead(AVMNodeDescriptor desc) + { + return fService.getContentDataForRead(getTenantNode(desc)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getContentDataForWrite(java.lang.String) + */ + public ContentData getContentDataForWrite(String path) + { + return fService.getContentDataForWrite(getTenantPath(path)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getContentReader(int, java.lang.String) + */ + public ContentReader getContentReader(int version, String path) + { + return fService.getContentReader(version, getTenantPath(path)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getContentWriter(java.lang.String) + */ + public ContentWriter getContentWriter(String path) + { + return fService.getContentWriter(getTenantPath(path)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getDeleted(int, java.lang.String) + */ + public List getDeleted(int version, String path) + { + return fService.getDeleted(version, getTenantPath(path)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getDirectoryListing(int, java.lang.String) + */ + public SortedMap getDirectoryListing(int version, String path) + { + return getBaseNodes(fService.getDirectoryListing(version, getTenantPath(path))); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getDirectoryListing(int, java.lang.String, boolean) + */ + public SortedMap getDirectoryListing(int version, String path, boolean includeDeleted) + { + return getBaseNodes(fService.getDirectoryListing(version, getTenantPath(path), includeDeleted)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getDirectoryListing(org.alfresco.service.cmr.avm.AVMNodeDescriptor) + */ + public SortedMap getDirectoryListing(AVMNodeDescriptor dir) + { + return getBaseNodes(fService.getDirectoryListing(getTenantNode(dir))); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getDirectoryListing(org.alfresco.service.cmr.avm.AVMNodeDescriptor, boolean) + */ + public SortedMap getDirectoryListing(AVMNodeDescriptor dir, boolean includeDeleted) + { + return getBaseNodes(fService.getDirectoryListing(getTenantNode(dir), includeDeleted)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getDirectoryListingArray(int, java.lang.String, boolean) + */ + public AVMNodeDescriptor[] getDirectoryListingArray(int version, String path, boolean includeDeleted) + { + return getBaseNodes(fService.getDirectoryListingArray(version, getTenantPath(path), includeDeleted)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getDirectoryListingArray(org.alfresco.service.cmr.avm.AVMNodeDescriptor, boolean) + */ + public AVMNodeDescriptor[] getDirectoryListingArray(AVMNodeDescriptor dir, boolean includeDeleted) + { + return getBaseNodes(fService.getDirectoryListingArray(getTenantNode(dir), includeDeleted)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getDirectoryListingDirect(int, java.lang.String) + */ + public SortedMap getDirectoryListingDirect(int version, String path) + { + return getBaseNodes(fService.getDirectoryListingDirect(version, getTenantPath(path))); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getDirectoryListingDirect(int, java.lang.String, boolean) + */ + public SortedMap getDirectoryListingDirect(int version, String path, boolean includeDeleted) + { + return getBaseNodes(fService.getDirectoryListingDirect(version, getTenantPath(path), includeDeleted)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getDirectoryListingDirect(org.alfresco.service.cmr.avm.AVMNodeDescriptor, boolean) + */ + public SortedMap getDirectoryListingDirect(AVMNodeDescriptor dir, boolean includeDeleted) + { + return getBaseNodes(fService.getDirectoryListingDirect(getTenantNode(dir), includeDeleted)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getFileInputStream(int, java.lang.String) + */ + public InputStream getFileInputStream(int version, String path) + { + return fService.getFileInputStream(version, getTenantPath(path)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getFileInputStream(org.alfresco.service.cmr.avm.AVMNodeDescriptor) + */ + public InputStream getFileInputStream(AVMNodeDescriptor desc) + { + return fService.getFileInputStream(getTenantNode(desc)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getFileOutputStream(java.lang.String) + */ + public OutputStream getFileOutputStream(String path) + { + return fService.getFileOutputStream(getTenantPath(path)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getHeadPaths(org.alfresco.service.cmr.avm.AVMNodeDescriptor) + */ + public List> getHeadPaths(AVMNodeDescriptor desc) + { + return fService.getHeadPaths(getTenantNode(desc)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getHistory(org.alfresco.service.cmr.avm.AVMNodeDescriptor, int) + */ + public List getHistory(AVMNodeDescriptor desc, int count) + { + return getBaseNodes(fService.getHistory(getTenantNode(desc), count)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getIndirectionPath(int, java.lang.String) + */ + public String getIndirectionPath(int version, String path) + { + return getBasePath(fService.getIndirectionPath(version, getTenantPath(path))); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getLatestSnapshotID(java.lang.String) + */ + public int getLatestSnapshotID(String storeName) + { + return fService.getLatestSnapshotID(getTenantStoreName(storeName)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getLayeringInfo(int, java.lang.String) + */ + public LayeringDescriptor getLayeringInfo(int version, String path) + { + return fService.getLayeringInfo(version, getTenantPath(path)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getNextVersionID(java.lang.String) + */ + public int getNextVersionID(String storeName) + { + return fService.getNextVersionID(storeName); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getNodeProperties(int, java.lang.String) + */ + public Map getNodeProperties(int version, String path) + { + return fService.getNodeProperties(version, getTenantPath(path)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getNodeProperties(org.alfresco.service.cmr.avm.AVMNodeDescriptor) + */ + public Map getNodeProperties(AVMNodeDescriptor desc) + { + // TODO - review + return fService.getNodeProperties(getTenantNode(desc)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getNodeProperty(int, java.lang.String, org.alfresco.service.namespace.QName) + */ + public PropertyValue getNodeProperty(int version, String path, QName name) + { + // TODO - review + return fService.getNodeProperty(version, getTenantPath(path), name); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getPaths(org.alfresco.service.cmr.avm.AVMNodeDescriptor) + */ + public List> getPaths(AVMNodeDescriptor desc) + { + return getBaseVPaths(fService.getPaths(getTenantNode(desc))); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getPathsInStoreHead(org.alfresco.service.cmr.avm.AVMNodeDescriptor, java.lang.String) + */ + public List> getPathsInStoreHead(AVMNodeDescriptor desc, String storeName) + { + return getBaseVPaths(fService.getPathsInStoreHead(getTenantNode(desc), getTenantStoreName(storeName))); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getStore(java.lang.String) + */ + public AVMStoreDescriptor getStore(String storeName) + { + return getBaseStore(fService.getStore(getTenantStoreName(storeName))); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getStoreProperties(java.lang.String) + */ + public Map getStoreProperties(String storeName) + { + return fService.getStoreProperties(getTenantStoreName(storeName)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getStoreProperty(java.lang.String, org.alfresco.service.namespace.QName) + */ + public PropertyValue getStoreProperty(String storeName, QName name) + { + return fService.getStoreProperty(getTenantStoreName(storeName), name); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getStoreRoot(int, java.lang.String) + */ + public AVMNodeDescriptor getStoreRoot(int version, String storeName) + { + return getBaseNode(fService.getStoreRoot(version, getTenantStoreName(storeName))); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getStoreVersions(java.lang.String) + */ + public List getStoreVersions(String storeName) + { + // TODO - review + return fService.getStoreVersions(getTenantStoreName(storeName)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getStoreVersions(java.lang.String, java.util.Date, java.util.Date) + */ + public List getStoreVersions(String storeName, Date from, Date to) + { + // TODO - review + return fService.getStoreVersions(getTenantStoreName(storeName), from, to); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getStores() + */ + public List getStores() + { + List allAvmStores = fService.getStores(); + if (isTenantServiceEnabled()) + { + List convertedValues = new ArrayList(); + for (AVMStoreDescriptor store : allAvmStores) + { + try + { + // MT: return tenant stores only (although for super System return all stores) + if (!AuthenticationUtil.isRunAsUserTheSystemUser()) + { + tenantService.checkDomain(store.getName()); + store = getBaseStore(store); + } + + convertedValues.add(store); + } + catch (RuntimeException re) + { + // deliberately ignore - stores in different domain will not be listed + } + } + + return convertedValues; + } + else + { + return allAvmStores; + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#getSystemStore() + */ + public AVMStoreDescriptor getSystemStore() + { + return getBaseStore(fService.getSystemStore()); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#hasAspect(int, java.lang.String, org.alfresco.service.namespace.QName) + */ + public boolean hasAspect(int version, String path, QName aspectName) + { + return fService.hasAspect(version, getTenantPath(path), aspectName); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#link(java.lang.String, java.lang.String, org.alfresco.service.cmr.avm.AVMNodeDescriptor) + */ + public void link(String parentPath, String name, AVMNodeDescriptor toLink) + { + fService.link(getTenantPath(parentPath), name, getTenantNode(toLink)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#lookup(int, java.lang.String) + */ + public AVMNodeDescriptor lookup(int version, String path) + { + return getBaseNode(fService.lookup(version, getTenantPath(path))); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#lookup(int, java.lang.String, boolean) + */ + public AVMNodeDescriptor lookup(int version, String path, boolean includeDeleted) + { + return getBaseNode(fService.lookup(version, getTenantPath(path), includeDeleted)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#lookup(org.alfresco.service.cmr.avm.AVMNodeDescriptor, java.lang.String) + */ + public AVMNodeDescriptor lookup(AVMNodeDescriptor dir, String name) + { + return getBaseNode(fService.lookup(getTenantNode(dir), name)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#lookup(org.alfresco.service.cmr.avm.AVMNodeDescriptor, java.lang.String, boolean) + */ + public AVMNodeDescriptor lookup(AVMNodeDescriptor dir, String name, boolean includeDeleted) + { + return getBaseNode(fService.lookup(getTenantNode(dir), name, includeDeleted)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#makePrimary(java.lang.String) + */ + public void makePrimary(String path) + { + fService.makePrimary(getTenantPath(path)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#makeTransparent(java.lang.String, java.lang.String) + */ + public void makeTransparent(String dirPath, String name) + { + fService.makeTransparent(getTenantPath(dirPath), name); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#purgeStore(java.lang.String) + */ + public void purgeStore(String storeName) + { + fService.purgeStore(getTenantStoreName(storeName)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#purgeVersion(int, java.lang.String) + */ + public void purgeVersion(int version, String storeName) + { + fService.purgeVersion(version, getTenantStoreName(storeName)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#queryStorePropertyKey(java.lang.String, org.alfresco.service.namespace.QName) + */ + public Map queryStorePropertyKey(String storeName, QName keyPattern) + { + return fService.queryStorePropertyKey(getTenantStoreName(storeName), keyPattern); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#queryStoresPropertyKeys(org.alfresco.service.namespace.QName) + */ + public Map> queryStoresPropertyKeys(QName keyPattern) + { + // TODO - review + return fService.queryStoresPropertyKeys(keyPattern); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#removeAspect(java.lang.String, org.alfresco.service.namespace.QName) + */ + public void removeAspect(String path, QName aspectName) + { + fService.removeAspect(getTenantPath(path), aspectName); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#removeNode(java.lang.String, java.lang.String) + */ + public void removeNode(String parent, String name) + { + fService.removeNode(getTenantPath(parent), name); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#removeNode(java.lang.String) + */ + public void removeNode(String path) + { + fService.removeNode(getTenantPath(path)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#rename(java.lang.String, java.lang.String, java.lang.String, java.lang.String) + */ + public void rename(String srcParent, String srcName, String dstParent, String dstName) + { + fService.rename(getTenantPath(srcParent), srcName, getTenantPath(dstParent), dstName); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#renameStore(java.lang.String, java.lang.String) + */ + public void renameStore(String sourceStoreName, String destStoreName) + { + fService.renameStore(getTenantStoreName(sourceStoreName), getTenantStoreName(destStoreName)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#retargetLayeredDirectory(java.lang.String, java.lang.String) + */ + public void retargetLayeredDirectory(String path, String target) + { + fService.retargetLayeredDirectory(getTenantPath(path), target); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#revert(java.lang.String, org.alfresco.service.cmr.avm.AVMNodeDescriptor) + */ + public void revert(String path, AVMNodeDescriptor toRevertTo) + { + fService.revert(getTenantPath(path), getTenantNode(toRevertTo)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#setContentData(java.lang.String, org.alfresco.service.cmr.repository.ContentData) + */ + public void setContentData(String path, ContentData data) + { + fService.setContentData(getTenantPath(path), data); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#setEncoding(java.lang.String, java.lang.String) + */ + public void setEncoding(String path, String encoding) + { + fService.setEncoding(getTenantPath(path), encoding); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#setGuid(java.lang.String, java.lang.String) + */ + public void setGuid(String path, String guid) + { + fService.setGuid(getTenantPath(path), guid); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#setMetaDataFrom(java.lang.String, org.alfresco.service.cmr.avm.AVMNodeDescriptor) + */ + public void setMetaDataFrom(String path, AVMNodeDescriptor from) + { + fService.setMetaDataFrom(getTenantPath(path), getTenantNode(from)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#setMimeType(java.lang.String, java.lang.String) + */ + public void setMimeType(String path, String mimeType) + { + fService.setMimeType(getTenantPath(path), mimeType); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#setNodeProperties(java.lang.String, java.util.Map) + */ + public void setNodeProperties(String path, Map properties) + { + fService.setNodeProperties(getTenantPath(path), properties); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#setNodeProperty(java.lang.String, org.alfresco.service.namespace.QName, org.alfresco.repo.domain.PropertyValue) + */ + public void setNodeProperty(String path, QName name, PropertyValue value) + { + fService.setNodeProperty(getTenantPath(path), name, value); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#setOpacity(java.lang.String, boolean) + */ + public void setOpacity(String path, boolean opacity) + { + fService.setOpacity(getTenantPath(path), opacity); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#setStoreProperties(java.lang.String, java.util.Map) + */ + public void setStoreProperties(String storeName, Map props) + { + fService.setStoreProperties(getTenantStoreName(storeName), props); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#setStoreProperty(java.lang.String, org.alfresco.service.namespace.QName, org.alfresco.repo.domain.PropertyValue) + */ + public void setStoreProperty(String storeName, QName name, PropertyValue value) + { + fService.setStoreProperty(getTenantStoreName(storeName), name, value); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#uncover(java.lang.String, java.lang.String) + */ + public void uncover(String dirPath, String name) + { + fService.uncover(getTenantPath(dirPath), name); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#createDirectory(java.lang.String, java.lang.String, java.util.List, java.util.Map) + */ + public void createDirectory(String path, String name, List aspects, Map properties) + { + fService.createDirectory(getTenantPath(path), name, aspects, properties); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#createFile(java.lang.String, java.lang.String, java.io.InputStream, java.util.List, java.util.Map) + */ + public void createFile(String path, String name, InputStream in, List aspects, Map properties) + { + fService.createFile(getTenantPath(path), name, in, aspects, properties); + } + + + private String getTenantStoreName(String avmStoreName) + { + if ((avmStoreName == null) || (! isTenantServiceEnabled())) + { + return avmStoreName; + } + + return tenantService.getName(avmStoreName); + } + + private String getBaseStoreName(String avmStoreName) + { + if ((avmStoreName == null) || (! isTenantServiceEnabled())) + { + return avmStoreName; + } + + return tenantService.getBaseName(avmStoreName); + } + + private String getTenantPath(String avmPath) + { + if ((avmPath == null) || (! isTenantServiceEnabled())) + { + return avmPath; + } + + String[] storePath = splitPath(avmPath); + return tenantService.getName(storePath[0]) + ':' + storePath[1]; + } + + private String getBasePath(String avmPath) + { + if ((avmPath == null) || (! isTenantServiceEnabled())) + { + return avmPath; + } + + String[] storePath = splitPath(avmPath); + return tenantService.getBaseName(storePath[0]) + ':' + storePath[1]; + } + + private Pair getBaseVPath(Pair p) + { + if ((p == null) || (! isTenantServiceEnabled())) + { + return p; + } + + return new Pair(p.getFirst(), getBasePath(p.getSecond())); + } + + private List> getBaseVPaths(List> paths) + { + if ((paths == null) || (! isTenantServiceEnabled())) + { + return paths; + } + + List> convertedPaths = new ArrayList>(paths.size()); + for (Pair path : paths) + { + convertedPaths.add(getBaseVPath(path)); + } + return convertedPaths; + } + + private List getBasePaths(List paths) + { + if ((paths == null) || (! isTenantServiceEnabled())) + { + return paths; + } + + List convertedPaths = new ArrayList(paths.size()); + for (String path : paths) + { + convertedPaths.add(getBasePath(path)); + } + return convertedPaths; + } + + private String[] splitPath(String path) + { + String[] storePath = path.split(":"); + if (storePath.length != 2) + { + throw new AVMBadArgumentException("Invalid Path: " + path); + } + return storePath; + } + + private AVMNodeDescriptor getTenantNode(AVMNodeDescriptor node) + { + if ((node == null) || (! isTenantServiceEnabled())) + { + return node; + } + + return new AVMNodeDescriptor( + getTenantPath(node.getPath()), + node.getName(), + node.getType(), + node.getCreator(), + node.getOwner(), + node.getLastModifier(), + node.getCreateDate(), + node.getModDate(), + node.getAccessDate(), + node.getId(), + node.getGuid(), + node.getVersionID(), + node.getIndirection(), + node.getIndirectionVersion(), + node.isPrimary(), + node.getLayerID(), + node.getOpacity(), + node.getLength(), + node.getDeletedType()); + } + + private AVMNodeDescriptor getBaseNode(AVMNodeDescriptor node) + { + if ((node == null) || (! isTenantServiceEnabled())) + { + return node; + } + + return new AVMNodeDescriptor( + getBasePath(node.getPath()), + node.getName(), + node.getType(), + node.getCreator(), + node.getOwner(), + node.getLastModifier(), + node.getCreateDate(), + node.getModDate(), + node.getAccessDate(), + node.getId(), + node.getGuid(), + node.getVersionID(), + node.getIndirection(), + node.getIndirectionVersion(), + node.isPrimary(), + node.getLayerID(), + node.getOpacity(), + node.getLength(), + node.getDeletedType()); + } + + private AVMStoreDescriptor getBaseStore(AVMStoreDescriptor store) + { + if ((store == null) || (! isTenantServiceEnabled())) + { + return store; + } + + return new AVMStoreDescriptor( + getBaseStoreName(store.getName()), + store.getCreator(), + store.getCreateDate()); + } + + private SortedMap getBaseNodes(SortedMap nodes) + { + if ((nodes == null) || (! isTenantServiceEnabled())) + { + return nodes; + } + + SortedMap convertedNodes = new TreeMap(String.CASE_INSENSITIVE_ORDER); + for (Map.Entry entry : nodes.entrySet()) + { + convertedNodes.put(entry.getKey(), getBaseNode(entry.getValue())); + } + return convertedNodes; + } + + private AVMNodeDescriptor[] getBaseNodes(AVMNodeDescriptor[] nodes) + { + if ((nodes == null) || (! isTenantServiceEnabled())) + { + return nodes; + } + + AVMNodeDescriptor[] convertedNodes = new AVMNodeDescriptor[nodes.length]; + for (int i = 0; i < nodes.length; i++) + { + convertedNodes[i] = getBaseNode(nodes[i]); + } + return convertedNodes; + } + + private List getBaseNodes(List nodes) + { + if ((nodes == null) || (! isTenantServiceEnabled())) + { + return nodes; + } + + List convertedNodes = new ArrayList(nodes.size()); + for (AVMNodeDescriptor node : nodes) + { + convertedNodes.add(getBaseNode(node)); + } + return convertedNodes; + } + + private boolean isTenantServiceEnabled() + { + if (enabled == null) + { + enabled = tenantService.isEnabled(); + } + + return enabled; + } + +} diff --git a/source/java/org/alfresco/repo/security/authentication/BasicUserNameGenerator.java b/source/java/org/alfresco/repo/security/authentication/BasicUserNameGenerator.java index faa2fe1f73..6882318c75 100644 --- a/source/java/org/alfresco/repo/security/authentication/BasicUserNameGenerator.java +++ b/source/java/org/alfresco/repo/security/authentication/BasicUserNameGenerator.java @@ -1,58 +1,71 @@ -/* - * 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 received 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.security.authentication; - -import org.apache.commons.lang.RandomStringUtils; - -/** - * Generates a simple numeric user name of specified length - * - * @author glen johnson at Alfresco dot com - */ -public class BasicUserNameGenerator implements UserNameGenerator -{ - // user name length property - private int userNameLength; - - /** - * Set the user name length - * - * @param userNameLength the user name length - */ - public void setUserNameLength(int userNameLength) - { - this.userNameLength = userNameLength; - } - - /** - * Returns a generated user name - * - * @return the generated user name - */ - public String generateUserName() - { - return RandomStringUtils.randomNumeric(userNameLength); - } -} +/* + * Copyright (C) 2005-2009 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 received 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.security.authentication; + +import org.alfresco.repo.tenant.TenantService; +import org.apache.commons.lang.RandomStringUtils; + +/** + * Generates a simple numeric user name of specified length + * + * @author glen johnson at Alfresco dot com + */ +public class BasicUserNameGenerator implements UserNameGenerator +{ + // user name length property + private int userNameLength; + + private TenantService tenantService; + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + /** + * Set the user name length + * + * @param userNameLength the user name length + */ + public void setUserNameLength(int userNameLength) + { + this.userNameLength = userNameLength; + } + + /** + * Returns a generated user name + * + * @return the generated user name + */ + public String generateUserName() + { + String userName = RandomStringUtils.randomNumeric(userNameLength); + if (tenantService.isEnabled()) + { + userName = tenantService.getDomainUser(userName, tenantService.getCurrentUserDomain()); + } + return userName; + } +} diff --git a/source/java/org/alfresco/repo/security/person/AbstractHomeFolderProvider.java b/source/java/org/alfresco/repo/security/person/AbstractHomeFolderProvider.java index e2cc9f55f0..3d4281e232 100644 --- a/source/java/org/alfresco/repo/security/person/AbstractHomeFolderProvider.java +++ b/source/java/org/alfresco/repo/security/person/AbstractHomeFolderProvider.java @@ -1,447 +1,470 @@ -/* - * 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.security.person; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; -import org.alfresco.service.cmr.security.PermissionService; -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.beans.factory.InitializingBean; - -/** - * Common support for creating home folders This is hooked into node creation events from Person type objects via the homeFolderManager. Provider must all be wired up to the - * homeFolderManager. - * - * @author Andy Hind - */ -public abstract class AbstractHomeFolderProvider implements HomeFolderProvider, BeanNameAware, InitializingBean -{ - /** - * The provider name - */ - private String name; - - /** - * The home folder manager - */ - private HomeFolderManager homeFolderManager; - - /** - * The store ref in which to conduct searches - */ - private StoreRef storeRef; - - /** - * Service registry to get hold of public services (so taht actions are audited) - */ - private ServiceRegistry serviceRegistry; - - /** - * The path to a folder - */ - private String path; - - /** - * Cache the result of the path look up. - */ - private NodeRef pathNodeRef; - - /** - * The owner to set on creation of a home folder (if unset this will be the uid). - */ - private String ownerOnCreate; - - /** - * Set if permissions are inherited when nodes are created. - */ - private boolean inheritsPermissionsOnCreate = false; - - /** - * A set of permissions to set for the owner when a home folder is created - */ - private Set ownerPermissionsToSetOnCreate; - - /** - * General permissions to set on the node Map<(String)uid, Set<(String)permission>>. - */ - private Map> permissionsToSetOnCreate; - - /** - * Permissions to set for the user - on create and reference. - */ - private Set userPermissions; - - /** - * Clear existing permissions on new home folders (useful of created from a template. - */ - private boolean clearExistingPermissionsOnCreate = false; - - public AbstractHomeFolderProvider() - { - super(); - } - - /** - * Register with the homeFolderManagewr - */ - public void afterPropertiesSet() throws Exception - { - homeFolderManager.addProvider(this); - } - - // === // - // IOC // - // === // - - /** - * Get the home folder manager. - */ - protected HomeFolderManager getHomeFolderManager() - { - return homeFolderManager; - } - - /** - * Set the home folder manager. - * - * @param homeFolderManager - */ - public void setHomeFolderManager(HomeFolderManager homeFolderManager) - { - this.homeFolderManager = homeFolderManager; - } - - /** - * Get the provider name - */ - public String getName() - { - return name; - } - - /** - * The provider name is taken from the bean name - */ - public void setBeanName(String name) - { - this.name = name; - } - - /** - * Get the path - * - * @return - */ - protected String getPath() - { - return path; - } - - /** - * Set the path - * - * @param path - */ - public void setPath(String path) - { - this.path = path; - } - - /** - * Get the store ref - * - * @return - */ - protected StoreRef getStoreRef() - { - return storeRef; - } - - /** - * Set the store ref - * - * @param storeRef - */ - public void setStoreRef(StoreRef storeRef) - { - this.storeRef = storeRef; - } - - /** - * Set the store from the string url. - * - * @param storeUrl - */ - public void setStoreUrl(String storeUrl) - { - this.storeRef = new StoreRef(storeUrl); - } - - /** - * Get the service registry. - * - * @return - */ - protected ServiceRegistry getServiceRegistry() - { - return serviceRegistry; - } - - /** - * Set the service registry. - * - * @param serviceRegistry - */ - public void setServiceRegistry(ServiceRegistry serviceRegistry) - { - this.serviceRegistry = serviceRegistry; - } - - /** - * Inherit permissions when home folder are created? - * - * @param inheritsPermissionsOnCreate - */ - public void setInheritsPermissionsOnCreate(boolean inheritsPermissionsOnCreate) - { - this.inheritsPermissionsOnCreate = inheritsPermissionsOnCreate; - } - - /** - * The owner to set on create. - * - * @param ownerOnCreate - */ - public void setOwnerOnCreate(String ownerOnCreate) - { - this.ownerOnCreate = ownerOnCreate; - } - - /** - * The owner permissions to set on create. - * - * @param ownerPermissionsToSetOnCreate - */ - public void setOwnerPermissionsToSetOnCreate(Set ownerPermissionsToSetOnCreate) - { - this.ownerPermissionsToSetOnCreate = ownerPermissionsToSetOnCreate; - } - - /** - * General permissions to set on create. - * - * @param permissionsToSetOnCreate - */ - public void setPermissionsToSetOnCreate(Map> permissionsToSetOnCreate) - { - this.permissionsToSetOnCreate = permissionsToSetOnCreate; - } - - /** - * User permissions to set on create and on reference. - * - * @param userPermissions - */ - public void setUserPermissions(Set userPermissions) - { - this.userPermissions = userPermissions; - } - - /** - * Clear exising permissions on create. Useful to clear permissions from a template. - * - * @param clearExistingPermissionsOnCreate - */ - public void setClearExistingPermissionsOnCreate(boolean clearExistingPermissionsOnCreate) - { - this.clearExistingPermissionsOnCreate = clearExistingPermissionsOnCreate; - } - - /** - * Cache path to node resolution/ - * - * @return - */ - protected synchronized NodeRef getPathNodeRef() - { - if (pathNodeRef == null) - { - pathNodeRef = resolvePath(path); - } - return pathNodeRef; - } - - /** - * Utility metho to resolve paths to nodes. - * - * @param pathToResolve - * @return - */ - protected NodeRef resolvePath(String pathToResolve) - { - List refs = serviceRegistry.getSearchService().selectNodes( - serviceRegistry.getNodeService().getRootNode(storeRef), pathToResolve, null, - serviceRegistry.getNamespaceService(), false); - if (refs.size() != 1) - { - throw new IllegalStateException("Non-unique path: found : " + pathToResolve + " " + refs.size()); - } - return refs.get(0); - } - - /** - * The implementation of the policy binding. Run as the system user for auditing. - */ - public void onCreateNode(ChildAssociationRef childAssocRef) - { - AuthenticationUtil.RunAsWork action = new OnCreateNode(childAssocRef); - AuthenticationUtil.runAs(action, AuthenticationUtil.getSystemUserName()); - } - - /** - * Abstract implementation to find/create the approriate home space. - * - * @param person - * @return - */ - protected abstract HomeSpaceNodeRef getHomeFolder(NodeRef person); - - /** - * Helper class to encapsulate the createion settinhg permissions etc - * - * @author Andy Hind - */ - private class OnCreateNode implements AuthenticationUtil.RunAsWork - { - ChildAssociationRef childAssocRef; - - OnCreateNode(ChildAssociationRef childAssocRef) - { - this.childAssocRef = childAssocRef; - } - - public NodeRef doWork() throws Exception - { - - // Find person - NodeRef personNodeRef = childAssocRef.getChildRef(); - // Get home folder - HomeSpaceNodeRef homeFolder = getHomeFolder(personNodeRef); - // If it exists - if (homeFolder.getNodeRef() != null) - { - // Get uid and keep - String uid = DefaultTypeConverter.INSTANCE.convert(String.class, serviceRegistry.getNodeService() - .getProperty(personNodeRef, ContentModel.PROP_USERNAME)); - - // If created or found then set (other wise it was already set correctly) - if (homeFolder.getStatus() != HomeSpaceNodeRef.Status.VALID) - { - serviceRegistry.getNodeService().setProperty(personNodeRef, ContentModel.PROP_HOMEFOLDER, - homeFolder.getNodeRef()); - } - - // If created.. - if (homeFolder.getStatus() == HomeSpaceNodeRef.Status.CREATED) - { - // Set to a specified owner or make owned by the person. - if (ownerOnCreate != null) - { - serviceRegistry.getOwnableService().setOwner(homeFolder.getNodeRef(), ownerOnCreate); - } - else - { - - serviceRegistry.getOwnableService().setOwner(homeFolder.getNodeRef(), uid); - } - - // clear permissions - useful of not required from a template - - if (clearExistingPermissionsOnCreate) - { - serviceRegistry.getPermissionService().deletePermissions(homeFolder.getNodeRef()); - } - - // inherit permissions - - serviceRegistry.getPermissionService().setInheritParentPermissions(homeFolder.getNodeRef(), - inheritsPermissionsOnCreate); - - // Set owner permissions - - if (ownerPermissionsToSetOnCreate != null) - { - for (String permission : ownerPermissionsToSetOnCreate) - { - serviceRegistry.getPermissionService().setPermission(homeFolder.getNodeRef(), - PermissionService.OWNER_AUTHORITY, permission, true); - } - } - - // Add other permissions - - if (permissionsToSetOnCreate != null) - { - for (String user : permissionsToSetOnCreate.keySet()) - { - Set set = permissionsToSetOnCreate.get(user); - if (set != null) - { - for (String permission : set) - { - serviceRegistry.getPermissionService().setPermission(homeFolder.getNodeRef(), user, - permission, true); - } - } - } - } - } - - // Add user permissions on create and reference - - if (userPermissions != null) - { - for (String permission : userPermissions) - { - serviceRegistry.getPermissionService().setPermission(homeFolder.getNodeRef(), uid, permission, - true); - } - } - } - return homeFolder.getNodeRef(); - - } - } - -} +/* + * Copyright (C) 2005-2009 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.security.person; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.security.PermissionService; +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.beans.factory.InitializingBean; + +/** + * Common support for creating home folders This is hooked into node creation events from Person type objects via the homeFolderManager. Provider must all be wired up to the + * homeFolderManager. + * + * @author Andy Hind + */ +public abstract class AbstractHomeFolderProvider implements HomeFolderProvider, BeanNameAware, InitializingBean +{ + /** + * The provider name + */ + private String name; + + /** + * The home folder manager + */ + private HomeFolderManager homeFolderManager; + + /** + * The store ref in which to conduct searches + */ + private StoreRef storeRef; + + /** + * Service registry to get hold of public services (so taht actions are audited) + */ + private ServiceRegistry serviceRegistry; + + /** + * Tenant service - required for MT-enabled environment, else optional + */ + private TenantService tenantService; + + /** + * The path to a folder + */ + private String path; + + /** + * Cache the result of the path look up. + */ + private Map pathNodeRefs; // MT-aware + + /** + * The owner to set on creation of a home folder (if unset this will be the uid). + */ + private String ownerOnCreate; + + /** + * Set if permissions are inherited when nodes are created. + */ + private boolean inheritsPermissionsOnCreate = false; + + /** + * A set of permissions to set for the owner when a home folder is created + */ + private Set ownerPermissionsToSetOnCreate; + + /** + * General permissions to set on the node Map<(String)uid, Set<(String)permission>>. + */ + private Map> permissionsToSetOnCreate; + + /** + * Permissions to set for the user - on create and reference. + */ + private Set userPermissions; + + /** + * Clear existing permissions on new home folders (useful of created from a template. + */ + private boolean clearExistingPermissionsOnCreate = false; + + public AbstractHomeFolderProvider() + { + super(); + + pathNodeRefs = new ConcurrentHashMap(); + } + + /** + * Register with the homeFolderManagewr + */ + public void afterPropertiesSet() throws Exception + { + homeFolderManager.addProvider(this); + } + + // === // + // IOC // + // === // + + /** + * Get the home folder manager. + */ + protected HomeFolderManager getHomeFolderManager() + { + return homeFolderManager; + } + + /** + * Set the home folder manager. + * + * @param homeFolderManager + */ + public void setHomeFolderManager(HomeFolderManager homeFolderManager) + { + this.homeFolderManager = homeFolderManager; + } + + /** + * Get the provider name + */ + public String getName() + { + return name; + } + + /** + * The provider name is taken from the bean name + */ + public void setBeanName(String name) + { + this.name = name; + } + + /** + * Get the path + * + * @return + */ + protected String getPath() + { + return path; + } + + /** + * Set the path + * + * @param path + */ + public void setPath(String path) + { + this.path = path; + } + + /** + * Get the store ref + * + * @return + */ + protected StoreRef getStoreRef() + { + return storeRef; + } + + /** + * Set the store ref + * + * @param storeRef + */ + public void setStoreRef(StoreRef storeRef) + { + this.storeRef = storeRef; + } + + /** + * Set the store from the string url. + * + * @param storeUrl + */ + public void setStoreUrl(String storeUrl) + { + this.storeRef = new StoreRef(storeUrl); + } + + /** + * Get the service registry. + * + * @return + */ + protected ServiceRegistry getServiceRegistry() + { + return serviceRegistry; + } + + /** + * Set the service registry. + * + * @param serviceRegistry + */ + public void setServiceRegistry(ServiceRegistry serviceRegistry) + { + this.serviceRegistry = serviceRegistry; + } + + /** + * Set the tenant service + * + * @param tenantService + */ + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + /** + * Inherit permissions when home folder are created? + * + * @param inheritsPermissionsOnCreate + */ + public void setInheritsPermissionsOnCreate(boolean inheritsPermissionsOnCreate) + { + this.inheritsPermissionsOnCreate = inheritsPermissionsOnCreate; + } + + /** + * The owner to set on create. + * + * @param ownerOnCreate + */ + public void setOwnerOnCreate(String ownerOnCreate) + { + this.ownerOnCreate = ownerOnCreate; + } + + /** + * The owner permissions to set on create. + * + * @param ownerPermissionsToSetOnCreate + */ + public void setOwnerPermissionsToSetOnCreate(Set ownerPermissionsToSetOnCreate) + { + this.ownerPermissionsToSetOnCreate = ownerPermissionsToSetOnCreate; + } + + /** + * General permissions to set on create. + * + * @param permissionsToSetOnCreate + */ + public void setPermissionsToSetOnCreate(Map> permissionsToSetOnCreate) + { + this.permissionsToSetOnCreate = permissionsToSetOnCreate; + } + + /** + * User permissions to set on create and on reference. + * + * @param userPermissions + */ + public void setUserPermissions(Set userPermissions) + { + this.userPermissions = userPermissions; + } + + /** + * Clear exising permissions on create. Useful to clear permissions from a template. + * + * @param clearExistingPermissionsOnCreate + */ + public void setClearExistingPermissionsOnCreate(boolean clearExistingPermissionsOnCreate) + { + this.clearExistingPermissionsOnCreate = clearExistingPermissionsOnCreate; + } + + /** + * Cache path to node resolution + * + * @return + */ + protected NodeRef getPathNodeRef() + { + String tenantDomain = (tenantService != null ? tenantService.getCurrentUserDomain() : TenantService.DEFAULT_DOMAIN); + + NodeRef pathNodeRef = pathNodeRefs.get(tenantDomain); + if (pathNodeRef == null) + { + pathNodeRef = resolvePath(path); + pathNodeRefs.put(tenantDomain, pathNodeRef); + } + return pathNodeRef; + } + + /** + * Utility metho to resolve paths to nodes. + * + * @param pathToResolve + * @return + */ + protected NodeRef resolvePath(String pathToResolve) + { + List refs = serviceRegistry.getSearchService().selectNodes( + serviceRegistry.getNodeService().getRootNode(storeRef), pathToResolve, null, + serviceRegistry.getNamespaceService(), false); + if (refs.size() != 1) + { + throw new IllegalStateException("Non-unique path: found : " + pathToResolve + " " + refs.size()); + } + return refs.get(0); + } + + /** + * The implementation of the policy binding. Run as the system user for auditing. + */ + public void onCreateNode(ChildAssociationRef childAssocRef) + { + AuthenticationUtil.RunAsWork action = new OnCreateNode(childAssocRef); + AuthenticationUtil.runAs(action, AuthenticationUtil.getSystemUserName()); + } + + /** + * Abstract implementation to find/create the approriate home space. + * + * @param person + * @return + */ + protected abstract HomeSpaceNodeRef getHomeFolder(NodeRef person); + + /** + * Helper class to encapsulate the createion settinhg permissions etc + * + * @author Andy Hind + */ + private class OnCreateNode implements AuthenticationUtil.RunAsWork + { + ChildAssociationRef childAssocRef; + + OnCreateNode(ChildAssociationRef childAssocRef) + { + this.childAssocRef = childAssocRef; + } + + public NodeRef doWork() throws Exception + { + + // Find person + NodeRef personNodeRef = childAssocRef.getChildRef(); + // Get home folder + HomeSpaceNodeRef homeFolder = getHomeFolder(personNodeRef); + // If it exists + if (homeFolder.getNodeRef() != null) + { + // Get uid and keep + String uid = DefaultTypeConverter.INSTANCE.convert(String.class, serviceRegistry.getNodeService() + .getProperty(personNodeRef, ContentModel.PROP_USERNAME)); + + // If created or found then set (other wise it was already set correctly) + if (homeFolder.getStatus() != HomeSpaceNodeRef.Status.VALID) + { + serviceRegistry.getNodeService().setProperty(personNodeRef, ContentModel.PROP_HOMEFOLDER, + homeFolder.getNodeRef()); + } + + // If created.. + if (homeFolder.getStatus() == HomeSpaceNodeRef.Status.CREATED) + { + // Set to a specified owner or make owned by the person. + if (ownerOnCreate != null) + { + serviceRegistry.getOwnableService().setOwner(homeFolder.getNodeRef(), ownerOnCreate); + } + else + { + + serviceRegistry.getOwnableService().setOwner(homeFolder.getNodeRef(), uid); + } + + // clear permissions - useful of not required from a template + + if (clearExistingPermissionsOnCreate) + { + serviceRegistry.getPermissionService().deletePermissions(homeFolder.getNodeRef()); + } + + // inherit permissions + + serviceRegistry.getPermissionService().setInheritParentPermissions(homeFolder.getNodeRef(), + inheritsPermissionsOnCreate); + + // Set owner permissions + + if (ownerPermissionsToSetOnCreate != null) + { + for (String permission : ownerPermissionsToSetOnCreate) + { + serviceRegistry.getPermissionService().setPermission(homeFolder.getNodeRef(), + PermissionService.OWNER_AUTHORITY, permission, true); + } + } + + // Add other permissions + + if (permissionsToSetOnCreate != null) + { + for (String user : permissionsToSetOnCreate.keySet()) + { + Set set = permissionsToSetOnCreate.get(user); + if (set != null) + { + for (String permission : set) + { + serviceRegistry.getPermissionService().setPermission(homeFolder.getNodeRef(), user, + permission, true); + } + } + } + } + } + + // Add user permissions on create and reference + + if (userPermissions != null) + { + for (String permission : userPermissions) + { + serviceRegistry.getPermissionService().setPermission(homeFolder.getNodeRef(), uid, permission, + true); + } + } + } + return homeFolder.getNodeRef(); + + } + } + +} diff --git a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java index b5c862ed9a..fc33bfe82a 100644 --- a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java +++ b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java @@ -1,770 +1,792 @@ -/* - * 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.security.person; - -import java.io.Serializable; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.cache.SimpleCache; -import org.alfresco.repo.node.NodeServicePolicies; -import org.alfresco.repo.policy.JavaBehaviour; -import org.alfresco.repo.policy.PolicyComponent; -import org.alfresco.repo.security.permissions.PermissionServiceSPI; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport; -import org.alfresco.repo.transaction.TransactionListenerAdapter; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; -import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.cmr.search.ResultSetRow; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.AuthorityService; -import org.alfresco.service.cmr.security.NoSuchPersonException; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.service.namespace.NamespacePrefixResolver; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.namespace.RegexQNamePattern; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.GUID; -import org.alfresco.util.PropertyCheck; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -public class PersonServiceImpl extends TransactionListenerAdapter implements PersonService, NodeServicePolicies.OnCreateNodePolicy, NodeServicePolicies.BeforeDeleteNodePolicy -{ - private static Log s_logger = LogFactory.getLog(PersonServiceImpl.class); - - private static final String DELETE = "DELETE"; - - private static final String SPLIT = "SPLIT"; - - private static final String LEAVE = "LEAVE"; - - public static final String SYSTEM_FOLDER_SHORT_QNAME = "sys:system"; - - public static final String PEOPLE_FOLDER_SHORT_QNAME = "sys:people"; - - // IOC - - private StoreRef storeRef; - - private TransactionService transactionService; - - private NodeService nodeService; - - private TenantService tenantService; - - private SearchService searchService; - - private AuthorityService authorityService; - - private DictionaryService dictionaryService; - - private PermissionServiceSPI permissionServiceSPI; - - private NamespacePrefixResolver namespacePrefixResolver; - - private PolicyComponent policyComponent; - - private boolean createMissingPeople; - - private static Set mutableProperties; - - private boolean userNamesAreCaseSensitive = false; - - private String defaultHomeFolderProvider; - - private boolean processDuplicates = true; - - private String duplicateMode = LEAVE; - - private boolean lastIsBest = true; - - private boolean includeAutoCreated = false; - - private PersonDao personDao; - - /** a transactionally-safe cache to be injected */ - private SimpleCache personCache; - - static - { - Set props = new HashSet(); - props.add(ContentModel.PROP_HOMEFOLDER); - props.add(ContentModel.PROP_FIRSTNAME); - // Middle Name - props.add(ContentModel.PROP_LASTNAME); - props.add(ContentModel.PROP_EMAIL); - props.add(ContentModel.PROP_ORGID); - mutableProperties = Collections.unmodifiableSet(props); - } - - @Override - public boolean equals(Object obj) - { - return this == obj; - } - - @Override - public int hashCode() - { - return 1; - } - - /** - * Spring bean init method - */ - public void init() - { - PropertyCheck.mandatory(this, "storeUrl", storeRef); - PropertyCheck.mandatory(this, "transactionService", transactionService); - PropertyCheck.mandatory(this, "nodeService", nodeService); - PropertyCheck.mandatory(this, "searchService", searchService); - PropertyCheck.mandatory(this, "permissionServiceSPI", permissionServiceSPI); - PropertyCheck.mandatory(this, "authorityService", authorityService); - PropertyCheck.mandatory(this, "namespacePrefixResolver", namespacePrefixResolver); - PropertyCheck.mandatory(this, "policyComponent", policyComponent); - PropertyCheck.mandatory(this, "personCache", personCache); - PropertyCheck.mandatory(this, "personDao", personDao); - - this.policyComponent - .bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), ContentModel.TYPE_PERSON, new JavaBehaviour(this, "onCreateNode")); - this.policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), ContentModel.TYPE_PERSON, new JavaBehaviour(this, - "beforeDeleteNode")); - } - - public boolean getUserNamesAreCaseSensitive() - { - return userNamesAreCaseSensitive; - } - - public void setUserNamesAreCaseSensitive(boolean userNamesAreCaseSensitive) - { - this.userNamesAreCaseSensitive = userNamesAreCaseSensitive; - } - - void setDefaultHomeFolderProvider(String defaultHomeFolderProvider) - { - this.defaultHomeFolderProvider = defaultHomeFolderProvider; - } - - public void setDuplicateMode(String duplicateMode) - { - this.duplicateMode = duplicateMode; - } - - public void setIncludeAutoCreated(boolean includeAutoCreated) - { - this.includeAutoCreated = includeAutoCreated; - } - - public void setLastIsBest(boolean lastIsBest) - { - this.lastIsBest = lastIsBest; - } - - public void setProcessDuplicates(boolean processDuplicates) - { - this.processDuplicates = processDuplicates; - } - - public void setPersonDao(PersonDao personDao) - { - this.personDao = personDao; - } - - /** - * Set the username to person cache. - * - * @param personCache - * a transactionally safe cache - */ - public void setPersonCache(SimpleCache personCache) - { - this.personCache = personCache; - } - - /** - * Retrieve the person NodeRef for a username key. Depending on configuration missing people will be created if not - * found, else a NoSuchPersonException exception will be thrown. - * - * @param userName - * of the person NodeRef to retrieve - * @return NodeRef of the person as specified by the username - * @throws NoSuchPersonException - */ - public NodeRef getPerson(String userName) - { - NodeRef personNode = getPersonOrNull(userName); - if (personNode == null) - { - TxnReadState txnReadState = AlfrescoTransactionSupport.getTransactionReadState(); - if (createMissingPeople() && txnReadState == TxnReadState.TXN_READ_WRITE) - { - // We create missing people AND are in a read-write txn - return createMissingPerson(userName); - } - else - { - throw new NoSuchPersonException(userName); - } - } - else - { - return personNode; - } - } - - public boolean personExists(String caseSensitiveUserName) - { - return getPersonOrNull(caseSensitiveUserName) != null; - } - - private NodeRef getPersonOrNull(String searchUserName) - { - NodeRef returnRef = this.personCache.get(searchUserName); - if (returnRef == null) - { - List refs = personDao.getPersonOrNull(searchUserName, userNamesAreCaseSensitive); - if (refs.size() > 1) - { - returnRef = handleDuplicates(refs, searchUserName); - } - else if (refs.size() == 1) - { - returnRef = refs.get(0); - } - - // add to cache - this.personCache.put(searchUserName, returnRef); - } - return returnRef; - } - - private NodeRef handleDuplicates(List refs, String searchUserName) - { - if (processDuplicates) - { - NodeRef best = findBest(refs); - HashSet toHandle = new HashSet(); - toHandle.addAll(refs); - toHandle.remove(best); - addDuplicateNodeRefsToHandle(toHandle); - return best; - } - else - { - if (userNamesAreCaseSensitive) - { - throw new AlfrescoRuntimeException("Found more than one user for " + searchUserName + " (case sensitive)"); - } - else - { - throw new AlfrescoRuntimeException("Found more than one user for " + searchUserName + " (case insensitive)"); - } - } - } - - private static final String KEY_POST_TXN_DUPLICATES = "PersonServiceImpl.KEY_POST_TXN_DUPLICATES"; - - /** - * Get the txn-bound usernames that need cleaning up - */ - private Set getPostTxnDuplicates() - { - @SuppressWarnings("unchecked") - Set postTxnDuplicates = (Set) AlfrescoTransactionSupport.getResource(KEY_POST_TXN_DUPLICATES); - if (postTxnDuplicates == null) - { - postTxnDuplicates = new HashSet(); - AlfrescoTransactionSupport.bindResource(KEY_POST_TXN_DUPLICATES, postTxnDuplicates); - } - return postTxnDuplicates; - } - - /** - * Flag a username for cleanup after the transaction. - */ - private void addDuplicateNodeRefsToHandle(Set refs) - { - // Firstly, bind this service to the transaction - AlfrescoTransactionSupport.bindListener(this); - // Now get the post txn duplicate list - Set postTxnDuplicates = getPostTxnDuplicates(); - postTxnDuplicates.addAll(refs); - } - - /** - * Process clean up any duplicates that were flagged during the transaction. - */ - @Override - public void afterCommit() - { - // Get the duplicates in a form that can be read by the transaction work anonymous instance - final Set postTxnDuplicates = getPostTxnDuplicates(); - - RetryingTransactionCallback processDuplicateWork = new RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - - if (duplicateMode.equalsIgnoreCase(SPLIT)) - { - split(postTxnDuplicates); - s_logger.info("Split duplicate person objects"); - } - else if (duplicateMode.equalsIgnoreCase(DELETE)) - { - delete(postTxnDuplicates); - s_logger.info("Deleted duplicate person objects"); - } - else - { - if (s_logger.isDebugEnabled()) - { - s_logger.debug("Duplicate person objects exist"); - } - } - - // Done - return null; - } - }; - transactionService.getRetryingTransactionHelper().doInTransaction(processDuplicateWork, false, true); - } - - private void delete(Set toDelete) - { - for (NodeRef nodeRef : toDelete) - { - nodeService.deleteNode(nodeRef); - } - } - - private void split(Set toSplit) - { - for (NodeRef nodeRef : toSplit) - { - String userName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME)); - nodeService.setProperty(nodeRef, ContentModel.PROP_USERNAME, userName + GUID.generate()); - } - } - - private NodeRef findBest(List refs) - { - if (lastIsBest) - { - Collections.sort(refs, new CreationDateComparator(nodeService, false)); - } - else - { - Collections.sort(refs, new CreationDateComparator(nodeService, true)); - } - - NodeRef fallBack = null; - - for (NodeRef nodeRef : refs) - { - if (fallBack == null) - { - fallBack = nodeRef; - } - - if (includeAutoCreated || !wasAutoCreated(nodeRef)) - { - return nodeRef; - } - } - - return fallBack; - } - - private boolean wasAutoCreated(NodeRef nodeRef) - { - String userName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME)); - - String testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_FIRSTNAME)); - if ((testString == null) || !testString.equals(userName)) - { - return false; - } - - testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_LASTNAME)); - if ((testString == null) || !testString.equals("")) - { - return false; - } - - testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_EMAIL)); - if ((testString == null) || !testString.equals("")) - { - return false; - } - - testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_ORGID)); - if ((testString == null) || !testString.equals("")) - { - return false; - } - - testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_HOME_FOLDER_PROVIDER)); - if ((testString == null) || !testString.equals(defaultHomeFolderProvider)) - { - return false; - } - - return true; - } - - public boolean createMissingPeople() - { - return createMissingPeople; - } - - public Set getMutableProperties() - { - return mutableProperties; - } - - public void setPersonProperties(String userName, Map properties) - { - NodeRef personNode = getPersonOrNull(userName); - if (personNode == null) - { - if (createMissingPeople()) - { - personNode = createMissingPerson(userName); - } - else - { - throw new PersonException("No person found for user name " + userName); - } - - } - else - { - String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(personNode, ContentModel.PROP_USERNAME)); - properties.put(ContentModel.PROP_USERNAME, realUserName); - } - Map update = nodeService.getProperties(personNode); - update.putAll(properties); - - nodeService.setProperties(personNode, update); - } - - public boolean isMutable() - { - return true; - } - - private NodeRef createMissingPerson(String userName) - { - HashMap properties = getDefaultProperties(userName); - return createPerson(properties); - } - - private HashMap getDefaultProperties(String userName) - { - HashMap properties = new HashMap(); - properties.put(ContentModel.PROP_USERNAME, userName); - properties.put(ContentModel.PROP_FIRSTNAME, tenantService.getBaseNameUser(userName)); - properties.put(ContentModel.PROP_LASTNAME, ""); - properties.put(ContentModel.PROP_EMAIL, ""); - properties.put(ContentModel.PROP_ORGID, ""); - properties.put(ContentModel.PROP_HOME_FOLDER_PROVIDER, defaultHomeFolderProvider); - - properties.put(ContentModel.PROP_SIZE_CURRENT, 0L); - properties.put(ContentModel.PROP_SIZE_QUOTA, -1L); // no quota - - return properties; - } - - public NodeRef createPerson(Map properties) - { - String userName = DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_USERNAME)); - - tenantService.checkDomainUser(userName); - - properties.put(ContentModel.PROP_USERNAME, userName); - properties.put(ContentModel.PROP_SIZE_CURRENT, 0L); - - return nodeService.createNode( - getPeopleContainer(), - ContentModel.ASSOC_CHILDREN, - QName.createQName("cm", userName, namespacePrefixResolver), - ContentModel.TYPE_PERSON, properties).getChildRef(); - } - - public NodeRef getPeopleContainer() - { - NodeRef rootNodeRef = nodeService.getRootNode(tenantService.getName(storeRef)); - List children = nodeService.getChildAssocs(rootNodeRef, RegexQNamePattern.MATCH_ALL, QName.createQName(SYSTEM_FOLDER_SHORT_QNAME, namespacePrefixResolver)); - - if (children.size() == 0) - { - throw new AlfrescoRuntimeException("Required people system path not found: " + SYSTEM_FOLDER_SHORT_QNAME); - } - - NodeRef systemNodeRef = children.get(0).getChildRef(); - - children = nodeService.getChildAssocs(systemNodeRef, RegexQNamePattern.MATCH_ALL, QName.createQName(PEOPLE_FOLDER_SHORT_QNAME, namespacePrefixResolver)); - - if (children.size() == 0) - { - throw new AlfrescoRuntimeException("Required people system path not found: " + PEOPLE_FOLDER_SHORT_QNAME); - } - - NodeRef peopleNodeRef = children.get(0).getChildRef(); - return peopleNodeRef; - } - - public void deletePerson(String userName) - { - NodeRef personNodeRef = getPersonOrNull(userName); - - // delete the person - if (personNodeRef != null) - { - nodeService.deleteNode(personNodeRef); - } - - // remove user from any containing authorities - Set containerAuthorities = authorityService.getContainingAuthorities(null, userName, true); - for (String containerAuthority : containerAuthorities) - { - authorityService.removeAuthority(containerAuthority, userName); - } - - // remove any user permissions - permissionServiceSPI.deletePermissions(userName); - } - - public Set getAllPeople() - { - return personDao.getAllPeople(); - } - - public Set getPeopleFilteredByProperty(QName propertyKey, Serializable propertyValue) - { - // check that given property key is defined for content model type 'cm:person' - // and throw exception if it isn't - if (this.dictionaryService.getProperty(ContentModel.TYPE_PERSON, propertyKey) == null) - { - throw new AlfrescoRuntimeException("Property '" + propertyKey + "' is not defined " - + "for content model type cm:person"); - } - - LinkedHashSet people = new LinkedHashSet(); - - // - // Search for people using the given property - // - - SearchParameters sp = new SearchParameters(); - sp.setLanguage(SearchService.LANGUAGE_LUCENE); - sp.setQuery("@cm\\:" + propertyKey.getLocalName() + ":\"" + propertyValue + "\""); - sp.addStore(tenantService.getName(storeRef)); - sp.excludeDataInTheCurrentTransaction(false); - - ResultSet rs = null; - - try - { - rs = searchService.query(sp); - - for (ResultSetRow row : rs) - { - NodeRef nodeRef = row.getNodeRef(); - if (nodeService.exists(nodeRef)) - { - people.add(nodeRef); - } - } - } - finally - { - if (rs != null) - { - rs.close(); - } - } - - return people; - } - - // Policies - - /* - * (non-Javadoc) - * - * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy#onCreateNode(org.alfresco.service.cmr.repository.ChildAssociationRef) - */ - public void onCreateNode(ChildAssociationRef childAssocRef) - { - NodeRef personRef = childAssocRef.getChildRef(); - String username = (String) this.nodeService.getProperty(personRef, ContentModel.PROP_USERNAME); - this.personCache.put(username, personRef); - } - - /* - * (non-Javadoc) - * - * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) - */ - public void beforeDeleteNode(NodeRef nodeRef) - { - String username = (String) this.nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME); - this.personCache.remove(username); - } - - // IOC Setters - - public void setCreateMissingPeople(boolean createMissingPeople) - { - this.createMissingPeople = createMissingPeople; - } - - public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) - { - this.namespacePrefixResolver = namespacePrefixResolver; - } - - public void setAuthorityService(AuthorityService authorityService) - { - this.authorityService = authorityService; - } - - public void setDictionaryService(DictionaryService dictionaryService) - { - this.dictionaryService = dictionaryService; - } - - public void setPermissionServiceSPI(PermissionServiceSPI permissionServiceSPI) - { - this.permissionServiceSPI = permissionServiceSPI; - } - - public void setTransactionService(TransactionService transactionService) - { - this.transactionService = transactionService; - } - - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - public void setTenantService(TenantService tenantService) - { - this.tenantService = tenantService; - } - - public void setSearchService(SearchService searchService) - { - this.searchService = searchService; - } - - public void setPolicyComponent(PolicyComponent policyComponent) - { - this.policyComponent = policyComponent; - } - - public void setStoreUrl(String storeUrl) - { - this.storeRef = new StoreRef(storeUrl); - } - - public String getUserIdentifier(String caseSensitiveUserName) - { - NodeRef nodeRef = getPersonOrNull(caseSensitiveUserName); - if ((nodeRef != null) && nodeService.exists(nodeRef)) - { - String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME)); - return realUserName; - } - return null; - } - - public static class CreationDateComparator implements Comparator - { - private NodeService nodeService; - - boolean ascending; - - CreationDateComparator(NodeService nodeService, boolean ascending) - { - this.nodeService = nodeService; - this.ascending = ascending; - } - - public int compare(NodeRef first, NodeRef second) - { - Date firstDate = DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(first, ContentModel.PROP_CREATED)); - Date secondDate = DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(second, ContentModel.PROP_CREATED)); - - if (firstDate != null) - { - if (secondDate != null) - { - return firstDate.compareTo(secondDate) * (ascending ? 1 : -1); - } - else - { - return ascending ? -1 : 1; - } - } - else - { - if (secondDate != null) - { - return ascending ? 1 : -1; - } - else - { - return 0; - } - } - - } - } -} +/* + * Copyright (C) 2005-2009 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.security.person; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.PermissionServiceSPI; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.repo.transaction.TransactionListenerAdapter; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.ResultSetRow; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.NoSuchPersonException; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.GUID; +import org.alfresco.util.PropertyCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class PersonServiceImpl extends TransactionListenerAdapter implements PersonService, NodeServicePolicies.OnCreateNodePolicy, NodeServicePolicies.BeforeDeleteNodePolicy +{ + private static Log s_logger = LogFactory.getLog(PersonServiceImpl.class); + + private static final String DELETE = "DELETE"; + + private static final String SPLIT = "SPLIT"; + + private static final String LEAVE = "LEAVE"; + + public static final String SYSTEM_FOLDER_SHORT_QNAME = "sys:system"; + + public static final String PEOPLE_FOLDER_SHORT_QNAME = "sys:people"; + + // IOC + + private StoreRef storeRef; + + private TransactionService transactionService; + + private NodeService nodeService; + + private TenantService tenantService; + + private SearchService searchService; + + private AuthorityService authorityService; + + private DictionaryService dictionaryService; + + private PermissionServiceSPI permissionServiceSPI; + + private NamespacePrefixResolver namespacePrefixResolver; + + private PolicyComponent policyComponent; + + private boolean createMissingPeople; + + private static Set mutableProperties; + + private boolean userNamesAreCaseSensitive = false; + + private String defaultHomeFolderProvider; + + private boolean processDuplicates = true; + + private String duplicateMode = LEAVE; + + private boolean lastIsBest = true; + + private boolean includeAutoCreated = false; + + private PersonDao personDao; + + /** a transactionally-safe cache to be injected */ + private SimpleCache personCache; + + static + { + Set props = new HashSet(); + props.add(ContentModel.PROP_HOMEFOLDER); + props.add(ContentModel.PROP_FIRSTNAME); + // Middle Name + props.add(ContentModel.PROP_LASTNAME); + props.add(ContentModel.PROP_EMAIL); + props.add(ContentModel.PROP_ORGID); + mutableProperties = Collections.unmodifiableSet(props); + } + + @Override + public boolean equals(Object obj) + { + return this == obj; + } + + @Override + public int hashCode() + { + return 1; + } + + /** + * Spring bean init method + */ + public void init() + { + PropertyCheck.mandatory(this, "storeUrl", storeRef); + PropertyCheck.mandatory(this, "transactionService", transactionService); + PropertyCheck.mandatory(this, "nodeService", nodeService); + PropertyCheck.mandatory(this, "searchService", searchService); + PropertyCheck.mandatory(this, "permissionServiceSPI", permissionServiceSPI); + PropertyCheck.mandatory(this, "authorityService", authorityService); + PropertyCheck.mandatory(this, "namespacePrefixResolver", namespacePrefixResolver); + PropertyCheck.mandatory(this, "policyComponent", policyComponent); + PropertyCheck.mandatory(this, "personCache", personCache); + PropertyCheck.mandatory(this, "personDao", personDao); + + this.policyComponent + .bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), ContentModel.TYPE_PERSON, new JavaBehaviour(this, "onCreateNode")); + this.policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), ContentModel.TYPE_PERSON, new JavaBehaviour(this, + "beforeDeleteNode")); + } + + public boolean getUserNamesAreCaseSensitive() + { + return userNamesAreCaseSensitive; + } + + public void setUserNamesAreCaseSensitive(boolean userNamesAreCaseSensitive) + { + this.userNamesAreCaseSensitive = userNamesAreCaseSensitive; + } + + void setDefaultHomeFolderProvider(String defaultHomeFolderProvider) + { + this.defaultHomeFolderProvider = defaultHomeFolderProvider; + } + + public void setDuplicateMode(String duplicateMode) + { + this.duplicateMode = duplicateMode; + } + + public void setIncludeAutoCreated(boolean includeAutoCreated) + { + this.includeAutoCreated = includeAutoCreated; + } + + public void setLastIsBest(boolean lastIsBest) + { + this.lastIsBest = lastIsBest; + } + + public void setProcessDuplicates(boolean processDuplicates) + { + this.processDuplicates = processDuplicates; + } + + public void setPersonDao(PersonDao personDao) + { + this.personDao = personDao; + } + + /** + * Set the username to person cache. + * + * @param personCache + * a transactionally safe cache + */ + public void setPersonCache(SimpleCache personCache) + { + this.personCache = personCache; + } + + /** + * Retrieve the person NodeRef for a username key. Depending on configuration missing people will be created if not + * found, else a NoSuchPersonException exception will be thrown. + * + * @param userName + * of the person NodeRef to retrieve + * @return NodeRef of the person as specified by the username + * @throws NoSuchPersonException + */ + public NodeRef getPerson(final String userName) + { + // MT share - for activity service system callback + if (tenantService.isEnabled() && (AuthenticationUtil.SYSTEM_USER_NAME.equals(AuthenticationUtil.getRunAsUser())) && tenantService.isTenantUser(userName)) + { + final String tenantDomain = tenantService.getUserDomain(userName); + + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public NodeRef doWork() throws Exception + { + return getPersonImpl(userName); + } + }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + } + else + { + return getPersonImpl(userName); + } + } + + private NodeRef getPersonImpl(String userName) + { + NodeRef personNode = getPersonOrNull(userName); + if (personNode == null) + { + TxnReadState txnReadState = AlfrescoTransactionSupport.getTransactionReadState(); + if (createMissingPeople() && txnReadState == TxnReadState.TXN_READ_WRITE) + { + // We create missing people AND are in a read-write txn + return createMissingPerson(userName); + } + else + { + throw new NoSuchPersonException(userName); + } + } + else + { + return personNode; + } + } + + public boolean personExists(String caseSensitiveUserName) + { + return getPersonOrNull(caseSensitiveUserName) != null; + } + + private NodeRef getPersonOrNull(String searchUserName) + { + NodeRef returnRef = this.personCache.get(searchUserName); + if (returnRef == null) + { + List refs = personDao.getPersonOrNull(searchUserName, userNamesAreCaseSensitive); + if (refs.size() > 1) + { + returnRef = handleDuplicates(refs, searchUserName); + } + else if (refs.size() == 1) + { + returnRef = refs.get(0); + } + + // add to cache + this.personCache.put(searchUserName, returnRef); + } + return returnRef; + } + + private NodeRef handleDuplicates(List refs, String searchUserName) + { + if (processDuplicates) + { + NodeRef best = findBest(refs); + HashSet toHandle = new HashSet(); + toHandle.addAll(refs); + toHandle.remove(best); + addDuplicateNodeRefsToHandle(toHandle); + return best; + } + else + { + if (userNamesAreCaseSensitive) + { + throw new AlfrescoRuntimeException("Found more than one user for " + searchUserName + " (case sensitive)"); + } + else + { + throw new AlfrescoRuntimeException("Found more than one user for " + searchUserName + " (case insensitive)"); + } + } + } + + private static final String KEY_POST_TXN_DUPLICATES = "PersonServiceImpl.KEY_POST_TXN_DUPLICATES"; + + /** + * Get the txn-bound usernames that need cleaning up + */ + private Set getPostTxnDuplicates() + { + @SuppressWarnings("unchecked") + Set postTxnDuplicates = (Set) AlfrescoTransactionSupport.getResource(KEY_POST_TXN_DUPLICATES); + if (postTxnDuplicates == null) + { + postTxnDuplicates = new HashSet(); + AlfrescoTransactionSupport.bindResource(KEY_POST_TXN_DUPLICATES, postTxnDuplicates); + } + return postTxnDuplicates; + } + + /** + * Flag a username for cleanup after the transaction. + */ + private void addDuplicateNodeRefsToHandle(Set refs) + { + // Firstly, bind this service to the transaction + AlfrescoTransactionSupport.bindListener(this); + // Now get the post txn duplicate list + Set postTxnDuplicates = getPostTxnDuplicates(); + postTxnDuplicates.addAll(refs); + } + + /** + * Process clean up any duplicates that were flagged during the transaction. + */ + @Override + public void afterCommit() + { + // Get the duplicates in a form that can be read by the transaction work anonymous instance + final Set postTxnDuplicates = getPostTxnDuplicates(); + + RetryingTransactionCallback processDuplicateWork = new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + + if (duplicateMode.equalsIgnoreCase(SPLIT)) + { + split(postTxnDuplicates); + s_logger.info("Split duplicate person objects"); + } + else if (duplicateMode.equalsIgnoreCase(DELETE)) + { + delete(postTxnDuplicates); + s_logger.info("Deleted duplicate person objects"); + } + else + { + if (s_logger.isDebugEnabled()) + { + s_logger.debug("Duplicate person objects exist"); + } + } + + // Done + return null; + } + }; + transactionService.getRetryingTransactionHelper().doInTransaction(processDuplicateWork, false, true); + } + + private void delete(Set toDelete) + { + for (NodeRef nodeRef : toDelete) + { + nodeService.deleteNode(nodeRef); + } + } + + private void split(Set toSplit) + { + for (NodeRef nodeRef : toSplit) + { + String userName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME)); + nodeService.setProperty(nodeRef, ContentModel.PROP_USERNAME, userName + GUID.generate()); + } + } + + private NodeRef findBest(List refs) + { + if (lastIsBest) + { + Collections.sort(refs, new CreationDateComparator(nodeService, false)); + } + else + { + Collections.sort(refs, new CreationDateComparator(nodeService, true)); + } + + NodeRef fallBack = null; + + for (NodeRef nodeRef : refs) + { + if (fallBack == null) + { + fallBack = nodeRef; + } + + if (includeAutoCreated || !wasAutoCreated(nodeRef)) + { + return nodeRef; + } + } + + return fallBack; + } + + private boolean wasAutoCreated(NodeRef nodeRef) + { + String userName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME)); + + String testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_FIRSTNAME)); + if ((testString == null) || !testString.equals(userName)) + { + return false; + } + + testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_LASTNAME)); + if ((testString == null) || !testString.equals("")) + { + return false; + } + + testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_EMAIL)); + if ((testString == null) || !testString.equals("")) + { + return false; + } + + testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_ORGID)); + if ((testString == null) || !testString.equals("")) + { + return false; + } + + testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_HOME_FOLDER_PROVIDER)); + if ((testString == null) || !testString.equals(defaultHomeFolderProvider)) + { + return false; + } + + return true; + } + + public boolean createMissingPeople() + { + return createMissingPeople; + } + + public Set getMutableProperties() + { + return mutableProperties; + } + + public void setPersonProperties(String userName, Map properties) + { + NodeRef personNode = getPersonOrNull(userName); + if (personNode == null) + { + if (createMissingPeople()) + { + personNode = createMissingPerson(userName); + } + else + { + throw new PersonException("No person found for user name " + userName); + } + + } + else + { + String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(personNode, ContentModel.PROP_USERNAME)); + properties.put(ContentModel.PROP_USERNAME, realUserName); + } + Map update = nodeService.getProperties(personNode); + update.putAll(properties); + + nodeService.setProperties(personNode, update); + } + + public boolean isMutable() + { + return true; + } + + private NodeRef createMissingPerson(String userName) + { + HashMap properties = getDefaultProperties(userName); + return createPerson(properties); + } + + private HashMap getDefaultProperties(String userName) + { + HashMap properties = new HashMap(); + properties.put(ContentModel.PROP_USERNAME, userName); + properties.put(ContentModel.PROP_FIRSTNAME, tenantService.getBaseNameUser(userName)); + properties.put(ContentModel.PROP_LASTNAME, ""); + properties.put(ContentModel.PROP_EMAIL, ""); + properties.put(ContentModel.PROP_ORGID, ""); + properties.put(ContentModel.PROP_HOME_FOLDER_PROVIDER, defaultHomeFolderProvider); + + properties.put(ContentModel.PROP_SIZE_CURRENT, 0L); + properties.put(ContentModel.PROP_SIZE_QUOTA, -1L); // no quota + + return properties; + } + + public NodeRef createPerson(Map properties) + { + String userName = DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_USERNAME)); + + tenantService.checkDomainUser(userName); + + properties.put(ContentModel.PROP_USERNAME, userName); + properties.put(ContentModel.PROP_SIZE_CURRENT, 0L); + + return nodeService.createNode( + getPeopleContainer(), + ContentModel.ASSOC_CHILDREN, + QName.createQName("cm", userName, namespacePrefixResolver), + ContentModel.TYPE_PERSON, properties).getChildRef(); + } + + public NodeRef getPeopleContainer() + { + NodeRef rootNodeRef = nodeService.getRootNode(tenantService.getName(storeRef)); + List children = nodeService.getChildAssocs(rootNodeRef, RegexQNamePattern.MATCH_ALL, QName.createQName(SYSTEM_FOLDER_SHORT_QNAME, namespacePrefixResolver)); + + if (children.size() == 0) + { + throw new AlfrescoRuntimeException("Required people system path not found: " + SYSTEM_FOLDER_SHORT_QNAME); + } + + NodeRef systemNodeRef = children.get(0).getChildRef(); + + children = nodeService.getChildAssocs(systemNodeRef, RegexQNamePattern.MATCH_ALL, QName.createQName(PEOPLE_FOLDER_SHORT_QNAME, namespacePrefixResolver)); + + if (children.size() == 0) + { + throw new AlfrescoRuntimeException("Required people system path not found: " + PEOPLE_FOLDER_SHORT_QNAME); + } + + NodeRef peopleNodeRef = children.get(0).getChildRef(); + return peopleNodeRef; + } + + public void deletePerson(String userName) + { + NodeRef personNodeRef = getPersonOrNull(userName); + + // delete the person + if (personNodeRef != null) + { + nodeService.deleteNode(personNodeRef); + } + + // remove user from any containing authorities + Set containerAuthorities = authorityService.getContainingAuthorities(null, userName, true); + for (String containerAuthority : containerAuthorities) + { + authorityService.removeAuthority(containerAuthority, userName); + } + + // remove any user permissions + permissionServiceSPI.deletePermissions(userName); + } + + public Set getAllPeople() + { + return personDao.getAllPeople(); + } + + public Set getPeopleFilteredByProperty(QName propertyKey, Serializable propertyValue) + { + // check that given property key is defined for content model type 'cm:person' + // and throw exception if it isn't + if (this.dictionaryService.getProperty(ContentModel.TYPE_PERSON, propertyKey) == null) + { + throw new AlfrescoRuntimeException("Property '" + propertyKey + "' is not defined " + + "for content model type cm:person"); + } + + LinkedHashSet people = new LinkedHashSet(); + + // + // Search for people using the given property + // + + SearchParameters sp = new SearchParameters(); + sp.setLanguage(SearchService.LANGUAGE_LUCENE); + sp.setQuery("@cm\\:" + propertyKey.getLocalName() + ":\"" + propertyValue + "\""); + sp.addStore(tenantService.getName(storeRef)); + sp.excludeDataInTheCurrentTransaction(false); + + ResultSet rs = null; + + try + { + rs = searchService.query(sp); + + for (ResultSetRow row : rs) + { + NodeRef nodeRef = row.getNodeRef(); + if (nodeService.exists(nodeRef)) + { + people.add(nodeRef); + } + } + } + finally + { + if (rs != null) + { + rs.close(); + } + } + + return people; + } + + // Policies + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy#onCreateNode(org.alfresco.service.cmr.repository.ChildAssociationRef) + */ + public void onCreateNode(ChildAssociationRef childAssocRef) + { + NodeRef personRef = childAssocRef.getChildRef(); + String username = (String) this.nodeService.getProperty(personRef, ContentModel.PROP_USERNAME); + this.personCache.put(username, personRef); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) + */ + public void beforeDeleteNode(NodeRef nodeRef) + { + String username = (String) this.nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME); + this.personCache.remove(username); + } + + // IOC Setters + + public void setCreateMissingPeople(boolean createMissingPeople) + { + this.createMissingPeople = createMissingPeople; + } + + public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) + { + this.namespacePrefixResolver = namespacePrefixResolver; + } + + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + public void setPermissionServiceSPI(PermissionServiceSPI permissionServiceSPI) + { + this.permissionServiceSPI = permissionServiceSPI; + } + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + public void setStoreUrl(String storeUrl) + { + this.storeRef = new StoreRef(storeUrl); + } + + public String getUserIdentifier(String caseSensitiveUserName) + { + NodeRef nodeRef = getPersonOrNull(caseSensitiveUserName); + if ((nodeRef != null) && nodeService.exists(nodeRef)) + { + String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME)); + return realUserName; + } + return null; + } + + public static class CreationDateComparator implements Comparator + { + private NodeService nodeService; + + boolean ascending; + + CreationDateComparator(NodeService nodeService, boolean ascending) + { + this.nodeService = nodeService; + this.ascending = ascending; + } + + public int compare(NodeRef first, NodeRef second) + { + Date firstDate = DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(first, ContentModel.PROP_CREATED)); + Date secondDate = DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(second, ContentModel.PROP_CREATED)); + + if (firstDate != null) + { + if (secondDate != null) + { + return firstDate.compareTo(secondDate) * (ascending ? 1 : -1); + } + else + { + return ascending ? -1 : 1; + } + } + else + { + if (secondDate != null) + { + return ascending ? 1 : -1; + } + else + { + return 0; + } + } + + } + } +} diff --git a/source/java/org/alfresco/repo/site/SiteServiceImpl.java b/source/java/org/alfresco/repo/site/SiteServiceImpl.java index f45d874f6f..e6b1ff9751 100644 --- a/source/java/org/alfresco/repo/site/SiteServiceImpl.java +++ b/source/java/org/alfresco/repo/site/SiteServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2009 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 @@ -35,6 +35,7 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.activities.ActivityType; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.cmr.activities.ActivityService; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.model.FileFolderService; @@ -107,6 +108,7 @@ public class SiteServiceImpl implements SiteService, SiteModel private TaggingService taggingService; private AuthorityService authorityService; private DictionaryService dictionaryService; + private TenantService tenantService; /** * Set the path to the location of the sites root folder. For example: @@ -211,6 +213,16 @@ public class SiteServiceImpl implements SiteService, SiteModel this.dictionaryService = dictionaryService; } + /** + * Set the tenant service + * + * @param tenantService tenant service + */ + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + /** * Checks that all necessary properties and services have been provided. */ @@ -587,7 +599,30 @@ public class SiteServiceImpl implements SiteService, SiteModel /** * @see org.alfresco.service.cmr.site.SiteService#getSite(java.lang.String) */ - public SiteInfo getSite(String shortName) + public SiteInfo getSite(final String shortName) + { + // MT share - for activity service system callback + if (tenantService.isEnabled() && (AuthenticationUtil.SYSTEM_USER_NAME.equals(AuthenticationUtil.getRunAsUser())) && tenantService.isTenantName(shortName)) + { + final String tenantDomain = tenantService.getDomain(shortName); + final String sName = tenantService.getBaseName(shortName, true); + + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public SiteInfo doWork() throws Exception + { + SiteInfo site = getSiteImpl(sName); + return new SiteInfoImpl(site.getSitePreset(), shortName, site.getTitle(), site.getDescription(), site.getVisibility(), site.getCustomProperties(), site.getNodeRef()); + } + }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + } + else + { + return getSiteImpl(shortName); + } + } + + private SiteInfo getSiteImpl(String shortName) { SiteInfo result = null; @@ -714,7 +749,29 @@ public class SiteServiceImpl implements SiteService, SiteModel /** * @see org.alfresco.service.cmr.site.SiteService#listMembers(String, String, String, boolean) */ - public Map listMembers(String shortName, String nameFilter, String roleFilter, boolean collapseGroups) + public Map listMembers(String shortName, final String nameFilter, final String roleFilter, final boolean collapseGroups) + { + // MT share - for activity service system callback + if (tenantService.isEnabled() && (AuthenticationUtil.SYSTEM_USER_NAME.equals(AuthenticationUtil.getRunAsUser())) && tenantService.isTenantName(shortName)) + { + final String tenantDomain = tenantService.getDomain(shortName); + final String sName = tenantService.getBaseName(shortName, true); + + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() + { + public Map doWork() throws Exception + { + return listMembersImpl(sName, nameFilter, roleFilter, collapseGroups); + } + }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + } + else + { + return listMembersImpl(shortName, nameFilter, roleFilter, collapseGroups); + } + } + + private Map listMembersImpl(String shortName, String nameFilter, String roleFilter, boolean collapseGroups) { NodeRef siteNodeRef = getSiteNodeRef(shortName); if (siteNodeRef == null) diff --git a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java index 6d3d096d23..6738a4a3ae 100755 --- a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java @@ -1,1203 +1,1250 @@ -/* - * Copyright (C) 2005-2008 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.tenant; - -import java.io.File; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; -import java.util.Set; -import java.util.regex.Pattern; - -import javax.transaction.UserTransaction; - -import net.sf.acegisecurity.providers.encoding.PasswordEncoder; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.admin.RepoModelDefinition; -import org.alfresco.repo.attributes.BooleanAttributeValue; -import org.alfresco.repo.attributes.MapAttribute; -import org.alfresco.repo.attributes.MapAttributeValue; -import org.alfresco.repo.attributes.StringAttributeValue; -import org.alfresco.repo.content.TenantRoutingFileContentStore; -import org.alfresco.repo.dictionary.DictionaryComponent; -import org.alfresco.repo.importer.ImporterBootstrap; -import org.alfresco.repo.node.db.DbNodeServiceImpl; -import org.alfresco.repo.security.authentication.AuthenticationComponent; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; -import org.alfresco.repo.workflow.WorkflowDeployer; -import org.alfresco.service.cmr.admin.RepoAdminService; -import org.alfresco.service.cmr.attributes.AttributeService; -import org.alfresco.service.cmr.module.ModuleService; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.view.RepositoryExporterService; -import org.alfresco.service.cmr.workflow.WorkflowDefinition; -import org.alfresco.service.cmr.workflow.WorkflowService; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.ParameterCheck; -import org.alfresco.util.PropertyCheck; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; - -/** - * MT Admin Service Implementation. - * - */ - -public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationContextAware -{ - // Logger - private static Log logger = LogFactory.getLog(MultiTAdminServiceImpl.class); - - // Keep hold of the app context - private ApplicationContext ctx; - - // Dependencies - private NodeService nodeService; - private DictionaryComponent dictionaryComponent; - private RepoAdminService repoAdminService; - private AuthenticationComponent authenticationComponent; - private TransactionService transactionService; - private MultiTServiceImpl tenantService; - private AttributeService attributeService; - private PasswordEncoder passwordEncoder; - private TenantRoutingFileContentStore tenantFileContentStore; - private WorkflowService workflowService; - private RepositoryExporterService repositoryExporterService; - private WorkflowDeployer workflowDeployer; - private ModuleService moduleService; - - /* - * Tenant domain/ids are unique strings that are case-insensitive. Tenant ids must be valid filenames. - * They may also map onto domains and hence should allow valid FQDN. - * - * The following PCRE-style - * regex defines a valid label within a FQDN: - * - * ^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$ - * - * Less formally: - * - * o Case insensitive - * o First/last character: alphanumeric - * o Interior characters: alphanumeric plus hyphen - * o Minimum length: 2 characters - * o Maximum length: 63 characters - * - * The FQDN (fully qualified domain name) has the following constraints: - * - * o Maximum 255 characters (***) - * o Must contain at least one alpha - * - * Note: (***) Due to various internal restrictions (such as store identifier) we restrict tenant ids to 75 characters. - */ - - protected final static String REGEX_VALID_DNS_LABEL = "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$"; - - protected final static String REGEX_CONTAINS_ALPHA = "^(.*)[a-zA-Z](.*)$"; - - protected final static int MAX_LEN = 75; - - public void setNodeService(DbNodeServiceImpl dbNodeService) - { - this.nodeService = dbNodeService; - } - - public void setDictionaryComponent(DictionaryComponent dictionaryComponent) - { - this.dictionaryComponent = dictionaryComponent; - } - - public void setRepoAdminService(RepoAdminService repoAdminService) - { - this.repoAdminService = repoAdminService; - } - - public void setAuthenticationComponent(AuthenticationComponent authenticationComponent) - { - this.authenticationComponent = authenticationComponent; - } - - public void setTransactionService(TransactionService transactionService) - { - this.transactionService = transactionService; - } - - public void setTenantService(MultiTServiceImpl tenantService) - { - this.tenantService = tenantService; - } - - public void setAttributeService(AttributeService attributeService) - { - this.attributeService = attributeService; - } - - public void setPasswordEncoder(PasswordEncoder passwordEncoder) - { - this.passwordEncoder = passwordEncoder; - } - - public void setTenantFileContentStore(TenantRoutingFileContentStore tenantFileContentStore) - { - this.tenantFileContentStore = tenantFileContentStore; - } - - public void setWorkflowService(WorkflowService workflowService) - { - this.workflowService = workflowService; - } - - public void setRepositoryExporterService(RepositoryExporterService repositoryExporterService) - { - this.repositoryExporterService = repositoryExporterService; - } - - public void setWorkflowDeployer(WorkflowDeployer workflowDeployer) - { - this.workflowDeployer = workflowDeployer; - } - - public void setModuleService(ModuleService moduleService) - { - this.moduleService = moduleService; - } - - public static final String PROTOCOL_STORE_USER = "user"; - public static final String PROTOCOL_STORE_WORKSPACE = "workspace"; - public static final String PROTOCOL_STORE_SYSTEM = "system"; - public static final String PROTOCOL_STORE_ARCHIVE = "archive"; - public static final String STORE_BASE_ID_USER = "alfrescoUserStore"; - public static final String STORE_BASE_ID_SYSTEM = "system"; - public static final String STORE_BASE_ID_VERSION1 = "lightWeightVersionStore"; // deprecated - public static final String STORE_BASE_ID_VERSION2 = "version2Store"; - public static final String STORE_BASE_ID_SPACES = "SpacesStore"; - - - private static final String TENANTS_ATTRIBUTE_PATH = "alfresco-tenants"; - private static final String TENANT_ATTRIBUTE_ENABLED = "enabled"; - private static final String TENANT_ROOT_CONTENT_STORE_DIR = "rootContentStoreDir"; - - private static final String ADMIN_BASENAME = TenantService.ADMIN_BASENAME; - - private List tenantDeployers = new ArrayList(); - - - protected void checkProperties() - { - PropertyCheck.mandatory(this, "NodeService", nodeService); - PropertyCheck.mandatory(this, "DictionaryComponent", dictionaryComponent); - PropertyCheck.mandatory(this, "RepoAdminService", repoAdminService); - PropertyCheck.mandatory(this, "TransactionService", transactionService); - PropertyCheck.mandatory(this, "TenantService", tenantService); - PropertyCheck.mandatory(this, "AttributeService", attributeService); - PropertyCheck.mandatory(this, "PasswordEncoder", passwordEncoder); - PropertyCheck.mandatory(this, "TenantFileContentStore", tenantFileContentStore); - PropertyCheck.mandatory(this, "WorkflowService", workflowService); - PropertyCheck.mandatory(this, "RepositoryExporterService", repositoryExporterService); - PropertyCheck.mandatory(this, "WorkflowDeployer", workflowDeployer); - - PropertyCheck.mandatory(this, "ModuleService - see updated alfresco/extension/mt/mt-admin-context.xml.sample", moduleService); - } - - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException - { - this.ctx = applicationContext; - } - - public void startTenants() - { - checkProperties(); - - AuthenticationUtil.setMtEnabled(true); - - // initialise the tenant admin service and status of tenants (using attribute service) - // note: this requires that the repository schema has already been initialised - - // register dictionary - to allow enable/disable tenant callbacks - register(dictionaryComponent); - - // register file store - to allow enable/disable tenant callbacks - register(tenantFileContentStore); - - UserTransaction userTransaction = transactionService.getUserTransaction(); - - try - { - authenticationComponent.setSystemUserAsCurrentUser(); - userTransaction.begin(); - - // bootstrap Tenant Service internal cache - List tenants = getAllTenants(); - - int enabledCount = 0; - int disabledCount = 0; - - for (Tenant tenant : tenants) - { - if (tenant.isEnabled()) - { - // this will also call tenant deployers registered so far ... - enableTenant(tenant.getTenantDomain(), true); - enabledCount++; - } - else - { - // explicitly disable, without calling disableTenant callback - disableTenant(tenant.getTenantDomain(), false); - disabledCount++; - } - } - - tenantService.register(this); // callback to refresh tenantStatus cache - - userTransaction.commit(); - - if (logger.isInfoEnabled()) - { - logger.info(String.format("Alfresco Multi-Tenant startup - %d enabled tenants, %d disabled tenants", - enabledCount, disabledCount)); - } - } - catch(Throwable e) - { - // rollback the transaction - try { if (userTransaction != null) {userTransaction.rollback();} } catch (Exception ex) {} - throw new AlfrescoRuntimeException("Failed to bootstrap tenants", e); - } - finally - { - authenticationComponent.clearCurrentSecurityContext(); - } - } - - public void stopTenants() - { - tenantDeployers.clear(); - tenantDeployers = null; - } - - /** - * @see TenantAdminService.createTenant() - */ - public void createTenant(final String tenantDomain, final char[] tenantAdminRawPassword) - { - createTenant(tenantDomain, tenantAdminRawPassword, null); - } - - /** - * @see TenantAdminService.createTenant() - */ - public void createTenant(final String tenantDomain, final char[] tenantAdminRawPassword, String rootContentStoreDir) - { - ParameterCheck.mandatory("tenantAdminRawPassword", tenantAdminRawPassword); - - initTenant(tenantDomain, rootContentStoreDir); - - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - dictionaryComponent.init(); - tenantFileContentStore.init(); - - // create tenant-specific stores - ImporterBootstrap userImporterBootstrap = (ImporterBootstrap)ctx.getBean("userBootstrap"); - bootstrapUserTenantStore(userImporterBootstrap, tenantDomain, tenantAdminRawPassword); - - ImporterBootstrap systemImporterBootstrap = (ImporterBootstrap)ctx.getBean("systemBootstrap"); - bootstrapSystemTenantStore(systemImporterBootstrap, tenantDomain); - - // deprecated - ImporterBootstrap versionImporterBootstrap = (ImporterBootstrap)ctx.getBean("versionBootstrap"); - bootstrapVersionTenantStore(versionImporterBootstrap, tenantDomain); - - ImporterBootstrap version2ImporterBootstrap = (ImporterBootstrap)ctx.getBean("version2Bootstrap"); - bootstrapVersionTenantStore(version2ImporterBootstrap, tenantDomain); - - ImporterBootstrap spacesArchiveImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesArchiveBootstrap"); - bootstrapSpacesArchiveTenantStore(spacesArchiveImporterBootstrap, tenantDomain); - - ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesBootstrap"); - bootstrapSpacesTenantStore(spacesImporterBootstrap, tenantDomain); - - // notify listeners that tenant has been created & hence enabled - for (TenantDeployer tenantDeployer : tenantDeployers) - { - tenantDeployer.onEnableTenant(); - } - - // bootstrap workflows - workflowDeployer.init(); - - // bootstrap modules (if any) - moduleService.startModules(); - - return null; - } - }, getSystemUser(tenantDomain)); - - logger.info("Tenant created: " + tenantDomain); - } - - /** - * Export tenant - equivalent to the tenant admin running a 'complete repo' export from the Web Client Admin - */ - public void exportTenant(final String tenantDomain, final File directoryDestination) - { - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - repositoryExporterService.export(directoryDestination, tenantDomain); - return null; - } - }, getSystemUser(tenantDomain)); - - logger.info("Tenant exported: " + tenantDomain); - } - - /** - * Create tenant by restoring from a complete repository export. This is equivalent to a bootstrap import using restore-context.xml. - */ - public void importTenant(final String tenantDomain, final File directorySource, String rootContentStoreDir) - { - initTenant(tenantDomain, rootContentStoreDir); - - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - dictionaryComponent.init(); - tenantFileContentStore.init(); - - // import tenant-specific stores - importBootstrapUserTenantStore(tenantDomain, directorySource); - importBootstrapSystemTenantStore(tenantDomain, directorySource); - importBootstrapVersionTenantStore(tenantDomain, directorySource); - importBootstrapSpacesArchiveTenantStore(tenantDomain, directorySource); - importBootstrapSpacesModelsTenantStore(tenantDomain, directorySource); - importBootstrapSpacesTenantStore(tenantDomain, directorySource); - - // notify listeners that tenant has been created & hence enabled - for (TenantDeployer tenantDeployer : tenantDeployers) - { - tenantDeployer.onEnableTenant(); - } - - // bootstrap workflows - workflowDeployer.init(); - - // bootstrap modules (if any) - moduleService.startModules(); - - return null; - } - }, getSystemUser(tenantDomain)); - - logger.info("Tenant imported: " + tenantDomain); - } - - public boolean existsTenant(String tenantDomain) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("tenantDomain", tenantDomain); - - return (getTenantAttributes(tenantDomain) != null); - } - - private void putTenantAttributes(String tenantDomain, Tenant tenant) - { - if (! attributeService.exists(TENANTS_ATTRIBUTE_PATH)) - { - // bootstrap - attributeService.setAttribute("", TENANTS_ATTRIBUTE_PATH, new MapAttributeValue()); - } - - MapAttribute tenantProps = new MapAttributeValue(); - tenantProps.put(TENANT_ATTRIBUTE_ENABLED, new BooleanAttributeValue(tenant.isEnabled())); - tenantProps.put(TENANT_ROOT_CONTENT_STORE_DIR, new StringAttributeValue(tenant.getRootContentStoreDir())); - - attributeService.setAttribute(TENANTS_ATTRIBUTE_PATH, tenantDomain, tenantProps); - - // update tenant status cache - ((MultiTServiceImpl)tenantService).putTenant(tenantDomain, tenant); - } - - private Tenant getTenantAttributes(String tenantDomain) - { - if (attributeService.exists(TENANTS_ATTRIBUTE_PATH+"/"+tenantDomain)) - { - MapAttribute map = (MapAttribute)attributeService.getAttribute(TENANTS_ATTRIBUTE_PATH+"/"+tenantDomain); - if (map != null) - { - return new Tenant(tenantDomain, - map.get(TENANT_ATTRIBUTE_ENABLED).getBooleanValue(), - map.get(TENANT_ROOT_CONTENT_STORE_DIR).getStringValue()); - } - } - - return null; - } - - public void enableTenant(String tenantDomain) - { - if (! existsTenant(tenantDomain)) - { - throw new RuntimeException("Tenant does not exist: " + tenantDomain); - } - - if (isEnabledTenant(tenantDomain)) - { - logger.warn("Tenant already enabled: " + tenantDomain); - } - - enableTenant(tenantDomain, true); - } - - private void enableTenant(String tenantDomain, boolean notifyTenantDeployers) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("tenantDomain", tenantDomain); - - Tenant tenant = getTenantAttributes(tenantDomain); - tenant = new Tenant(tenantDomain, true, tenant.getRootContentStoreDir()); // enable - putTenantAttributes(tenantDomain, tenant); - - if (notifyTenantDeployers) - { - // notify listeners that tenant has been enabled - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - for (TenantDeployer tenantDeployer : tenantDeployers) - { - tenantDeployer.onEnableTenant(); - } - return null; - } - }, getSystemUser(tenantDomain)); - } - - logger.info("Tenant enabled: " + tenantDomain); - } - - public void disableTenant(String tenantDomain) - { - if (! existsTenant(tenantDomain)) - { - throw new RuntimeException("Tenant does not exist: " + tenantDomain); - } - - if (! isEnabledTenant(tenantDomain)) - { - logger.warn("Tenant already disabled: " + tenantDomain); - } - - disableTenant(tenantDomain, true); - } - - public void disableTenant(String tenantDomain, boolean notifyTenantDeployers) - { - if (notifyTenantDeployers) - { - // notify listeners that tenant has been disabled - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - for (TenantDeployer tenantDeployer : tenantDeployers) - { - tenantDeployer.onDisableTenant(); - } - return null; - } - }, getSystemUser(tenantDomain)); - } - - // update tenant attributes / tenant cache - need to disable after notifying listeners (else they cannot disable) - Tenant tenant = getTenantAttributes(tenantDomain); - tenant = new Tenant(tenantDomain, false, tenant.getRootContentStoreDir()); // disable - putTenantAttributes(tenantDomain, tenant); - - logger.info("Tenant disabled: " + tenantDomain); - } - - public boolean isEnabledTenant(String tenantDomain) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("tenantDomain", tenantDomain); - - Tenant tenant = getTenantAttributes(tenantDomain); - if (tenant != null) - { - return tenant.isEnabled(); - } - - return false; - } - - protected String getRootContentStoreDir(String tenantDomain) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("tenantDomain", tenantDomain); - - Tenant tenant = getTenantAttributes(tenantDomain); - if (tenant != null) - { - return tenant.getRootContentStoreDir(); - } - - return null; - } - - protected void putRootContentStoreDir(String tenantDomain, String rootContentStoreDir) - { - Tenant tenant = getTenantAttributes(tenantDomain); - tenant = new Tenant(tenantDomain, tenant.isEnabled(), rootContentStoreDir); - putTenantAttributes(tenantDomain, tenant); - } - - public Tenant getTenant(String tenantDomain) - { - if (! existsTenant(tenantDomain)) - { - throw new RuntimeException("Tenant does not exist: " + tenantDomain); - } - - return new Tenant(tenantDomain, isEnabledTenant(tenantDomain), getRootContentStoreDir(tenantDomain)); - } - - /** - * @see TenantAdminService.deleteTenant() - */ - public void deleteTenant(String tenantDomain) - { - if (! existsTenant(tenantDomain)) - { - throw new RuntimeException("Tenant does not exist: " + tenantDomain); - } - else - { - try - { - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - List workflowDefs = workflowService.getDefinitions(); - if (workflowDefs != null) - { - for (WorkflowDefinition workflowDef : workflowDefs) - { - workflowService.undeployDefinition(workflowDef.getId()); - } - } - - List messageResourceBundles = repoAdminService.getMessageBundles(); - if (messageResourceBundles != null) - { - for (String messageResourceBundle : messageResourceBundles) - { - repoAdminService.undeployMessageBundle(messageResourceBundle); - } - } - - List models = repoAdminService.getModels(); - if (models != null) - { - for (RepoModelDefinition model : models) - { - repoAdminService.undeployModel(model.getRepoName()); - } - } - - return null; - } - }, getSystemUser(tenantDomain)); - - final String tenantAdminUser = getTenantAdminUser(tenantDomain); - - // delete tenant-specific stores - nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_SPACES))); - nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_ARCHIVE, STORE_BASE_ID_SPACES))); - nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_VERSION1))); - nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_VERSION2))); - nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_SYSTEM, STORE_BASE_ID_SYSTEM))); - nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_USER, STORE_BASE_ID_USER))); - - - // notify listeners that tenant has been deleted & hence disabled - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - for (TenantDeployer tenantDeployer : tenantDeployers) - { - tenantDeployer.onDisableTenant(); - } - return null; - } - }, getSystemUser(tenantDomain)); - - // remove tenant - attributeService.removeAttribute(TENANTS_ATTRIBUTE_PATH, tenantDomain); - } - catch (Throwable t) - { - throw new AlfrescoRuntimeException("Failed to delete tenant: " + tenantDomain, t); - } - } - } - - /** - * @see TenantAdminService.getAllTenants() - */ - public List getAllTenants() - { - MapAttribute map = (MapAttribute)attributeService.getAttribute(TENANTS_ATTRIBUTE_PATH); - - List tenants = new ArrayList(); - - if (map != null) - { - // note: getAllTenants is called first, by TenantDeployer - hence need to initialise the TenantService status cache - Set tenantDomains = map.keySet(); - - for (String tenantDomain : tenantDomains) - { - Tenant tenant = getTenantAttributes(tenantDomain); - tenants.add(new Tenant(tenantDomain, tenant.isEnabled(), tenant.getRootContentStoreDir())); - } - } - - return tenants; // list of tenants or empty list - } - - private void importBootstrapSystemTenantStore(String tenantDomain, File directorySource) - { - // Import Bootstrap (restore) Tenant-Specific Version Store - Properties bootstrapView = new Properties(); - bootstrapView.put("path", "/"); - bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_system.acp"); - - List bootstrapViews = new ArrayList(1); - bootstrapViews.add(bootstrapView); - - ImporterBootstrap systemImporterBootstrap = (ImporterBootstrap)ctx.getBean("systemBootstrap"); - systemImporterBootstrap.setBootstrapViews(bootstrapViews); - systemImporterBootstrap.setLog(true); - - bootstrapSystemTenantStore(systemImporterBootstrap, tenantDomain); - } - - private void bootstrapSystemTenantStore(ImporterBootstrap systemImporterBootstrap, String tenantDomain) - { - // Bootstrap Tenant-Specific System Store - StoreRef bootstrapStoreRef = systemImporterBootstrap.getStoreRef(); - StoreRef tenantBootstrapStoreRef = new StoreRef(bootstrapStoreRef.getProtocol(), tenantService.getName(bootstrapStoreRef.getIdentifier(), tenantDomain)); - systemImporterBootstrap.setStoreUrl(tenantBootstrapStoreRef.toString()); - - // override default property (workspace://SpacesStore) - List mustNotExistStoreUrls = new ArrayList(); - mustNotExistStoreUrls.add(new StoreRef(PROTOCOL_STORE_WORKSPACE, tenantService.getName(STORE_BASE_ID_USER, tenantDomain)).toString()); - systemImporterBootstrap.setMustNotExistStoreUrls(mustNotExistStoreUrls); - - systemImporterBootstrap.bootstrap(); - - // reset since systemImporter is singleton (hence reused) - systemImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); - - logger.debug("Bootstrapped store: " + tenantService.getBaseName(tenantBootstrapStoreRef)); - } - - private void importBootstrapUserTenantStore(String tenantDomain, File directorySource) - { - // Import Bootstrap (restore) Tenant-Specific User Store - Properties bootstrapView = new Properties(); - bootstrapView.put("path", "/"); - bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_users.acp"); - - List bootstrapViews = new ArrayList(1); - bootstrapViews.add(bootstrapView); - - ImporterBootstrap userImporterBootstrap = (ImporterBootstrap)ctx.getBean("userBootstrap"); - userImporterBootstrap.setBootstrapViews(bootstrapViews); - userImporterBootstrap.setLog(true); - - bootstrapUserTenantStore(userImporterBootstrap, tenantDomain, null); - } - - private void bootstrapUserTenantStore(ImporterBootstrap userImporterBootstrap, String tenantDomain, char[] tenantAdminRawPassword) - { - // Bootstrap Tenant-Specific User Store - StoreRef bootstrapStoreRef = userImporterBootstrap.getStoreRef(); - bootstrapStoreRef = new StoreRef(bootstrapStoreRef.getProtocol(), tenantService.getName(bootstrapStoreRef.getIdentifier(), tenantDomain)); - userImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); - - // override admin username property - Properties props = userImporterBootstrap.getConfiguration(); - props.put("alfresco_user_store.adminusername", getTenantAdminUser(tenantDomain)); - - if (tenantAdminRawPassword != null) - { - String salt = null; // GUID.generate(); - props.put("alfresco_user_store.adminpassword", passwordEncoder.encodePassword(new String(tenantAdminRawPassword), salt)); - } - - userImporterBootstrap.bootstrap(); - - logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); - } - - private void importBootstrapVersionTenantStore(String tenantDomain, File directorySource) - { - // Import Bootstrap (restore) Tenant-Specific Version Store - Properties bootstrapView = new Properties(); - bootstrapView.put("path", "/"); - bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_versions2.acp"); - - List bootstrapViews = new ArrayList(1); - bootstrapViews.add(bootstrapView); - - ImporterBootstrap versionImporterBootstrap = (ImporterBootstrap)ctx.getBean("versionBootstrap"); - versionImporterBootstrap.setBootstrapViews(bootstrapViews); - versionImporterBootstrap.setLog(true); - - bootstrapVersionTenantStore(versionImporterBootstrap, tenantDomain); - } - - private void bootstrapVersionTenantStore(ImporterBootstrap versionImporterBootstrap, String tenantDomain) - { - // Bootstrap Tenant-Specific Version Store - StoreRef bootstrapStoreRef = versionImporterBootstrap.getStoreRef(); - bootstrapStoreRef = new StoreRef(bootstrapStoreRef.getProtocol(), tenantService.getName(bootstrapStoreRef.getIdentifier(), tenantDomain)); - versionImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); - - versionImporterBootstrap.bootstrap(); - - logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); - } - - private void importBootstrapSpacesArchiveTenantStore(String tenantDomain, File directorySource) - { - // Import Bootstrap (restore) Tenant-Specific Spaces Archive Store - Properties bootstrapView = new Properties(); - bootstrapView.put("path", "/"); - bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_spaces_archive.acp"); - - List bootstrapViews = new ArrayList(1); - bootstrapViews.add(bootstrapView); - - ImporterBootstrap spacesArchiveImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesArchiveBootstrap"); - spacesArchiveImporterBootstrap.setBootstrapViews(bootstrapViews); - spacesArchiveImporterBootstrap.setLog(true); - - bootstrapSpacesArchiveTenantStore(spacesArchiveImporterBootstrap, tenantDomain); - } - - private void bootstrapSpacesArchiveTenantStore(ImporterBootstrap spacesArchiveImporterBootstrap, String tenantDomain) - { - // Bootstrap Tenant-Specific Spaces Archive Store - StoreRef bootstrapStoreRef = spacesArchiveImporterBootstrap.getStoreRef(); - bootstrapStoreRef = new StoreRef(bootstrapStoreRef.getProtocol(), tenantService.getName(bootstrapStoreRef.getIdentifier(), tenantDomain)); - spacesArchiveImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); - - // override default property (archive://SpacesStore) - List mustNotExistStoreUrls = new ArrayList(); - mustNotExistStoreUrls.add(bootstrapStoreRef.toString()); - spacesArchiveImporterBootstrap.setMustNotExistStoreUrls(mustNotExistStoreUrls); - - spacesArchiveImporterBootstrap.bootstrap(); - - logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); - } - - private void importBootstrapSpacesModelsTenantStore(String tenantDomain, File directorySource) - { - // Import Bootstrap (restore) Tenant-Specific Spaces Store - Properties bootstrapView = new Properties(); - bootstrapView.put("path", "/"); - bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_models.acp"); - - List bootstrapViews = new ArrayList(1); - bootstrapViews.add(bootstrapView); - - ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesBootstrap"); - spacesImporterBootstrap.setBootstrapViews(bootstrapViews); - spacesImporterBootstrap.setLog(true); - - bootstrapSpacesTenantStore(spacesImporterBootstrap, tenantDomain); - } - - private void importBootstrapSpacesTenantStore(String tenantDomain, File directorySource) - { - // Import Bootstrap (restore) Tenant-Specific Spaces Store - Properties bootstrapView = new Properties(); - bootstrapView.put("path", "/"); - bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_spaces.acp"); - bootstrapView.put("uuidBinding", "UPDATE_EXISTING"); - - List bootstrapViews = new ArrayList(1); - bootstrapViews.add(bootstrapView); - - ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesBootstrap"); - spacesImporterBootstrap.setBootstrapViews(bootstrapViews); - spacesImporterBootstrap.setLog(true); - - spacesImporterBootstrap.setUseExistingStore(true); - - bootstrapSpacesTenantStore(spacesImporterBootstrap, tenantDomain); - } - - private void bootstrapSpacesTenantStore(ImporterBootstrap spacesImporterBootstrap, String tenantDomain) - { - // Bootstrap Tenant-Specific Spaces Store - StoreRef bootstrapStoreRef = spacesImporterBootstrap.getStoreRef(); - bootstrapStoreRef = new StoreRef(bootstrapStoreRef.getProtocol(), tenantService.getName(bootstrapStoreRef.getIdentifier(), tenantDomain)); - spacesImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); - - // override admin username property - Properties props = spacesImporterBootstrap.getConfiguration(); - props.put("alfresco_user_store.adminusername", getTenantAdminUser(tenantDomain)); - - // override guest username property - props.put("alfresco_user_store.guestusername", getTenantGuestUser(tenantDomain)); - - spacesImporterBootstrap.bootstrap(); - - /* TODO - pending fix for ETHREEOH-283 - // calculate any missing usages - UserUsageTrackingComponent userUsageTrackingComponent = (UserUsageTrackingComponent)ctx.getBean(UserUsageBootstrapJob.KEY_COMPONENT); - userUsageTrackingComponent.bootstrapInternal(); - */ - - logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); - } - - public void deployTenants(final TenantDeployer deployer, Log logger) - { - if (deployer == null) - { - throw new AlfrescoRuntimeException("Deployer must be provided"); - } - if (logger == null) - { - throw new AlfrescoRuntimeException("Logger must be provided"); - } - - if (tenantService.isEnabled()) - { - UserTransaction userTransaction = transactionService.getUserTransaction(); - authenticationComponent.setSystemUserAsCurrentUser(); - - List tenants = null; - try - { - userTransaction.begin(); - tenants = getAllTenants(); - userTransaction.commit(); - } - catch(Throwable e) - { - // rollback the transaction - try { if (userTransaction != null) {userTransaction.rollback();} } catch (Exception ex) {} - throw new AlfrescoRuntimeException("Failed to get tenants", e); - } - finally - { - authenticationComponent.clearCurrentSecurityContext(); - } - - for (Tenant tenant : tenants) - { - if (tenant.isEnabled()) - { - try - { - // switch to admin in order to deploy within context of tenant domain - // assumes each tenant has default "admin" user - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - // init the service within tenant context - deployer.init(); - return null; - } - }, getSystemUser(tenant.getTenantDomain())); - - } - catch (Throwable e) - { - logger.error("Deployment failed" + e); - - StringWriter stringWriter = new StringWriter(); - e.printStackTrace(new PrintWriter(stringWriter)); - logger.error(stringWriter.toString()); - - // tenant deploy failure should not necessarily affect other tenants - } - } - } - } - } - - public void undeployTenants(final TenantDeployer deployer, Log logger) - { - if (deployer == null) - { - throw new AlfrescoRuntimeException("Deployer must be provided"); - } - if (logger == null) - { - throw new AlfrescoRuntimeException("Logger must be provided"); - } - - if (tenantService.isEnabled()) - { - UserTransaction userTransaction = transactionService.getUserTransaction(); - authenticationComponent.setSystemUserAsCurrentUser(); - - List tenants = null; - try - { - userTransaction.begin(); - tenants = getAllTenants(); - userTransaction.commit(); - } - catch(Throwable e) - { - // rollback the transaction - try { if (userTransaction != null) {userTransaction.rollback();} } catch (Exception ex) {} - try {authenticationComponent.clearCurrentSecurityContext(); } catch (Exception ex) {} - throw new AlfrescoRuntimeException("Failed to get tenants", e); - } - - try - { - AuthenticationUtil.pushAuthentication(); - for (Tenant tenant : tenants) - { - if (tenant.isEnabled()) - { - try - { - // switch to admin in order to deploy within context of tenant domain - // assumes each tenant has default "admin" user - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - // destroy the service within tenant context - deployer.destroy(); - return null; - } - }, getSystemUser(tenant.getTenantDomain())); - - } - catch (Throwable e) - { - logger.error("Undeployment failed" + e); - - StringWriter stringWriter = new StringWriter(); - e.printStackTrace(new PrintWriter(stringWriter)); - logger.error(stringWriter.toString()); - - // tenant undeploy failure should not necessarily affect other tenants - } - } - } - } - finally - { - AuthenticationUtil.popAuthentication(); - } - } - } - - public void register(TenantDeployer deployer) - { - if (deployer == null) - { - throw new AlfrescoRuntimeException("Deployer must be provided"); - } - - if (! tenantDeployers.contains(deployer)) - { - tenantDeployers.add(deployer); - } - } - - public void unregister(TenantDeployer deployer) - { - if (deployer == null) - { - throw new AlfrescoRuntimeException("Deployer must be provided"); - } - - if (tenantDeployers != null) - { - tenantDeployers.remove(deployer); - } - } - - public void resetCache(String tenantDomain) - { - if (existsTenant(tenantDomain)) - { - if (isEnabledTenant(tenantDomain)) - { - enableTenant(tenantDomain); - } - else - { - disableTenant(tenantDomain); - } - } - else - { - throw new AlfrescoRuntimeException("No such tenant " + tenantDomain); - } - } - - private void initTenant(String tenantDomain, String rootContentStoreDir) - { - validateTenantName(tenantDomain); - - if (existsTenant(tenantDomain)) - { - throw new AlfrescoRuntimeException("Tenant already exists: " + tenantDomain); - } - - if (rootContentStoreDir == null) - { - rootContentStoreDir = tenantFileContentStore.getDefaultRootDir(); - } - else - { - File tenantRootDir = new File(rootContentStoreDir); - if ((tenantRootDir.exists()) && (tenantRootDir.list().length != 0)) - { - throw new AlfrescoRuntimeException("Tenant root directory is not empty: " + rootContentStoreDir); - } - } - - // init - need to enable tenant (including tenant service) before stores bootstrap - Tenant tenant = new Tenant(tenantDomain, true, rootContentStoreDir); - putTenantAttributes(tenantDomain, tenant); - } - - private void validateTenantName(String tenantDomain) - { - ParameterCheck.mandatory("tenantDomain", tenantDomain); - - if (tenantDomain.length() > MAX_LEN) - { - throw new IllegalArgumentException(tenantDomain + " is not a valid tenant name (must be less than " + MAX_LEN + " characters)"); - } - - if (! Pattern.matches(REGEX_CONTAINS_ALPHA, tenantDomain)) - { - throw new IllegalArgumentException(tenantDomain + " is not a valid tenant name (must contain at least one alpha character)"); - } - - String[] dnsLabels = tenantDomain.split("\\."); - if (dnsLabels.length != 0) - { - for (int i = 0; i < dnsLabels.length; i++) - { - if (! Pattern.matches(REGEX_VALID_DNS_LABEL, dnsLabels[i])) - { - throw new IllegalArgumentException(dnsLabels[i] + " is not a valid DNS label (must match " + REGEX_VALID_DNS_LABEL + ")"); - } - } - } - else - { - if (! Pattern.matches(REGEX_VALID_DNS_LABEL, tenantDomain)) - { - throw new IllegalArgumentException(tenantDomain + " is not a valid DNS label (must match " + REGEX_VALID_DNS_LABEL + ")"); - } - } - } - - // tenant deployer/user services delegated to tenant service - - public boolean isEnabled() - { - return tenantService.isEnabled(); - } - - public String getCurrentUserDomain() - { - return tenantService.getCurrentUserDomain(); - } - - public String getUserDomain(String username) - { - return tenantService.getUserDomain(username); - } - - public String getBaseNameUser(String username) - { - return tenantService.getBaseNameUser(username); - } - - public String getDomainUser(String baseUsername, String tenantDomain) - { - return tenantService.getDomainUser(baseUsername, tenantDomain); - } - - public String getDomain(String name) - { - return tenantService.getDomain(name); - } - - // local helpers - - private String getSystemUser(String tenantDomain) - { - return tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain); - } - - private String getTenantAdminUser(String tenantDomain) - { - return tenantService.getDomainUser(ADMIN_BASENAME, tenantDomain); - } - - private String getTenantGuestUser(String tenantDomain) - { - return tenantService.getDomainUser(authenticationComponent.getGuestUserName(), tenantDomain); - } -} +/* + * Copyright (C) 2005-2009 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.tenant; + +import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.regex.Pattern; + +import javax.transaction.UserTransaction; + +import net.sf.acegisecurity.providers.encoding.PasswordEncoder; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.admin.RepoModelDefinition; +import org.alfresco.repo.attributes.BooleanAttributeValue; +import org.alfresco.repo.attributes.MapAttribute; +import org.alfresco.repo.attributes.MapAttributeValue; +import org.alfresco.repo.attributes.StringAttributeValue; +import org.alfresco.repo.content.TenantRoutingFileContentStore; +import org.alfresco.repo.dictionary.DictionaryComponent; +import org.alfresco.repo.importer.ImporterBootstrap; +import org.alfresco.repo.node.db.DbNodeServiceImpl; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.site.SiteAVMBootstrap; +import org.alfresco.repo.workflow.WorkflowDeployer; +import org.alfresco.service.cmr.admin.RepoAdminService; +import org.alfresco.service.cmr.attributes.AttributeService; +import org.alfresco.service.cmr.module.ModuleService; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.view.RepositoryExporterService; +import org.alfresco.service.cmr.workflow.WorkflowDefinition; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ParameterCheck; +import org.alfresco.util.PropertyCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * MT Admin Service Implementation. + * + */ + +public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationContextAware +{ + // Logger + private static Log logger = LogFactory.getLog(MultiTAdminServiceImpl.class); + + // Keep hold of the app context + private ApplicationContext ctx; + + // Dependencies + private NodeService nodeService; + private DictionaryComponent dictionaryComponent; + private RepoAdminService repoAdminService; + private AuthenticationComponent authenticationComponent; + private TransactionService transactionService; + private MultiTServiceImpl tenantService; + private AttributeService attributeService; + private PasswordEncoder passwordEncoder; + private TenantRoutingFileContentStore tenantFileContentStore; + private WorkflowService workflowService; + private RepositoryExporterService repositoryExporterService; + private ModuleService moduleService; + private SiteAVMBootstrap siteAVMBootstrap; + + private List workflowDeployers = new ArrayList(); + + /* + * Tenant domain/ids are unique strings that are case-insensitive. Tenant ids must be valid filenames. + * They may also map onto domains and hence should allow valid FQDN. + * + * The following PCRE-style + * regex defines a valid label within a FQDN: + * + * ^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$ + * + * Less formally: + * + * o Case insensitive + * o First/last character: alphanumeric + * o Interior characters: alphanumeric plus hyphen + * o Minimum length: 2 characters + * o Maximum length: 63 characters + * + * The FQDN (fully qualified domain name) has the following constraints: + * + * o Maximum 255 characters (***) + * o Must contain at least one alpha + * + * Note: (***) Due to various internal restrictions (such as store identifier) we restrict tenant ids to 75 characters. + */ + + protected final static String REGEX_VALID_DNS_LABEL = "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$"; + + protected final static String REGEX_CONTAINS_ALPHA = "^(.*)[a-zA-Z](.*)$"; + + protected final static int MAX_LEN = 75; + + public void setNodeService(DbNodeServiceImpl dbNodeService) + { + this.nodeService = dbNodeService; + } + + public void setDictionaryComponent(DictionaryComponent dictionaryComponent) + { + this.dictionaryComponent = dictionaryComponent; + } + + public void setRepoAdminService(RepoAdminService repoAdminService) + { + this.repoAdminService = repoAdminService; + } + + public void setAuthenticationComponent(AuthenticationComponent authenticationComponent) + { + this.authenticationComponent = authenticationComponent; + } + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public void setTenantService(MultiTServiceImpl tenantService) + { + this.tenantService = tenantService; + } + + public void setAttributeService(AttributeService attributeService) + { + this.attributeService = attributeService; + } + + public void setPasswordEncoder(PasswordEncoder passwordEncoder) + { + this.passwordEncoder = passwordEncoder; + } + + public void setTenantFileContentStore(TenantRoutingFileContentStore tenantFileContentStore) + { + this.tenantFileContentStore = tenantFileContentStore; + } + + public void setWorkflowService(WorkflowService workflowService) + { + this.workflowService = workflowService; + } + + public void setRepositoryExporterService(RepositoryExporterService repositoryExporterService) + { + this.repositoryExporterService = repositoryExporterService; + } + + /** + * @deprecated see setWorkflowDeployers + */ + public void setWorkflowDeployer(WorkflowDeployer workflowDeployer) + { + // NOOP + logger.warn(WARN_MSG); + } + + public void setModuleService(ModuleService moduleService) + { + this.moduleService = moduleService; + } + + public void setSiteAVMBootstrap(SiteAVMBootstrap siteAVMBootstrap) + { + this.siteAVMBootstrap = siteAVMBootstrap; + } + + public static final String PROTOCOL_STORE_USER = "user"; + public static final String PROTOCOL_STORE_WORKSPACE = "workspace"; + public static final String PROTOCOL_STORE_SYSTEM = "system"; + public static final String PROTOCOL_STORE_ARCHIVE = "archive"; + public static final String STORE_BASE_ID_USER = "alfrescoUserStore"; + public static final String STORE_BASE_ID_SYSTEM = "system"; + public static final String STORE_BASE_ID_VERSION1 = "lightWeightVersionStore"; // deprecated + public static final String STORE_BASE_ID_VERSION2 = "version2Store"; + public static final String STORE_BASE_ID_SPACES = "SpacesStore"; + + + private static final String TENANTS_ATTRIBUTE_PATH = "alfresco-tenants"; + private static final String TENANT_ATTRIBUTE_ENABLED = "enabled"; + private static final String TENANT_ROOT_CONTENT_STORE_DIR = "rootContentStoreDir"; + + private static final String ADMIN_BASENAME = TenantService.ADMIN_BASENAME; + + private List tenantDeployers = new ArrayList(); + + private static final String WARN_MSG = "Please update your alfresco/extension/mt/mt-admin-context.xml to use baseMultiTAdminService (see latest alfresco/extension/mt/mt-admin-context.xml.sample)"; + + protected void checkProperties() + { + PropertyCheck.mandatory(this, "NodeService", nodeService); + PropertyCheck.mandatory(this, "DictionaryComponent", dictionaryComponent); + PropertyCheck.mandatory(this, "RepoAdminService", repoAdminService); + PropertyCheck.mandatory(this, "TransactionService", transactionService); + PropertyCheck.mandatory(this, "TenantService", tenantService); + PropertyCheck.mandatory(this, "AttributeService", attributeService); + PropertyCheck.mandatory(this, "PasswordEncoder", passwordEncoder); + PropertyCheck.mandatory(this, "TenantFileContentStore", tenantFileContentStore); + PropertyCheck.mandatory(this, "WorkflowService", workflowService); + PropertyCheck.mandatory(this, "RepositoryExporterService", repositoryExporterService); + + // for backwards compatibility only + + if (moduleService == null) + { + // default for now + logger.warn(WARN_MSG); + moduleService = (ModuleService)ctx.getBean("moduleService"); + } + + if (siteAVMBootstrap == null) + { + // default for now + logger.warn(WARN_MSG); + siteAVMBootstrap = (SiteAVMBootstrap)ctx.getBean("siteAVMBootstrap"); + } + } + + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + this.ctx = applicationContext; + } + + public void startTenants() + { + checkProperties(); + + AuthenticationUtil.setMtEnabled(true); + + // initialise the tenant admin service and status of tenants (using attribute service) + // note: this requires that the repository schema has already been initialised + + // register dictionary - to allow enable/disable tenant callbacks + register(dictionaryComponent); + + // register file store - to allow enable/disable tenant callbacks + register(tenantFileContentStore); + + UserTransaction userTransaction = transactionService.getUserTransaction(); + + try + { + authenticationComponent.setSystemUserAsCurrentUser(); + userTransaction.begin(); + + // bootstrap Tenant Service internal cache + List tenants = getAllTenants(); + + int enabledCount = 0; + int disabledCount = 0; + + for (Tenant tenant : tenants) + { + if (tenant.isEnabled()) + { + // this will also call tenant deployers registered so far ... + enableTenant(tenant.getTenantDomain(), true); + enabledCount++; + } + else + { + // explicitly disable, without calling disableTenant callback + disableTenant(tenant.getTenantDomain(), false); + disabledCount++; + } + } + + tenantService.register(this); // callback to refresh tenantStatus cache + + userTransaction.commit(); + + if (logger.isInfoEnabled()) + { + logger.info(String.format("Alfresco Multi-Tenant startup - %d enabled tenants, %d disabled tenants", + enabledCount, disabledCount)); + } + } + catch(Throwable e) + { + // rollback the transaction + try { if (userTransaction != null) {userTransaction.rollback();} } catch (Exception ex) {} + throw new AlfrescoRuntimeException("Failed to bootstrap tenants", e); + } + finally + { + authenticationComponent.clearCurrentSecurityContext(); + } + } + + public void stopTenants() + { + tenantDeployers.clear(); + tenantDeployers = null; + } + + /** + * @see TenantAdminService.createTenant() + */ + public void createTenant(final String tenantDomain, final char[] tenantAdminRawPassword) + { + createTenant(tenantDomain, tenantAdminRawPassword, null); + } + + /** + * @see TenantAdminService.createTenant() + */ + public void createTenant(final String tenantDomain, final char[] tenantAdminRawPassword, String rootContentStoreDir) + { + ParameterCheck.mandatory("tenantAdminRawPassword", tenantAdminRawPassword); + + initTenant(tenantDomain, rootContentStoreDir); + + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + dictionaryComponent.init(); + tenantFileContentStore.init(); + + // create tenant-specific stores + ImporterBootstrap userImporterBootstrap = (ImporterBootstrap)ctx.getBean("userBootstrap"); + bootstrapUserTenantStore(userImporterBootstrap, tenantDomain, tenantAdminRawPassword); + + ImporterBootstrap systemImporterBootstrap = (ImporterBootstrap)ctx.getBean("systemBootstrap"); + bootstrapSystemTenantStore(systemImporterBootstrap, tenantDomain); + + // deprecated + ImporterBootstrap versionImporterBootstrap = (ImporterBootstrap)ctx.getBean("versionBootstrap"); + bootstrapVersionTenantStore(versionImporterBootstrap, tenantDomain); + + ImporterBootstrap version2ImporterBootstrap = (ImporterBootstrap)ctx.getBean("version2Bootstrap"); + bootstrapVersionTenantStore(version2ImporterBootstrap, tenantDomain); + + ImporterBootstrap spacesArchiveImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesArchiveBootstrap"); + bootstrapSpacesArchiveTenantStore(spacesArchiveImporterBootstrap, tenantDomain); + + ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesBootstrap"); + bootstrapSpacesTenantStore(spacesImporterBootstrap, tenantDomain); + + siteAVMBootstrap.bootstrap(); + + // notify listeners that tenant has been created & hence enabled + for (TenantDeployer tenantDeployer : tenantDeployers) + { + tenantDeployer.onEnableTenant(); + } + + // bootstrap workflows + for (WorkflowDeployer workflowDeployer : workflowDeployers) + { + workflowDeployer.init(); + } + + // bootstrap modules (if any) + moduleService.startModules(); + + return null; + } + }, getSystemUser(tenantDomain)); + + logger.info("Tenant created: " + tenantDomain); + } + + /** + * Export tenant - equivalent to the tenant admin running a 'complete repo' export from the Web Client Admin + */ + public void exportTenant(final String tenantDomain, final File directoryDestination) + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + repositoryExporterService.export(directoryDestination, tenantDomain); + return null; + } + }, getSystemUser(tenantDomain)); + + logger.info("Tenant exported: " + tenantDomain); + } + + /** + * Create tenant by restoring from a complete repository export. This is equivalent to a bootstrap import using restore-context.xml. + */ + public void importTenant(final String tenantDomain, final File directorySource, String rootContentStoreDir) + { + initTenant(tenantDomain, rootContentStoreDir); + + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + dictionaryComponent.init(); + tenantFileContentStore.init(); + + // import tenant-specific stores + importBootstrapUserTenantStore(tenantDomain, directorySource); + importBootstrapSystemTenantStore(tenantDomain, directorySource); + importBootstrapVersionTenantStore(tenantDomain, directorySource); + importBootstrapSpacesArchiveTenantStore(tenantDomain, directorySource); + importBootstrapSpacesModelsTenantStore(tenantDomain, directorySource); + importBootstrapSpacesTenantStore(tenantDomain, directorySource); + + // notify listeners that tenant has been created & hence enabled + for (TenantDeployer tenantDeployer : tenantDeployers) + { + tenantDeployer.onEnableTenant(); + } + + // bootstrap workflows + for (WorkflowDeployer workflowDeployer : workflowDeployers) + { + workflowDeployer.init(); + } + + // bootstrap modules (if any) + moduleService.startModules(); + + return null; + } + }, getSystemUser(tenantDomain)); + + logger.info("Tenant imported: " + tenantDomain); + } + + public boolean existsTenant(String tenantDomain) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("tenantDomain", tenantDomain); + + return (getTenantAttributes(tenantDomain) != null); + } + + private void putTenantAttributes(String tenantDomain, Tenant tenant) + { + if (! attributeService.exists(TENANTS_ATTRIBUTE_PATH)) + { + // bootstrap + attributeService.setAttribute("", TENANTS_ATTRIBUTE_PATH, new MapAttributeValue()); + } + + MapAttribute tenantProps = new MapAttributeValue(); + tenantProps.put(TENANT_ATTRIBUTE_ENABLED, new BooleanAttributeValue(tenant.isEnabled())); + tenantProps.put(TENANT_ROOT_CONTENT_STORE_DIR, new StringAttributeValue(tenant.getRootContentStoreDir())); + + attributeService.setAttribute(TENANTS_ATTRIBUTE_PATH, tenantDomain, tenantProps); + + // update tenant status cache + ((MultiTServiceImpl)tenantService).putTenant(tenantDomain, tenant); + } + + private Tenant getTenantAttributes(String tenantDomain) + { + if (attributeService.exists(TENANTS_ATTRIBUTE_PATH+"/"+tenantDomain)) + { + MapAttribute map = (MapAttribute)attributeService.getAttribute(TENANTS_ATTRIBUTE_PATH+"/"+tenantDomain); + if (map != null) + { + return new Tenant(tenantDomain, + map.get(TENANT_ATTRIBUTE_ENABLED).getBooleanValue(), + map.get(TENANT_ROOT_CONTENT_STORE_DIR).getStringValue()); + } + } + + return null; + } + + public void enableTenant(String tenantDomain) + { + if (! existsTenant(tenantDomain)) + { + throw new RuntimeException("Tenant does not exist: " + tenantDomain); + } + + if (isEnabledTenant(tenantDomain)) + { + logger.warn("Tenant already enabled: " + tenantDomain); + } + + enableTenant(tenantDomain, true); + } + + private void enableTenant(String tenantDomain, boolean notifyTenantDeployers) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("tenantDomain", tenantDomain); + + Tenant tenant = getTenantAttributes(tenantDomain); + tenant = new Tenant(tenantDomain, true, tenant.getRootContentStoreDir()); // enable + putTenantAttributes(tenantDomain, tenant); + + if (notifyTenantDeployers) + { + // notify listeners that tenant has been enabled + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + for (TenantDeployer tenantDeployer : tenantDeployers) + { + tenantDeployer.onEnableTenant(); + } + return null; + } + }, getSystemUser(tenantDomain)); + } + + logger.info("Tenant enabled: " + tenantDomain); + } + + public void disableTenant(String tenantDomain) + { + if (! existsTenant(tenantDomain)) + { + throw new RuntimeException("Tenant does not exist: " + tenantDomain); + } + + if (! isEnabledTenant(tenantDomain)) + { + logger.warn("Tenant already disabled: " + tenantDomain); + } + + disableTenant(tenantDomain, true); + } + + public void disableTenant(String tenantDomain, boolean notifyTenantDeployers) + { + if (notifyTenantDeployers) + { + // notify listeners that tenant has been disabled + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + for (TenantDeployer tenantDeployer : tenantDeployers) + { + tenantDeployer.onDisableTenant(); + } + return null; + } + }, getSystemUser(tenantDomain)); + } + + // update tenant attributes / tenant cache - need to disable after notifying listeners (else they cannot disable) + Tenant tenant = getTenantAttributes(tenantDomain); + tenant = new Tenant(tenantDomain, false, tenant.getRootContentStoreDir()); // disable + putTenantAttributes(tenantDomain, tenant); + + logger.info("Tenant disabled: " + tenantDomain); + } + + public boolean isEnabledTenant(String tenantDomain) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("tenantDomain", tenantDomain); + + Tenant tenant = getTenantAttributes(tenantDomain); + if (tenant != null) + { + return tenant.isEnabled(); + } + + return false; + } + + protected String getRootContentStoreDir(String tenantDomain) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("tenantDomain", tenantDomain); + + Tenant tenant = getTenantAttributes(tenantDomain); + if (tenant != null) + { + return tenant.getRootContentStoreDir(); + } + + return null; + } + + protected void putRootContentStoreDir(String tenantDomain, String rootContentStoreDir) + { + Tenant tenant = getTenantAttributes(tenantDomain); + tenant = new Tenant(tenantDomain, tenant.isEnabled(), rootContentStoreDir); + putTenantAttributes(tenantDomain, tenant); + } + + public Tenant getTenant(String tenantDomain) + { + if (! existsTenant(tenantDomain)) + { + throw new RuntimeException("Tenant does not exist: " + tenantDomain); + } + + return new Tenant(tenantDomain, isEnabledTenant(tenantDomain), getRootContentStoreDir(tenantDomain)); + } + + /** + * @see TenantAdminService.deleteTenant() + */ + public void deleteTenant(String tenantDomain) + { + if (! existsTenant(tenantDomain)) + { + throw new RuntimeException("Tenant does not exist: " + tenantDomain); + } + else + { + try + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + List workflowDefs = workflowService.getDefinitions(); + if (workflowDefs != null) + { + for (WorkflowDefinition workflowDef : workflowDefs) + { + workflowService.undeployDefinition(workflowDef.getId()); + } + } + + List messageResourceBundles = repoAdminService.getMessageBundles(); + if (messageResourceBundles != null) + { + for (String messageResourceBundle : messageResourceBundles) + { + repoAdminService.undeployMessageBundle(messageResourceBundle); + } + } + + List models = repoAdminService.getModels(); + if (models != null) + { + for (RepoModelDefinition model : models) + { + repoAdminService.undeployModel(model.getRepoName()); + } + } + + return null; + } + }, getSystemUser(tenantDomain)); + + final String tenantAdminUser = getTenantAdminUser(tenantDomain); + + // delete tenant-specific stores + nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_SPACES))); + nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_ARCHIVE, STORE_BASE_ID_SPACES))); + nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_VERSION1))); + nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_VERSION2))); + nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_SYSTEM, STORE_BASE_ID_SYSTEM))); + nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_USER, STORE_BASE_ID_USER))); + + + // notify listeners that tenant has been deleted & hence disabled + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + for (TenantDeployer tenantDeployer : tenantDeployers) + { + tenantDeployer.onDisableTenant(); + } + return null; + } + }, getSystemUser(tenantDomain)); + + // remove tenant + attributeService.removeAttribute(TENANTS_ATTRIBUTE_PATH, tenantDomain); + } + catch (Throwable t) + { + throw new AlfrescoRuntimeException("Failed to delete tenant: " + tenantDomain, t); + } + } + } + + /** + * @see TenantAdminService.getAllTenants() + */ + public List getAllTenants() + { + MapAttribute map = (MapAttribute)attributeService.getAttribute(TENANTS_ATTRIBUTE_PATH); + + List tenants = new ArrayList(); + + if (map != null) + { + // note: getAllTenants is called first, by TenantDeployer - hence need to initialise the TenantService status cache + Set tenantDomains = map.keySet(); + + for (String tenantDomain : tenantDomains) + { + Tenant tenant = getTenantAttributes(tenantDomain); + tenants.add(new Tenant(tenantDomain, tenant.isEnabled(), tenant.getRootContentStoreDir())); + } + } + + return tenants; // list of tenants or empty list + } + + private void importBootstrapSystemTenantStore(String tenantDomain, File directorySource) + { + // Import Bootstrap (restore) Tenant-Specific Version Store + Properties bootstrapView = new Properties(); + bootstrapView.put("path", "/"); + bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_system.acp"); + + List bootstrapViews = new ArrayList(1); + bootstrapViews.add(bootstrapView); + + ImporterBootstrap systemImporterBootstrap = (ImporterBootstrap)ctx.getBean("systemBootstrap"); + systemImporterBootstrap.setBootstrapViews(bootstrapViews); + systemImporterBootstrap.setLog(true); + + bootstrapSystemTenantStore(systemImporterBootstrap, tenantDomain); + } + + private void bootstrapSystemTenantStore(ImporterBootstrap systemImporterBootstrap, String tenantDomain) + { + // Bootstrap Tenant-Specific System Store + StoreRef bootstrapStoreRef = systemImporterBootstrap.getStoreRef(); + StoreRef tenantBootstrapStoreRef = new StoreRef(bootstrapStoreRef.getProtocol(), tenantService.getName(bootstrapStoreRef.getIdentifier(), tenantDomain)); + systemImporterBootstrap.setStoreUrl(tenantBootstrapStoreRef.toString()); + + // override default property (workspace://SpacesStore) + List mustNotExistStoreUrls = new ArrayList(); + mustNotExistStoreUrls.add(new StoreRef(PROTOCOL_STORE_WORKSPACE, tenantService.getName(STORE_BASE_ID_USER, tenantDomain)).toString()); + systemImporterBootstrap.setMustNotExistStoreUrls(mustNotExistStoreUrls); + + systemImporterBootstrap.bootstrap(); + + // reset since systemImporter is singleton (hence reused) + systemImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); + + logger.debug("Bootstrapped store: " + tenantService.getBaseName(tenantBootstrapStoreRef)); + } + + private void importBootstrapUserTenantStore(String tenantDomain, File directorySource) + { + // Import Bootstrap (restore) Tenant-Specific User Store + Properties bootstrapView = new Properties(); + bootstrapView.put("path", "/"); + bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_users.acp"); + + List bootstrapViews = new ArrayList(1); + bootstrapViews.add(bootstrapView); + + ImporterBootstrap userImporterBootstrap = (ImporterBootstrap)ctx.getBean("userBootstrap"); + userImporterBootstrap.setBootstrapViews(bootstrapViews); + userImporterBootstrap.setLog(true); + + bootstrapUserTenantStore(userImporterBootstrap, tenantDomain, null); + } + + private void bootstrapUserTenantStore(ImporterBootstrap userImporterBootstrap, String tenantDomain, char[] tenantAdminRawPassword) + { + // Bootstrap Tenant-Specific User Store + StoreRef bootstrapStoreRef = userImporterBootstrap.getStoreRef(); + bootstrapStoreRef = new StoreRef(bootstrapStoreRef.getProtocol(), tenantService.getName(bootstrapStoreRef.getIdentifier(), tenantDomain)); + userImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); + + // override admin username property + Properties props = userImporterBootstrap.getConfiguration(); + props.put("alfresco_user_store.adminusername", getTenantAdminUser(tenantDomain)); + + if (tenantAdminRawPassword != null) + { + String salt = null; // GUID.generate(); + props.put("alfresco_user_store.adminpassword", passwordEncoder.encodePassword(new String(tenantAdminRawPassword), salt)); + } + + userImporterBootstrap.bootstrap(); + + logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); + } + + private void importBootstrapVersionTenantStore(String tenantDomain, File directorySource) + { + // Import Bootstrap (restore) Tenant-Specific Version Store + Properties bootstrapView = new Properties(); + bootstrapView.put("path", "/"); + bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_versions2.acp"); + + List bootstrapViews = new ArrayList(1); + bootstrapViews.add(bootstrapView); + + ImporterBootstrap versionImporterBootstrap = (ImporterBootstrap)ctx.getBean("versionBootstrap"); + versionImporterBootstrap.setBootstrapViews(bootstrapViews); + versionImporterBootstrap.setLog(true); + + bootstrapVersionTenantStore(versionImporterBootstrap, tenantDomain); + } + + private void bootstrapVersionTenantStore(ImporterBootstrap versionImporterBootstrap, String tenantDomain) + { + // Bootstrap Tenant-Specific Version Store + StoreRef bootstrapStoreRef = versionImporterBootstrap.getStoreRef(); + bootstrapStoreRef = new StoreRef(bootstrapStoreRef.getProtocol(), tenantService.getName(bootstrapStoreRef.getIdentifier(), tenantDomain)); + versionImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); + + versionImporterBootstrap.bootstrap(); + + logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); + } + + private void importBootstrapSpacesArchiveTenantStore(String tenantDomain, File directorySource) + { + // Import Bootstrap (restore) Tenant-Specific Spaces Archive Store + Properties bootstrapView = new Properties(); + bootstrapView.put("path", "/"); + bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_spaces_archive.acp"); + + List bootstrapViews = new ArrayList(1); + bootstrapViews.add(bootstrapView); + + ImporterBootstrap spacesArchiveImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesArchiveBootstrap"); + spacesArchiveImporterBootstrap.setBootstrapViews(bootstrapViews); + spacesArchiveImporterBootstrap.setLog(true); + + bootstrapSpacesArchiveTenantStore(spacesArchiveImporterBootstrap, tenantDomain); + } + + private void bootstrapSpacesArchiveTenantStore(ImporterBootstrap spacesArchiveImporterBootstrap, String tenantDomain) + { + // Bootstrap Tenant-Specific Spaces Archive Store + StoreRef bootstrapStoreRef = spacesArchiveImporterBootstrap.getStoreRef(); + bootstrapStoreRef = new StoreRef(bootstrapStoreRef.getProtocol(), tenantService.getName(bootstrapStoreRef.getIdentifier(), tenantDomain)); + spacesArchiveImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); + + // override default property (archive://SpacesStore) + List mustNotExistStoreUrls = new ArrayList(); + mustNotExistStoreUrls.add(bootstrapStoreRef.toString()); + spacesArchiveImporterBootstrap.setMustNotExistStoreUrls(mustNotExistStoreUrls); + + spacesArchiveImporterBootstrap.bootstrap(); + + logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); + } + + private void importBootstrapSpacesModelsTenantStore(String tenantDomain, File directorySource) + { + // Import Bootstrap (restore) Tenant-Specific Spaces Store + Properties bootstrapView = new Properties(); + bootstrapView.put("path", "/"); + bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_models.acp"); + + List bootstrapViews = new ArrayList(1); + bootstrapViews.add(bootstrapView); + + ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesBootstrap"); + spacesImporterBootstrap.setBootstrapViews(bootstrapViews); + spacesImporterBootstrap.setLog(true); + + bootstrapSpacesTenantStore(spacesImporterBootstrap, tenantDomain); + } + + private void importBootstrapSpacesTenantStore(String tenantDomain, File directorySource) + { + // Import Bootstrap (restore) Tenant-Specific Spaces Store + Properties bootstrapView = new Properties(); + bootstrapView.put("path", "/"); + bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_spaces.acp"); + bootstrapView.put("uuidBinding", "UPDATE_EXISTING"); + + List bootstrapViews = new ArrayList(1); + bootstrapViews.add(bootstrapView); + + ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesBootstrap"); + spacesImporterBootstrap.setBootstrapViews(bootstrapViews); + spacesImporterBootstrap.setLog(true); + + spacesImporterBootstrap.setUseExistingStore(true); + + bootstrapSpacesTenantStore(spacesImporterBootstrap, tenantDomain); + } + + private void bootstrapSpacesTenantStore(ImporterBootstrap spacesImporterBootstrap, String tenantDomain) + { + // Bootstrap Tenant-Specific Spaces Store + StoreRef bootstrapStoreRef = spacesImporterBootstrap.getStoreRef(); + bootstrapStoreRef = new StoreRef(bootstrapStoreRef.getProtocol(), tenantService.getName(bootstrapStoreRef.getIdentifier(), tenantDomain)); + spacesImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); + + // override admin username property + Properties props = spacesImporterBootstrap.getConfiguration(); + props.put("alfresco_user_store.adminusername", getTenantAdminUser(tenantDomain)); + + // override guest username property + props.put("alfresco_user_store.guestusername", getTenantGuestUser(tenantDomain)); + + spacesImporterBootstrap.bootstrap(); + + /* TODO - pending fix for ETHREEOH-283 + // calculate any missing usages + UserUsageTrackingComponent userUsageTrackingComponent = (UserUsageTrackingComponent)ctx.getBean(UserUsageBootstrapJob.KEY_COMPONENT); + userUsageTrackingComponent.bootstrapInternal(); + */ + + logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); + } + + public void deployTenants(final TenantDeployer deployer, Log logger) + { + if (deployer == null) + { + throw new AlfrescoRuntimeException("Deployer must be provided"); + } + if (logger == null) + { + throw new AlfrescoRuntimeException("Logger must be provided"); + } + + if (tenantService.isEnabled()) + { + UserTransaction userTransaction = transactionService.getUserTransaction(); + authenticationComponent.setSystemUserAsCurrentUser(); + + List tenants = null; + try + { + userTransaction.begin(); + tenants = getAllTenants(); + userTransaction.commit(); + } + catch(Throwable e) + { + // rollback the transaction + try { if (userTransaction != null) {userTransaction.rollback();} } catch (Exception ex) {} + throw new AlfrescoRuntimeException("Failed to get tenants", e); + } + finally + { + authenticationComponent.clearCurrentSecurityContext(); + } + + for (Tenant tenant : tenants) + { + if (tenant.isEnabled()) + { + try + { + // switch to admin in order to deploy within context of tenant domain + // assumes each tenant has default "admin" user + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + // init the service within tenant context + deployer.init(); + return null; + } + }, getSystemUser(tenant.getTenantDomain())); + + } + catch (Throwable e) + { + logger.error("Deployment failed" + e); + + StringWriter stringWriter = new StringWriter(); + e.printStackTrace(new PrintWriter(stringWriter)); + logger.error(stringWriter.toString()); + + // tenant deploy failure should not necessarily affect other tenants + } + } + } + } + } + + public void undeployTenants(final TenantDeployer deployer, Log logger) + { + if (deployer == null) + { + throw new AlfrescoRuntimeException("Deployer must be provided"); + } + if (logger == null) + { + throw new AlfrescoRuntimeException("Logger must be provided"); + } + + if (tenantService.isEnabled()) + { + UserTransaction userTransaction = transactionService.getUserTransaction(); + authenticationComponent.setSystemUserAsCurrentUser(); + + List tenants = null; + try + { + userTransaction.begin(); + tenants = getAllTenants(); + userTransaction.commit(); + } + catch(Throwable e) + { + // rollback the transaction + try { if (userTransaction != null) {userTransaction.rollback();} } catch (Exception ex) {} + try {authenticationComponent.clearCurrentSecurityContext(); } catch (Exception ex) {} + throw new AlfrescoRuntimeException("Failed to get tenants", e); + } + + try + { + AuthenticationUtil.pushAuthentication(); + for (Tenant tenant : tenants) + { + if (tenant.isEnabled()) + { + try + { + // switch to admin in order to deploy within context of tenant domain + // assumes each tenant has default "admin" user + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + // destroy the service within tenant context + deployer.destroy(); + return null; + } + }, getSystemUser(tenant.getTenantDomain())); + + } + catch (Throwable e) + { + logger.error("Undeployment failed" + e); + + StringWriter stringWriter = new StringWriter(); + e.printStackTrace(new PrintWriter(stringWriter)); + logger.error(stringWriter.toString()); + + // tenant undeploy failure should not necessarily affect other tenants + } + } + } + } + finally + { + AuthenticationUtil.popAuthentication(); + } + } + } + + public void register(TenantDeployer deployer) + { + if (deployer == null) + { + throw new AlfrescoRuntimeException("TenantDeployer must be provided"); + } + + if (! tenantDeployers.contains(deployer)) + { + tenantDeployers.add(deployer); + } + } + + public void unregister(TenantDeployer deployer) + { + if (deployer == null) + { + throw new AlfrescoRuntimeException("TenantDeployer must be provided"); + } + + if (tenantDeployers != null) + { + tenantDeployers.remove(deployer); + } + } + + public void register(WorkflowDeployer workflowDeployer) + { + if (workflowDeployer == null) + { + throw new AlfrescoRuntimeException("WorkflowDeployer must be provided"); + } + + if (! workflowDeployers.contains(workflowDeployer)) + { + workflowDeployers.add(workflowDeployer); + } + } + + public void resetCache(String tenantDomain) + { + if (existsTenant(tenantDomain)) + { + if (isEnabledTenant(tenantDomain)) + { + enableTenant(tenantDomain); + } + else + { + disableTenant(tenantDomain); + } + } + else + { + throw new AlfrescoRuntimeException("No such tenant " + tenantDomain); + } + } + + private void initTenant(String tenantDomain, String rootContentStoreDir) + { + validateTenantName(tenantDomain); + + if (existsTenant(tenantDomain)) + { + throw new AlfrescoRuntimeException("Tenant already exists: " + tenantDomain); + } + + if (rootContentStoreDir == null) + { + rootContentStoreDir = tenantFileContentStore.getDefaultRootDir(); + } + else + { + File tenantRootDir = new File(rootContentStoreDir); + if ((tenantRootDir.exists()) && (tenantRootDir.list().length != 0)) + { + throw new AlfrescoRuntimeException("Tenant root directory is not empty: " + rootContentStoreDir); + } + } + + // init - need to enable tenant (including tenant service) before stores bootstrap + Tenant tenant = new Tenant(tenantDomain, true, rootContentStoreDir); + putTenantAttributes(tenantDomain, tenant); + } + + private void validateTenantName(String tenantDomain) + { + ParameterCheck.mandatory("tenantDomain", tenantDomain); + + if (tenantDomain.length() > MAX_LEN) + { + throw new IllegalArgumentException(tenantDomain + " is not a valid tenant name (must be less than " + MAX_LEN + " characters)"); + } + + if (! Pattern.matches(REGEX_CONTAINS_ALPHA, tenantDomain)) + { + throw new IllegalArgumentException(tenantDomain + " is not a valid tenant name (must contain at least one alpha character)"); + } + + String[] dnsLabels = tenantDomain.split("\\."); + if (dnsLabels.length != 0) + { + for (int i = 0; i < dnsLabels.length; i++) + { + if (! Pattern.matches(REGEX_VALID_DNS_LABEL, dnsLabels[i])) + { + throw new IllegalArgumentException(dnsLabels[i] + " is not a valid DNS label (must match " + REGEX_VALID_DNS_LABEL + ")"); + } + } + } + else + { + if (! Pattern.matches(REGEX_VALID_DNS_LABEL, tenantDomain)) + { + throw new IllegalArgumentException(tenantDomain + " is not a valid DNS label (must match " + REGEX_VALID_DNS_LABEL + ")"); + } + } + } + + // tenant deployer/user services delegated to tenant service + + public boolean isEnabled() + { + return tenantService.isEnabled(); + } + + public String getCurrentUserDomain() + { + return tenantService.getCurrentUserDomain(); + } + + public String getUserDomain(String username) + { + return tenantService.getUserDomain(username); + } + + public String getBaseNameUser(String username) + { + return tenantService.getBaseNameUser(username); + } + + public String getDomainUser(String baseUsername, String tenantDomain) + { + return tenantService.getDomainUser(baseUsername, tenantDomain); + } + + public String getDomain(String name) + { + return tenantService.getDomain(name); + } + + // local helpers + + private String getSystemUser(String tenantDomain) + { + return tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain); + } + + private String getTenantAdminUser(String tenantDomain) + { + return tenantService.getDomainUser(ADMIN_BASENAME, tenantDomain); + } + + private String getTenantGuestUser(String tenantDomain) + { + return tenantService.getDomainUser(authenticationComponent.getGuestUserName(), tenantDomain); + } +} diff --git a/source/java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptor.java b/source/java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptor.java index cdaa4c7dd8..4efd12ad5e 100644 --- a/source/java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptor.java +++ b/source/java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptor.java @@ -1,173 +1,173 @@ -/* - * Copyright (C) 2005-2008 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.tenant; - +/* + * Copyright (C) 2005-2009 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.tenant; + import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; -import java.util.List; +import java.util.List; import java.util.Map; -import java.util.Set; - +import java.util.Set; + import org.alfresco.model.ContentModel; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.service.cmr.repository.AssociationRef; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.Path; -import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.QName; -import org.alfresco.util.EqualsHelper; -import org.aopalliance.intercept.MethodInvocation; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.aop.support.DelegatingIntroductionInterceptor; - -/** - * Interceptor to translate Node - * - * @since 3.0 - * @author Derek Hulley - * @author janv - */ -public class MultiTNodeServiceInterceptor extends DelegatingIntroductionInterceptor//implements NodeService -{ - private static final long serialVersionUID = -5462852271914961462L; - - private static Log logger = LogFactory.getLog(MultiTNodeServiceInterceptor.class); - - private static ThreadLocal ignoreMT = new ThreadLocal(); - - private TenantService tenantService; - - /** - * Change the filtering behaviour of this interceptor on the current thread. - * Use this to switch off the filtering and pass references in and out as they - * are found. - * - * @param ignoreMT true if the current thread is able to handle - * MultiTenant-enabled references. - * @return true if the current transaction is MT aware - */ - public static boolean setIgnoreMT(boolean ignoreMT) - { - boolean wasIgnoreMT = isIgnoreMT(); - MultiTNodeServiceInterceptor.ignoreMT.set(Boolean.valueOf(ignoreMT)); - return wasIgnoreMT; - } - - /** - * @return Returns true if the current thread has marked itself - * as being able to handle MultiTenant references. - */ - static public boolean isIgnoreMT() - { - if (ignoreMT.get() == null) - { - return false; - } - else - { - return ignoreMT.get(); - } - } - - public void setTenantService(TenantService tenantService) - { - this.tenantService = tenantService; - } - - @SuppressWarnings("unchecked") - public Object invoke(MethodInvocation invocation) throws Throwable - { +import org.alfresco.util.EqualsHelper; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.aop.support.DelegatingIntroductionInterceptor; + +/** + * Interceptor to translate Node + * + * @since 3.0 + * @author Derek Hulley + * @author janv + */ +public class MultiTNodeServiceInterceptor extends DelegatingIntroductionInterceptor//implements NodeService +{ + private static final long serialVersionUID = -5462852271914961462L; + + private static Log logger = LogFactory.getLog(MultiTNodeServiceInterceptor.class); + + private static ThreadLocal ignoreMT = new ThreadLocal(); + + private TenantService tenantService; + + /** + * Change the filtering behaviour of this interceptor on the current thread. + * Use this to switch off the filtering and pass references in and out as they + * are found. + * + * @param ignoreMT true if the current thread is able to handle + * MultiTenant-enabled references. + * @return true if the current transaction is MT aware + */ + public static boolean setIgnoreMT(boolean ignoreMT) + { + boolean wasIgnoreMT = isIgnoreMT(); + MultiTNodeServiceInterceptor.ignoreMT.set(Boolean.valueOf(ignoreMT)); + return wasIgnoreMT; + } + + /** + * @return Returns true if the current thread has marked itself + * as being able to handle MultiTenant references. + */ + static public boolean isIgnoreMT() + { + if (ignoreMT.get() == null) + { + return false; + } + else + { + return ignoreMT.get(); + } + } + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + @SuppressWarnings("unchecked") + public Object invoke(MethodInvocation invocation) throws Throwable + { // See if we can shortcut (for super 'System' only) if (AuthenticationUtil.getSystemUserName().equals(AuthenticationUtil.getRunAsUser()) || !AuthenticationUtil.isMtEnabled()) - { - return invocation.proceed(); - } - - String methodName = invocation.getMethod().getName(); - - if (logger.isDebugEnabled()) - { - logger.debug("Intercepting method " + methodName); - } - - Object[] args = invocation.getArguments(); - // Convert each of the arguments to the underlying (full) reference. - for (int i = 0; i < args.length; i++) - { - Object arg = args[i]; - Object newArg = arg; - if (arg == null) - { - // No conversion possible - } - if (arg instanceof StoreRef) - { - StoreRef ref = (StoreRef) arg; - newArg = tenantService.getName(ref); - } - else if (arg instanceof NodeRef) - { - NodeRef ref = (NodeRef) arg; - newArg = tenantService.getName(ref); - } - else if (arg instanceof ChildAssociationRef) - { - ChildAssociationRef ref = (ChildAssociationRef) arg; - newArg = tenantService.getName(ref); - } - else if (arg instanceof AssociationRef) - { - AssociationRef ref = (AssociationRef) arg; - newArg = tenantService.getName(ref); - } - - if (logger.isDebugEnabled()) - { - if (!EqualsHelper.nullSafeEquals(newArg, arg)) - { - logger.debug( - "Argument converted: \n" + - " Before: " + arg + "\n" + - " After: " + newArg); - } - } - - // Substitute the new value - args[i] = newArg; - } - - // Make the call - Object ret = invocation.proceed(); - + { + return invocation.proceed(); + } + + String methodName = invocation.getMethod().getName(); + + if (logger.isDebugEnabled()) + { + logger.debug("Intercepting method " + methodName); + } + + Object[] args = invocation.getArguments(); + // Convert each of the arguments to the underlying (full) reference. + for (int i = 0; i < args.length; i++) + { + Object arg = args[i]; + Object newArg = arg; + if (arg == null) + { + // No conversion possible + } + if (arg instanceof StoreRef) + { + StoreRef ref = (StoreRef) arg; + newArg = tenantService.getName(ref); + } + else if (arg instanceof NodeRef) + { + NodeRef ref = (NodeRef) arg; + newArg = tenantService.getName(ref); + } + else if (arg instanceof ChildAssociationRef) + { + ChildAssociationRef ref = (ChildAssociationRef) arg; + newArg = tenantService.getName(ref); + } + else if (arg instanceof AssociationRef) + { + AssociationRef ref = (AssociationRef) arg; + newArg = tenantService.getName(ref); + } + + if (logger.isDebugEnabled()) + { + if (!EqualsHelper.nullSafeEquals(newArg, arg)) + { + logger.debug( + "Argument converted: \n" + + " Before: " + arg + "\n" + + " After: " + newArg); + } + } + + // Substitute the new value + args[i] = newArg; + } + + // Make the call + Object ret = invocation.proceed(); + if (methodName.equals("getProperty")) - { + { if (ret != null) { // Convert the outbound value @@ -213,135 +213,135 @@ public class MultiTNodeServiceInterceptor extends DelegatingIntroductionIntercep } else if (methodName.equals("getStores")) { - if ((ret == null) || (! (ret instanceof List))) - { - return null; - } - - List rawValues = (List)ret; - final List convertedValues = new ArrayList(rawValues.size()); - - for (StoreRef ref : rawValues) - { - StoreRef storeRef = ref; - try - { - if (tenantService.isEnabled()) - { - // MT: return tenant stores only (although for super System return all stores - as used by - // ConfigurationChecker, IndexRecovery, IndexBackup etc) + if ((ret == null) || (! (ret instanceof List))) + { + return null; + } + + List rawValues = (List)ret; + final List convertedValues = new ArrayList(rawValues.size()); + + for (StoreRef ref : rawValues) + { + StoreRef storeRef = ref; + try + { + if (tenantService.isEnabled() && (! storeRef.getProtocol().equals(StoreRef.PROTOCOL_AVM))) + { + // MT: return tenant stores only (although for super System return all stores - as used by + // ConfigurationChecker, IndexRecovery, IndexBackup etc) if (!AuthenticationUtil.isRunAsUserTheSystemUser()) - { - tenantService.checkDomain(storeRef.getIdentifier()); - storeRef = tenantService.getBaseName(storeRef); - } - } - - convertedValues.add(storeRef); - } - catch (RuntimeException re) - { - // deliberately ignore - stores in different domain will not be listed - } - } - - return convertedValues; - } - else - { - // Convert the outbound value - ret = convertOutboundValue(ret); - } - - // done - return ret; - } - - /** - * Convert outbound collection to spoofed (no tenant prefix) values. - */ - private Collection convertOutboundValues(Collection rawValues) - { - /* - * Return types can be Lists or Sets, so cater for both. - */ - final Collection convertedValues; - if (rawValues instanceof List) - { - convertedValues = new ArrayList(rawValues.size()); - } - else if (rawValues instanceof Set) - { - convertedValues = new HashSet(rawValues.size(), 1.0F); - } - else - { - throw new IllegalArgumentException("Interceptor can only handle List and Set return types."); - } - - for (Object rawValue : rawValues) - { - Object convertedValue = convertOutboundValue(rawValue); - convertedValues.add(convertedValue); - } - // Done - return convertedValues; - } - - /** - * Convert outbound single value to spoofed (no tenant prefix) value. - */ - @SuppressWarnings("unchecked") - private Object convertOutboundValue(Object rawValue) - { - if (rawValue == null) - { - return null; - } - - // Deal with collections - Object value = rawValue; - if (rawValue instanceof Collection) - { - value = convertOutboundValues((Collection)rawValue); - } - else if (rawValue instanceof StoreRef) - { - StoreRef ref = (StoreRef) rawValue; + { + tenantService.checkDomain(storeRef.getIdentifier()); + storeRef = tenantService.getBaseName(storeRef); + } + } + + convertedValues.add(storeRef); + } + catch (RuntimeException re) + { + // deliberately ignore - stores in different domain will not be listed + } + } + + return convertedValues; + } + else + { + // Convert the outbound value + ret = convertOutboundValue(ret); + } + + // done + return ret; + } + + /** + * Convert outbound collection to spoofed (no tenant prefix) values. + */ + private Collection convertOutboundValues(Collection rawValues) + { + /* + * Return types can be Lists or Sets, so cater for both. + */ + final Collection convertedValues; + if (rawValues instanceof List) + { + convertedValues = new ArrayList(rawValues.size()); + } + else if (rawValues instanceof Set) + { + convertedValues = new HashSet(rawValues.size(), 1.0F); + } + else + { + throw new IllegalArgumentException("Interceptor can only handle List and Set return types."); + } + + for (Object rawValue : rawValues) + { + Object convertedValue = convertOutboundValue(rawValue); + convertedValues.add(convertedValue); + } + // Done + return convertedValues; + } + + /** + * Convert outbound single value to spoofed (no tenant prefix) value. + */ + @SuppressWarnings("unchecked") + private Object convertOutboundValue(Object rawValue) + { + if (rawValue == null) + { + return null; + } + + // Deal with collections + Object value = rawValue; + if (rawValue instanceof Collection) + { + value = convertOutboundValues((Collection)rawValue); + } + else if (rawValue instanceof StoreRef) + { + StoreRef ref = (StoreRef) rawValue; value = tenantService.getBaseName(ref); - } - else if (rawValue instanceof NodeRef) - { - NodeRef ref = (NodeRef) rawValue; - value = tenantService.getBaseName(ref); - } - else if (rawValue instanceof ChildAssociationRef) - { - ChildAssociationRef ref = (ChildAssociationRef) rawValue; + } + else if (rawValue instanceof NodeRef) + { + NodeRef ref = (NodeRef) rawValue; value = tenantService.getBaseName(ref); - } - else if (rawValue instanceof AssociationRef) - { - AssociationRef ref = (AssociationRef) rawValue; + } + else if (rawValue instanceof ChildAssociationRef) + { + ChildAssociationRef ref = (ChildAssociationRef) rawValue; value = tenantService.getBaseName(ref); - } - else if (rawValue instanceof Path) - { - Path ref = (Path)rawValue; - Path outboundPath = new Path(); - Iterator itr = ref.iterator(); - while (itr.hasNext()) - { - Path.Element pathElement = itr.next(); - if (pathElement instanceof Path.ChildAssocElement) - { - pathElement = new Path.ChildAssocElement(tenantService.getBaseName(((Path.ChildAssocElement)pathElement).getRef())); - } - outboundPath.append(pathElement); - } - value = outboundPath; - } - // Done - return value; - } -} + } + else if (rawValue instanceof AssociationRef) + { + AssociationRef ref = (AssociationRef) rawValue; + value = tenantService.getBaseName(ref); + } + else if (rawValue instanceof Path) + { + Path ref = (Path)rawValue; + Path outboundPath = new Path(); + Iterator itr = ref.iterator(); + while (itr.hasNext()) + { + Path.Element pathElement = itr.next(); + if (pathElement instanceof Path.ChildAssocElement) + { + pathElement = new Path.ChildAssocElement(tenantService.getBaseName(((Path.ChildAssocElement)pathElement).getRef())); + } + outboundPath.append(pathElement); + } + value = outboundPath; + } + // Done + return value; + } +} diff --git a/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java b/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java index a48297cbdb..9bae787e98 100755 --- a/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java @@ -1,698 +1,717 @@ -/* - * Copyright (C) 2005-2008 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.tenant; - -import java.util.List; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.cache.SimpleCache; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.service.cmr.repository.AssociationRef; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.ParameterCheck; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/* - * MT Service implementation - * - * Adapts names to be tenant specific or vice-versa. - */ -public class MultiTServiceImpl implements TenantService -{ - private static Log logger = LogFactory.getLog(MultiTServiceImpl.class); - - // clusterable cache of enabled/disabled tenants - managed via TenantAdmin Service - private SimpleCache tenantsCache; - - private MultiTAdminServiceImpl tenantAdminService = null; // registered (rather than injected) - to avoid circular dependency - - - public void setTenantsCache(SimpleCache tenantsCache) - { - this.tenantsCache = tenantsCache; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getName(org.alfresco.service.cmr.repository.NodeRef) - */ - public NodeRef getName(NodeRef nodeRef) - { - if (nodeRef == null) { return null; } - - return new NodeRef(nodeRef.getStoreRef().getProtocol(), getName(nodeRef.getStoreRef().getIdentifier()), nodeRef.getId()); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getName(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef) - */ - public NodeRef getName(NodeRef inNodeRef, NodeRef nodeRef) - { - if (inNodeRef == null || nodeRef == null) { return null; } - - int idx = inNodeRef.getStoreRef().getIdentifier().lastIndexOf(SEPARATOR); - if (idx != -1) - { - String tenantDomain = inNodeRef.getStoreRef().getIdentifier().substring(1, idx); - return new NodeRef(nodeRef.getStoreRef().getProtocol(), getName(nodeRef.getStoreRef().getIdentifier(), tenantDomain), nodeRef.getId()); - } - - return nodeRef; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getName(org.alfresco.service.cmr.repository.StoreRef) - */ - public StoreRef getName(StoreRef storeRef) - { - if (storeRef == null) { return null; } - - return new StoreRef(storeRef.getProtocol(), getName(storeRef.getIdentifier())); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getName(org.alfresco.service.cmr.repository.ChildAssociationRef) - */ - public ChildAssociationRef getName(ChildAssociationRef childAssocRef) - { - if (childAssocRef == null) { return null; } - - return new ChildAssociationRef( - childAssocRef.getTypeQName(), - getName(childAssocRef.getParentRef()), - childAssocRef.getQName(), - getName(childAssocRef.getChildRef()), - childAssocRef.isPrimary(), - childAssocRef.getNthSibling()); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getName(org.alfresco.service.cmr.repository.AssociationRef) - */ - public AssociationRef getName(AssociationRef assocRef) - { - if (assocRef == null) { return null; } - - return new AssociationRef( - getName(assocRef.getSourceRef()), - assocRef.getTypeQName(), - getName(assocRef.getTargetRef())); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getName(java.lang.String, org.alfresco.service.cmr.repository.StoreRef) - */ - public StoreRef getName(String username, StoreRef storeRef) - { - if (storeRef == null) { return null; } - - if (username != null) - { - int idx = username.lastIndexOf(SEPARATOR); - if ((idx > 0) && (idx < (username.length()-1))) - { - String tenantDomain = username.substring(idx+1); - return new StoreRef(storeRef.getProtocol(), getName(storeRef.getIdentifier(), tenantDomain)); - } - } - - return storeRef; - } - - protected String getName(String name, String tenantDomain) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("name", name); - ParameterCheck.mandatory("tenantDomain", tenantDomain); - - checkTenantEnabled(tenantDomain); - - int idx1 = name.indexOf(SEPARATOR); - if (idx1 != 0) - { - // no domain, so add it as a prefix (between two domain separators) - name = SEPARATOR + tenantDomain + SEPARATOR + name; - } - else - { - int idx2 = name.indexOf(SEPARATOR, 1); - String nameDomain = name.substring(1, idx2); - if (! tenantDomain.equals(nameDomain)) - { - throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); - } - } - - return name; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getName(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) - */ - public QName getName(NodeRef inNodeRef, QName name) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("InNodeRef", inNodeRef); - ParameterCheck.mandatory("Name", name); - - int idx = inNodeRef.getStoreRef().getIdentifier().lastIndexOf(SEPARATOR); - if (idx != -1) - { - String tenantDomain = inNodeRef.getStoreRef().getIdentifier().substring(1, idx); - checkTenantEnabled(tenantDomain); - return getName(name, tenantDomain); - } - - return name; - - } - - private QName getName(QName name, String tenantDomain) - { - String namespace = name.getNamespaceURI(); - int idx1 = namespace.indexOf(SEPARATOR); - if (idx1 == -1) - { - // no domain, so add it as a prefix (between two domain separators) - namespace = SEPARATOR + tenantDomain + SEPARATOR + namespace; - name = QName.createQName(namespace, name.getLocalName()); - } - else - { - int idx2 = namespace.indexOf(SEPARATOR, 1); - String nameDomain = namespace.substring(1, idx2); - if (! tenantDomain.equals(nameDomain)) - { - throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); - } - } - - return name; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getName(java.lang.String) - */ - public String getName(String name) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("name", name); - - String tenantDomain = getCurrentUserDomain(); - - if (! tenantDomain.equals(DEFAULT_DOMAIN)) - { - int idx1 = name.indexOf(SEPARATOR); - if (idx1 != 0) - { - // no tenant domain prefix, so add it - name = SEPARATOR + tenantDomain + SEPARATOR + name; - } - else - { - int idx2 = name.indexOf(SEPARATOR, 1); - String nameDomain = name.substring(1, idx2); - if (! tenantDomain.equals(nameDomain)) - { - throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); - } - } - } - - return name; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getBaseName(org.alfresco.service.namespace.QName, boolean) - */ - public QName getBaseName(QName name, boolean forceForNonTenant) - { - String baseNamespaceURI = getBaseName(name.getNamespaceURI(), forceForNonTenant); - return QName.createQName(baseNamespaceURI, name.getLocalName()); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getBaseName(org.alfresco.service.cmr.repository.NodeRef) - */ - public NodeRef getBaseName(NodeRef nodeRef) - { - if (nodeRef == null) { return null; } - - return new NodeRef(nodeRef.getStoreRef().getProtocol(), getBaseName(nodeRef.getStoreRef().getIdentifier()), nodeRef.getId()); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getBaseName(org.alfresco.service.cmr.repository.StoreRef) - */ - public StoreRef getBaseName(StoreRef storeRef) - { - if (storeRef == null) { return null; } - - return new StoreRef(storeRef.getProtocol(), getBaseName(storeRef.getIdentifier())); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getBaseName(org.alfresco.service.cmr.repository.ChildAssociationRef) - */ - public ChildAssociationRef getBaseName(ChildAssociationRef childAssocRef) - { - if (childAssocRef == null) { return null; } - - return new ChildAssociationRef( - childAssocRef.getTypeQName(), - getBaseName(childAssocRef.getParentRef()), - childAssocRef.getQName(), - getBaseName(childAssocRef.getChildRef()), - childAssocRef.isPrimary(), - childAssocRef.getNthSibling()); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getBaseName(org.alfresco.service.cmr.repository.AssociationRef) - */ - public AssociationRef getBaseName(AssociationRef assocRef) - { - if (assocRef == null) { return null; } - - return new AssociationRef( - getBaseName(assocRef.getSourceRef()), - assocRef.getTypeQName(), - getBaseName(assocRef.getTargetRef())); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getBaseName(java.lang.String) - */ - public String getBaseName(String name) - { - // get base name, but don't force for non-tenant user (e.g. super admin) - return getBaseName(name, false); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getBaseName(java.lang.String, boolean) - */ - public String getBaseName(String name, boolean forceForNonTenant) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("name", name); - - String tenantDomain = getCurrentUserDomain(); - - int idx1 = name.indexOf(SEPARATOR); - if (idx1 == 0) - { - int idx2 = name.indexOf(SEPARATOR, 1); - String nameDomain = name.substring(1, idx2); - - if ((! tenantDomain.equals(DEFAULT_DOMAIN)) && (! tenantDomain.equals(nameDomain))) - { - throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); - } - - if ((! tenantDomain.equals(DEFAULT_DOMAIN)) || (forceForNonTenant)) - { - // remove tenant domain - name = name.substring(idx2+1); - } - } - - return name; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getBaseNameUser(java.lang.String) - */ - public String getBaseNameUser(String name) - { - // can be null (e.g. for System user / during app ctx init) - if (name != null) - { - int idx = name.lastIndexOf(SEPARATOR); - if (idx != -1) - { - return name.substring(0, idx); - } - } - return name; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#checkDomainUser(java.lang.String) - */ - public void checkDomainUser(String username) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("Username", username); - - String tenantDomain = getCurrentUserDomain(); - - if (! tenantDomain.equals(DEFAULT_DOMAIN)) - { - int idx2 = username.lastIndexOf(SEPARATOR); - if ((idx2 > 0) && (idx2 < (username.length()-1))) - { - String tenantUserDomain = username.substring(idx2+1); - - if ((tenantUserDomain == null) || (! tenantDomain.equals(tenantUserDomain))) - { - throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + tenantUserDomain); - } - } - else - { - throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = "); - } - } - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#checkDomain(java.lang.String) - */ - public void checkDomain(String name) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("Name", name); - - String nameDomain = null; - - int idx1 = name.indexOf(SEPARATOR); - if (idx1 == 0) - { - int idx2 = name.indexOf(SEPARATOR, 1); - nameDomain = name.substring(1, idx2); - } - - String tenantDomain = getCurrentUserDomain(); - - if (((nameDomain == null) && (! tenantDomain.equals(DEFAULT_DOMAIN))) || - ((nameDomain != null) && (! nameDomain.equals(tenantDomain)))) - { - throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); - } - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getRootNode(org.alfresco.service.cmr.repository.NodeService, org.alfresco.service.cmr.search.SearchService, org.alfresco.service.namespace.NamespaceService, java.lang.String, org.alfresco.service.cmr.repository.NodeRef) - */ - public NodeRef getRootNode(NodeService nodeService, SearchService searchService, NamespaceService namespaceService, String rootPath, NodeRef rootNodeRef) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("NodeService", nodeService); - ParameterCheck.mandatory("SearchService", searchService); - ParameterCheck.mandatory("NamespaceService", namespaceService); - ParameterCheck.mandatory("RootPath", rootPath); - ParameterCheck.mandatory("RootNodeRef", rootNodeRef); - - String username = AuthenticationUtil.getFullyAuthenticatedUser(); - StoreRef storeRef = getName(username, rootNodeRef.getStoreRef()); - - AuthenticationUtil.RunAsWork action = new GetRootNode(nodeService, searchService, namespaceService, rootPath, rootNodeRef, storeRef); - return getBaseName(AuthenticationUtil.runAs(action, AuthenticationUtil.getSystemUserName())); - } - - private class GetRootNode implements AuthenticationUtil.RunAsWork - { - NodeService nodeService; - SearchService searchService; - NamespaceService namespaceService; - String rootPath; - NodeRef rootNodeRef; - StoreRef storeRef; - - GetRootNode(NodeService nodeService, SearchService searchService, NamespaceService namespaceService, String rootPath, NodeRef rootNodeRef, StoreRef storeRef) - { - this.nodeService = nodeService; - this.searchService = searchService; - this.namespaceService = namespaceService; - this.rootPath = rootPath; - this.rootNodeRef = rootNodeRef; - this.storeRef = storeRef; - } - - public NodeRef doWork() throws Exception - { - // Get company home / root for the tenant domain - // Do this as the System user in case the tenant user does not have permission - - // Connect to the repo and ensure that the store exists - if (! nodeService.exists(storeRef)) - { - throw new AlfrescoRuntimeException("Store not created prior to application startup: " + storeRef); - } - NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); - - // Find the root node for this device - List nodeRefs = searchService.selectNodes(storeRootNodeRef, rootPath, null, namespaceService, false); - - if (nodeRefs.size() > 1) - { - throw new AlfrescoRuntimeException("Multiple possible roots for device: \n" + - " root path: " + rootPath + "\n" + - " results: " + nodeRefs); - } - else if (nodeRefs.size() == 0) - { - // nothing found - throw new AlfrescoRuntimeException("No root found for device: \n" + - " root path: " + rootPath); - } - else - { - // we found a node - rootNodeRef = nodeRefs.get(0); - } - - return rootNodeRef; - } - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#isTenantUser() - */ - public boolean isTenantUser() - { - return isTenantUser(AuthenticationUtil.getRunAsUser()); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#isTenantUser(java.lang.String) - */ - public boolean isTenantUser(String username) - { - // can be null (e.g. for System user / during app ctx init) - if (username != null) { - int idx = username.lastIndexOf(SEPARATOR); - if ((idx > 0) && (idx < (username.length()-1))) - { - return true; - } - } - return false; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#isTenantName(java.lang.String) - */ - public boolean isTenantName(String name) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("name", name); - - int idx1 = name.indexOf(SEPARATOR); - if (idx1 == 0) - { - int idx2 = name.indexOf(SEPARATOR, 1); - if (idx2 != -1) - { - return true; - } - } - - return false; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getUserDomain(java.lang.String) - */ - public String getUserDomain(String username) - { - // can be null (e.g. for System user / during app ctx init) - if (username != null) - { - int idx = username.lastIndexOf(SEPARATOR); - if ((idx > 0) && (idx < (username.length()-1))) - { - String tenantDomain = username.substring(idx+1); - - checkTenantEnabled(tenantDomain); - - return tenantDomain; - } - } - - return DEFAULT_DOMAIN; // default domain - non-tenant user - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantUserService#getCurrentUserDomain() - */ - public String getCurrentUserDomain() - { - String user = AuthenticationUtil.getRunAsUser(); - return getUserDomain(user); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantUserService#getDomain(java.lang.String) - */ - public String getDomain(String name) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("name", name); - - String tenantDomain = getCurrentUserDomain(); - - String nameDomain = DEFAULT_DOMAIN; - - int idx1 = name.indexOf(SEPARATOR); - if (idx1 == 0) - { - int idx2 = name.indexOf(SEPARATOR, 1); - nameDomain = name.substring(1, idx2); - - if ((! tenantDomain.equals(DEFAULT_DOMAIN)) && (! tenantDomain.equals(nameDomain))) - { - throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); - } - } - - return nameDomain; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantUserService#getDomainUser(java.lang.String, java.lang.String) - */ - public String getDomainUser(String baseUsername, String tenantDomain) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("baseUsername", baseUsername); - - if ((tenantDomain == null) || (tenantDomain.equals(DEFAULT_DOMAIN))) - { - return baseUsername; - } - else - { - if (baseUsername.contains(SEPARATOR)) - { - throw new AlfrescoRuntimeException("Invalid base username: " + baseUsername); - } - - if (tenantDomain.contains(SEPARATOR)) - { - throw new AlfrescoRuntimeException("Invalid tenant domain: " + tenantDomain); - } - - return baseUsername + SEPARATOR + tenantDomain; - } - } - - protected void checkTenantEnabled(String tenantDomain) - { - // note: System user can access disabled tenants - if (!AuthenticationUtil.isRunAsUserTheSystemUser() && !(getTenant(tenantDomain).isEnabled())) - { - throw new AlfrescoRuntimeException("Tenant is not enabled: " + tenantDomain); - } - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantService#getTenant(java.lang.String) - */ - public Tenant getTenant(String tenantDomain) - { - Tenant tenant = tenantsCache.get(tenantDomain); - if (tenant == null) - { - // backed by TenantAdminService - update this cache, e.g. could have been invalidated and/or expired - if (tenantAdminService != null) - { - tenant = tenantAdminService.getTenant(tenantDomain); - if (tenant == null) - { - throw new AlfrescoRuntimeException("No such tenant " + tenantDomain); - } - else - { - putTenant(tenantDomain, tenant); - } - } - } - - return tenant; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.tenant.TenantUserService#isEnabled() - */ - public boolean isEnabled() - { - return true; - } - - // should only be called by Tenant Admin Service - protected void register(MultiTAdminServiceImpl tenantAdminService) - { - this.tenantAdminService = tenantAdminService; - } - - // should only be called by Tenant Admin Service - protected void putTenant(String tenantDomain, Tenant tenant) - { - if (logger.isDebugEnabled()) - { - logger.debug("putTenant " + tenantDomain); - } - tenantsCache.put(tenantDomain, tenant); - } - - // should only be called by Tenant Admin Service - protected void removeTenant(String tenantDomain) - { - if (logger.isDebugEnabled()) - { - logger.debug("removeTenant " + tenantDomain); - } - tenantsCache.remove(tenantDomain); - } - -} +/* + * Copyright (C) 2005-2009 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.tenant; + +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/* + * MT Service implementation + * + * Adapts names to be tenant specific or vice-versa. + */ +public class MultiTServiceImpl implements TenantService +{ + private static Log logger = LogFactory.getLog(MultiTServiceImpl.class); + + // clusterable cache of enabled/disabled tenants - managed via TenantAdmin Service + private SimpleCache tenantsCache; + + private MultiTAdminServiceImpl tenantAdminService = null; // registered (rather than injected) - to avoid circular dependency + + + public void setTenantsCache(SimpleCache tenantsCache) + { + this.tenantsCache = tenantsCache; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getName(org.alfresco.service.cmr.repository.NodeRef) + */ + public NodeRef getName(NodeRef nodeRef) + { + if (nodeRef == null) { return null; } + + return new NodeRef(nodeRef.getStoreRef().getProtocol(), getName(nodeRef.getStoreRef().getIdentifier()), nodeRef.getId()); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getName(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef) + */ + public NodeRef getName(NodeRef inNodeRef, NodeRef nodeRef) + { + if (inNodeRef == null || nodeRef == null) { return null; } + + int idx = inNodeRef.getStoreRef().getIdentifier().lastIndexOf(SEPARATOR); + if (idx != -1) + { + String tenantDomain = inNodeRef.getStoreRef().getIdentifier().substring(1, idx); + return new NodeRef(nodeRef.getStoreRef().getProtocol(), getName(nodeRef.getStoreRef().getIdentifier(), tenantDomain), nodeRef.getId()); + } + + return nodeRef; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getName(org.alfresco.service.cmr.repository.StoreRef) + */ + public StoreRef getName(StoreRef storeRef) + { + if (storeRef == null) { return null; } + + return new StoreRef(storeRef.getProtocol(), getName(storeRef.getIdentifier())); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getName(org.alfresco.service.cmr.repository.ChildAssociationRef) + */ + public ChildAssociationRef getName(ChildAssociationRef childAssocRef) + { + if (childAssocRef == null) { return null; } + + return new ChildAssociationRef( + childAssocRef.getTypeQName(), + getName(childAssocRef.getParentRef()), + childAssocRef.getQName(), + getName(childAssocRef.getChildRef()), + childAssocRef.isPrimary(), + childAssocRef.getNthSibling()); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getName(org.alfresco.service.cmr.repository.AssociationRef) + */ + public AssociationRef getName(AssociationRef assocRef) + { + if (assocRef == null) { return null; } + + return new AssociationRef( + getName(assocRef.getSourceRef()), + assocRef.getTypeQName(), + getName(assocRef.getTargetRef())); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getName(java.lang.String, org.alfresco.service.cmr.repository.StoreRef) + */ + public StoreRef getName(String username, StoreRef storeRef) + { + if (storeRef == null) { return null; } + + if (username != null) + { + int idx = username.lastIndexOf(SEPARATOR); + if ((idx > 0) && (idx < (username.length()-1))) + { + String tenantDomain = username.substring(idx+1); + return new StoreRef(storeRef.getProtocol(), getName(storeRef.getIdentifier(), tenantDomain)); + } + } + + return storeRef; + } + + protected String getName(String name, String tenantDomain) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("name", name); + ParameterCheck.mandatory("tenantDomain", tenantDomain); + + checkTenantEnabled(tenantDomain); + + int idx1 = name.indexOf(SEPARATOR); + if (idx1 != 0) + { + // no domain, so add it as a prefix (between two domain separators) + name = SEPARATOR + tenantDomain + SEPARATOR + name; + } + else + { + int idx2 = name.indexOf(SEPARATOR, 1); + String nameDomain = name.substring(1, idx2); + if (! tenantDomain.equals(nameDomain)) + { + throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); + } + } + + return name; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getName(org.alfresco.service.namespace.QName) + */ + public QName getName(QName name) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("Name", name); + + String tenantDomain = getCurrentUserDomain(); + + if (! tenantDomain.equals(DEFAULT_DOMAIN)) + { + checkTenantEnabled(tenantDomain); + name = getName(name, tenantDomain); + } + + return name; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getName(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public QName getName(NodeRef inNodeRef, QName name) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("InNodeRef", inNodeRef); + ParameterCheck.mandatory("Name", name); + + int idx = inNodeRef.getStoreRef().getIdentifier().lastIndexOf(SEPARATOR); + if (idx != -1) + { + String tenantDomain = inNodeRef.getStoreRef().getIdentifier().substring(1, idx); + checkTenantEnabled(tenantDomain); + return getName(name, tenantDomain); + } + + return name; + + } + + private QName getName(QName name, String tenantDomain) + { + String namespace = name.getNamespaceURI(); + int idx1 = namespace.indexOf(SEPARATOR); + if (idx1 == -1) + { + // no domain, so add it as a prefix (between two domain separators) + namespace = SEPARATOR + tenantDomain + SEPARATOR + namespace; + name = QName.createQName(namespace, name.getLocalName()); + } + else + { + int idx2 = namespace.indexOf(SEPARATOR, 1); + String nameDomain = namespace.substring(1, idx2); + if (! tenantDomain.equals(nameDomain)) + { + throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); + } + } + + return name; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getName(java.lang.String) + */ + public String getName(String name) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("name", name); + + String tenantDomain = getCurrentUserDomain(); + + if (! tenantDomain.equals(DEFAULT_DOMAIN)) + { + int idx1 = name.indexOf(SEPARATOR); + if (idx1 != 0) + { + // no tenant domain prefix, so add it + name = SEPARATOR + tenantDomain + SEPARATOR + name; + } + else + { + int idx2 = name.indexOf(SEPARATOR, 1); + String nameDomain = name.substring(1, idx2); + if (! tenantDomain.equals(nameDomain)) + { + throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); + } + } + } + + return name; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getBaseName(org.alfresco.service.namespace.QName, boolean) + */ + public QName getBaseName(QName name, boolean forceForNonTenant) + { + String baseNamespaceURI = getBaseName(name.getNamespaceURI(), forceForNonTenant); + return QName.createQName(baseNamespaceURI, name.getLocalName()); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getBaseName(org.alfresco.service.cmr.repository.NodeRef) + */ + public NodeRef getBaseName(NodeRef nodeRef) + { + if (nodeRef == null) { return null; } + + return new NodeRef(nodeRef.getStoreRef().getProtocol(), getBaseName(nodeRef.getStoreRef().getIdentifier()), nodeRef.getId()); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getBaseName(org.alfresco.service.cmr.repository.StoreRef) + */ + public StoreRef getBaseName(StoreRef storeRef) + { + if (storeRef == null) { return null; } + + return new StoreRef(storeRef.getProtocol(), getBaseName(storeRef.getIdentifier())); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getBaseName(org.alfresco.service.cmr.repository.ChildAssociationRef) + */ + public ChildAssociationRef getBaseName(ChildAssociationRef childAssocRef) + { + if (childAssocRef == null) { return null; } + + return new ChildAssociationRef( + childAssocRef.getTypeQName(), + getBaseName(childAssocRef.getParentRef()), + childAssocRef.getQName(), + getBaseName(childAssocRef.getChildRef()), + childAssocRef.isPrimary(), + childAssocRef.getNthSibling()); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getBaseName(org.alfresco.service.cmr.repository.AssociationRef) + */ + public AssociationRef getBaseName(AssociationRef assocRef) + { + if (assocRef == null) { return null; } + + return new AssociationRef( + getBaseName(assocRef.getSourceRef()), + assocRef.getTypeQName(), + getBaseName(assocRef.getTargetRef())); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getBaseName(java.lang.String) + */ + public String getBaseName(String name) + { + // get base name, but don't force for non-tenant user (e.g. super admin) + return getBaseName(name, false); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getBaseName(java.lang.String, boolean) + */ + public String getBaseName(String name, boolean forceForNonTenant) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("name", name); + + String tenantDomain = getCurrentUserDomain(); + + int idx1 = name.indexOf(SEPARATOR); + if (idx1 == 0) + { + int idx2 = name.indexOf(SEPARATOR, 1); + String nameDomain = name.substring(1, idx2); + + if ((! tenantDomain.equals(DEFAULT_DOMAIN)) && (! tenantDomain.equals(nameDomain))) + { + throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); + } + + if ((! tenantDomain.equals(DEFAULT_DOMAIN)) || (forceForNonTenant)) + { + // remove tenant domain + name = name.substring(idx2+1); + } + } + + return name; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getBaseNameUser(java.lang.String) + */ + public String getBaseNameUser(String name) + { + // can be null (e.g. for System user / during app ctx init) + if (name != null) + { + int idx = name.lastIndexOf(SEPARATOR); + if (idx != -1) + { + return name.substring(0, idx); + } + } + return name; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#checkDomainUser(java.lang.String) + */ + public void checkDomainUser(String username) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("Username", username); + + String tenantDomain = getCurrentUserDomain(); + + if (! tenantDomain.equals(DEFAULT_DOMAIN)) + { + int idx2 = username.lastIndexOf(SEPARATOR); + if ((idx2 > 0) && (idx2 < (username.length()-1))) + { + String tenantUserDomain = username.substring(idx2+1); + + if ((tenantUserDomain == null) || (! tenantDomain.equals(tenantUserDomain))) + { + throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + tenantUserDomain); + } + } + else + { + throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = "); + } + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#checkDomain(java.lang.String) + */ + public void checkDomain(String name) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("Name", name); + + String nameDomain = null; + + int idx1 = name.indexOf(SEPARATOR); + if (idx1 == 0) + { + int idx2 = name.indexOf(SEPARATOR, 1); + nameDomain = name.substring(1, idx2); + } + + String tenantDomain = getCurrentUserDomain(); + + if (((nameDomain == null) && (! tenantDomain.equals(DEFAULT_DOMAIN))) || + ((nameDomain != null) && (! nameDomain.equals(tenantDomain)))) + { + throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getRootNode(org.alfresco.service.cmr.repository.NodeService, org.alfresco.service.cmr.search.SearchService, org.alfresco.service.namespace.NamespaceService, java.lang.String, org.alfresco.service.cmr.repository.NodeRef) + */ + public NodeRef getRootNode(NodeService nodeService, SearchService searchService, NamespaceService namespaceService, String rootPath, NodeRef rootNodeRef) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("NodeService", nodeService); + ParameterCheck.mandatory("SearchService", searchService); + ParameterCheck.mandatory("NamespaceService", namespaceService); + ParameterCheck.mandatory("RootPath", rootPath); + ParameterCheck.mandatory("RootNodeRef", rootNodeRef); + + String username = AuthenticationUtil.getFullyAuthenticatedUser(); + StoreRef storeRef = getName(username, rootNodeRef.getStoreRef()); + + AuthenticationUtil.RunAsWork action = new GetRootNode(nodeService, searchService, namespaceService, rootPath, rootNodeRef, storeRef); + return getBaseName(AuthenticationUtil.runAs(action, AuthenticationUtil.getSystemUserName())); + } + + private class GetRootNode implements AuthenticationUtil.RunAsWork + { + NodeService nodeService; + SearchService searchService; + NamespaceService namespaceService; + String rootPath; + NodeRef rootNodeRef; + StoreRef storeRef; + + GetRootNode(NodeService nodeService, SearchService searchService, NamespaceService namespaceService, String rootPath, NodeRef rootNodeRef, StoreRef storeRef) + { + this.nodeService = nodeService; + this.searchService = searchService; + this.namespaceService = namespaceService; + this.rootPath = rootPath; + this.rootNodeRef = rootNodeRef; + this.storeRef = storeRef; + } + + public NodeRef doWork() throws Exception + { + // Get company home / root for the tenant domain + // Do this as the System user in case the tenant user does not have permission + + // Connect to the repo and ensure that the store exists + if (! nodeService.exists(storeRef)) + { + throw new AlfrescoRuntimeException("Store not created prior to application startup: " + storeRef); + } + NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); + + // Find the root node for this device + List nodeRefs = searchService.selectNodes(storeRootNodeRef, rootPath, null, namespaceService, false); + + if (nodeRefs.size() > 1) + { + throw new AlfrescoRuntimeException("Multiple possible roots for device: \n" + + " root path: " + rootPath + "\n" + + " results: " + nodeRefs); + } + else if (nodeRefs.size() == 0) + { + // nothing found + throw new AlfrescoRuntimeException("No root found for device: \n" + + " root path: " + rootPath); + } + else + { + // we found a node + rootNodeRef = nodeRefs.get(0); + } + + return rootNodeRef; + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#isTenantUser() + */ + public boolean isTenantUser() + { + return isTenantUser(AuthenticationUtil.getRunAsUser()); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#isTenantUser(java.lang.String) + */ + public boolean isTenantUser(String username) + { + // can be null (e.g. for System user / during app ctx init) + if (username != null) { + int idx = username.lastIndexOf(SEPARATOR); + if ((idx > 0) && (idx < (username.length()-1))) + { + return true; + } + } + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#isTenantName(java.lang.String) + */ + public boolean isTenantName(String name) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("name", name); + + int idx1 = name.indexOf(SEPARATOR); + if (idx1 == 0) + { + int idx2 = name.indexOf(SEPARATOR, 1); + if (idx2 != -1) + { + return true; + } + } + + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getUserDomain(java.lang.String) + */ + public String getUserDomain(String username) + { + // can be null (e.g. for System user / during app ctx init) + if (username != null) + { + int idx = username.lastIndexOf(SEPARATOR); + if ((idx > 0) && (idx < (username.length()-1))) + { + String tenantDomain = username.substring(idx+1); + + checkTenantEnabled(tenantDomain); + + return tenantDomain; + } + } + + return DEFAULT_DOMAIN; // default domain - non-tenant user + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantUserService#getCurrentUserDomain() + */ + public String getCurrentUserDomain() + { + String user = AuthenticationUtil.getRunAsUser(); + return getUserDomain(user); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantUserService#getDomain(java.lang.String) + */ + public String getDomain(String name) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("name", name); + + String tenantDomain = getCurrentUserDomain(); + + String nameDomain = DEFAULT_DOMAIN; + + int idx1 = name.indexOf(SEPARATOR); + if (idx1 == 0) + { + int idx2 = name.indexOf(SEPARATOR, 1); + nameDomain = name.substring(1, idx2); + + if ((! tenantDomain.equals(DEFAULT_DOMAIN)) && (! tenantDomain.equals(nameDomain))) + { + throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); + } + } + + return nameDomain; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantUserService#getDomainUser(java.lang.String, java.lang.String) + */ + public String getDomainUser(String baseUsername, String tenantDomain) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("baseUsername", baseUsername); + + if ((tenantDomain == null) || (tenantDomain.equals(DEFAULT_DOMAIN))) + { + return baseUsername; + } + else + { + if (baseUsername.contains(SEPARATOR)) + { + throw new AlfrescoRuntimeException("Invalid base username: " + baseUsername); + } + + if (tenantDomain.contains(SEPARATOR)) + { + throw new AlfrescoRuntimeException("Invalid tenant domain: " + tenantDomain); + } + + return baseUsername + SEPARATOR + tenantDomain; + } + } + + protected void checkTenantEnabled(String tenantDomain) + { + // note: System user can access disabled tenants + if (!AuthenticationUtil.isRunAsUserTheSystemUser() && !(getTenant(tenantDomain).isEnabled())) + { + throw new AlfrescoRuntimeException("Tenant is not enabled: " + tenantDomain); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantService#getTenant(java.lang.String) + */ + public Tenant getTenant(String tenantDomain) + { + Tenant tenant = tenantsCache.get(tenantDomain); + if (tenant == null) + { + // backed by TenantAdminService - update this cache, e.g. could have been invalidated and/or expired + if (tenantAdminService != null) + { + tenant = tenantAdminService.getTenant(tenantDomain); + if (tenant == null) + { + throw new AlfrescoRuntimeException("No such tenant " + tenantDomain); + } + else + { + putTenant(tenantDomain, tenant); + } + } + } + + return tenant; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.tenant.TenantUserService#isEnabled() + */ + public boolean isEnabled() + { + return true; + } + + // should only be called by Tenant Admin Service + protected void register(MultiTAdminServiceImpl tenantAdminService) + { + this.tenantAdminService = tenantAdminService; + } + + // should only be called by Tenant Admin Service + protected void putTenant(String tenantDomain, Tenant tenant) + { + if (logger.isDebugEnabled()) + { + logger.debug("putTenant " + tenantDomain); + } + tenantsCache.put(tenantDomain, tenant); + } + + // should only be called by Tenant Admin Service + protected void removeTenant(String tenantDomain) + { + if (logger.isDebugEnabled()) + { + logger.debug("removeTenant " + tenantDomain); + } + tenantsCache.remove(tenantDomain); + } + +} diff --git a/source/java/org/alfresco/repo/tenant/SingleTAdminServiceImpl.java b/source/java/org/alfresco/repo/tenant/SingleTAdminServiceImpl.java index 6b4fe6ad7a..70af75a68b 100644 --- a/source/java/org/alfresco/repo/tenant/SingleTAdminServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/SingleTAdminServiceImpl.java @@ -1,223 +1,231 @@ -/* - * Copyright (C) 2005-2008 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.tenant; - -import java.io.File; -import java.util.Collections; -import java.util.List; - -import org.apache.commons.logging.Log; - -/** - * Empty Tenant Deployer Service implementation (for Single-Tenant / Single-Instance) - */ - -public class SingleTAdminServiceImpl implements TenantAdminService -{ - /** - * NO-OP - */ - public void startTenants() - { - } - - /** - * NO-OP - */ - public void stopTenants() - { - } - - /** - * @return Returns false always - */ - public boolean isEnabled() - { - return false; - } - - /** - * NO-OP - */ - public void initialiseTenants() - { - } - - /** - * NO-OP - */ - public void deployTenants(final TenantDeployer deployer, Log logger) - { - } - - /** - * NO-OP - */ - public void undeployTenants(final TenantDeployer deployer, Log logger) - { - } - - /** - * NO-OP - */ - public void register(TenantDeployer tenantDeployer) - { - } - - /** - * NO-OP - */ - public void unregister(TenantDeployer tenantDeployer) - { - } - - /** - * @return Returns an empty list always - */ - public List getAllTenants() - { - return Collections.emptyList(); - } - - /** - * @return Returns {@link TenantService#DEFAULT_DOMAIN} always - */ - public String getCurrentUserDomain() - { - return TenantService.DEFAULT_DOMAIN; - } - - /** - * @return Returns {@link TenantService#DEFAULT_DOMAIN} always - */ - public String getUserDomain(String username) - { - return TenantService.DEFAULT_DOMAIN; - } - - /** - * @return Returns the given username always - */ - public String getBaseNameUser(String username) - { - return username; - } - - /** - * @return Returns the given baseUserName always - */ - public String getDomainUser(String baseUsername, String tenantDomain) - { - return baseUsername; - } - - /** - * @return Returns {@link TenantService#DEFAULT_DOMAIN} always - */ - public String getDomain(String name) - { - return TenantService.DEFAULT_DOMAIN; - } - - /** - * @throws UnsupportedOperationException always - */ - public void createTenant(String tenantDomain, char[] adminRawPassword, String rootContentStoreDir) - { - throw new UnsupportedOperationException("Single tenant mode is active."); - } - - /** - * @throws UnsupportedOperationException always - */ - public void createTenant(String tenantDomain, char[] adminRawPassword) - { - throw new UnsupportedOperationException("Single tenant mode is active."); - } - - /** - * @throws UnsupportedOperationException always - */ - public void deleteTenant(String tenantDomain) - { - throw new UnsupportedOperationException("Single tenant mode is active."); - } - - /** - * @throws UnsupportedOperationException always - */ - public void disableTenant(String tenantDomain) - { - throw new UnsupportedOperationException("Single tenant mode is active."); - } - - /** - * @throws UnsupportedOperationException always - */ - public void enableTenant(String tenantDomain) - { - throw new UnsupportedOperationException("Single tenant mode is active."); - } - - /** - * @throws UnsupportedOperationException always - */ - public boolean existsTenant(String tenantDomain) - { - throw new UnsupportedOperationException("Single tenant mode is active."); - } - - /** - * @throws UnsupportedOperationException always - */ - public void exportTenant(String tenantDomain, File directoryDestination) - { - throw new UnsupportedOperationException("Single tenant mode is active."); - } - - /** - * @throws UnsupportedOperationException always - */ - public Tenant getTenant(String tenantDomain) - { - throw new UnsupportedOperationException("Single tenant mode is active."); - } - - /** - * @throws UnsupportedOperationException always - */ - public void importTenant(String tenantDomain, File directorySource, String rootContentStoreDir) - { - throw new UnsupportedOperationException("Single tenant mode is active."); - } - - /** - * @throws UnsupportedOperationException always - */ - public boolean isEnabledTenant(String tenantDomain) - { - throw new UnsupportedOperationException("Single tenant mode is active."); - } +/* + * Copyright (C) 2005-2009 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.tenant; + +import java.io.File; +import java.util.Collections; +import java.util.List; + +import org.alfresco.repo.workflow.WorkflowDeployer; +import org.apache.commons.logging.Log; + +/** + * Empty Tenant Deployer Service implementation (for Single-Tenant / Single-Instance) + */ + +public class SingleTAdminServiceImpl implements TenantAdminService +{ + /** + * NO-OP + */ + public void startTenants() + { + } + + /** + * NO-OP + */ + public void stopTenants() + { + } + + /** + * @return Returns false always + */ + public boolean isEnabled() + { + return false; + } + + /** + * NO-OP + */ + public void initialiseTenants() + { + } + + /** + * NO-OP + */ + public void deployTenants(final TenantDeployer deployer, Log logger) + { + } + + /** + * NO-OP + */ + public void undeployTenants(final TenantDeployer deployer, Log logger) + { + } + + /** + * NO-OP + */ + public void register(TenantDeployer tenantDeployer) + { + } + + /** + * NO-OP + */ + public void unregister(TenantDeployer tenantDeployer) + { + } + + /** + * NO-OP + */ + public void register(WorkflowDeployer workflowDeployer) + { + } + + /** + * @return Returns an empty list always + */ + public List getAllTenants() + { + return Collections.emptyList(); + } + + /** + * @return Returns {@link TenantService#DEFAULT_DOMAIN} always + */ + public String getCurrentUserDomain() + { + return TenantService.DEFAULT_DOMAIN; + } + + /** + * @return Returns {@link TenantService#DEFAULT_DOMAIN} always + */ + public String getUserDomain(String username) + { + return TenantService.DEFAULT_DOMAIN; + } + + /** + * @return Returns the given username always + */ + public String getBaseNameUser(String username) + { + return username; + } + + /** + * @return Returns the given baseUserName always + */ + public String getDomainUser(String baseUsername, String tenantDomain) + { + return baseUsername; + } + + /** + * @return Returns {@link TenantService#DEFAULT_DOMAIN} always + */ + public String getDomain(String name) + { + return TenantService.DEFAULT_DOMAIN; + } + + /** + * @throws UnsupportedOperationException always + */ + public void createTenant(String tenantDomain, char[] adminRawPassword, String rootContentStoreDir) + { + throw new UnsupportedOperationException("Single tenant mode is active."); + } + + /** + * @throws UnsupportedOperationException always + */ + public void createTenant(String tenantDomain, char[] adminRawPassword) + { + throw new UnsupportedOperationException("Single tenant mode is active."); + } + + /** + * @throws UnsupportedOperationException always + */ + public void deleteTenant(String tenantDomain) + { + throw new UnsupportedOperationException("Single tenant mode is active."); + } + + /** + * @throws UnsupportedOperationException always + */ + public void disableTenant(String tenantDomain) + { + throw new UnsupportedOperationException("Single tenant mode is active."); + } + + /** + * @throws UnsupportedOperationException always + */ + public void enableTenant(String tenantDomain) + { + throw new UnsupportedOperationException("Single tenant mode is active."); + } + + /** + * @throws UnsupportedOperationException always + */ + public boolean existsTenant(String tenantDomain) + { + throw new UnsupportedOperationException("Single tenant mode is active."); + } + + /** + * @throws UnsupportedOperationException always + */ + public void exportTenant(String tenantDomain, File directoryDestination) + { + throw new UnsupportedOperationException("Single tenant mode is active."); + } + + /** + * @throws UnsupportedOperationException always + */ + public Tenant getTenant(String tenantDomain) + { + throw new UnsupportedOperationException("Single tenant mode is active."); + } + + /** + * @throws UnsupportedOperationException always + */ + public void importTenant(String tenantDomain, File directorySource, String rootContentStoreDir) + { + throw new UnsupportedOperationException("Single tenant mode is active."); + } + + /** + * @throws UnsupportedOperationException always + */ + public boolean isEnabledTenant(String tenantDomain) + { + throw new UnsupportedOperationException("Single tenant mode is active."); + } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/tenant/SingleTServiceImpl.java b/source/java/org/alfresco/repo/tenant/SingleTServiceImpl.java index 73a6c5ac95..26bd299123 100644 --- a/source/java/org/alfresco/repo/tenant/SingleTServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/SingleTServiceImpl.java @@ -1,182 +1,187 @@ -/* - * 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.tenant; - -import org.alfresco.service.cmr.repository.AssociationRef; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; - -/** - * Empty Tenant Service implementation (for Single-Tenant / Single-Instance) - */ - -public class SingleTServiceImpl implements TenantService -{ - public NodeRef getName(NodeRef nodeRef) - { - return nodeRef; - } - - public NodeRef getName(NodeRef inNodeRef, NodeRef nodeRef) - { - return nodeRef; - } - - public StoreRef getName(StoreRef storeRef) - { - return storeRef; - } - - public ChildAssociationRef getName(ChildAssociationRef childAssocRef) - { - return childAssocRef; - } - - public AssociationRef getName(AssociationRef assocRef) - { - return assocRef; - } - - public StoreRef getName(String username, StoreRef storeRef) - { - return storeRef; - } - - public QName getName(NodeRef inNodeRef, QName name) - { - return name; - } - - public String getName(String name) - { - return name; - } - - public QName getBaseName(QName name, boolean forceForNonTenant) - { - return name; - } - - public NodeRef getBaseName(NodeRef nodeRef) - { - return nodeRef; - } - - public StoreRef getBaseName(StoreRef storeRef) - { - return storeRef; - } - - public ChildAssociationRef getBaseName(ChildAssociationRef childAssocRef) - { - return childAssocRef; - } - - public AssociationRef getBaseName(AssociationRef assocRef) - { - return assocRef; - } - - public String getBaseName(String name) - { - return name; - } - - - public String getBaseName(String name, boolean forceForNonTenant) - { - return name; - } - - public String getBaseNameUser(String name) - { - return name; - } - - public void checkDomainUser(String username) - { - // NOOP - } - - public void checkDomain(String name) - { - // NOOP - } - - public NodeRef getRootNode(NodeService nodeService, SearchService searchService, NamespaceService namespaceService, String rootPath, NodeRef rootNodeRef) - { - return rootNodeRef; - } - - public boolean isTenantUser() - { - return false; - } - - public boolean isTenantUser(String username) - { - return false; - } - - public boolean isTenantName(String name) - { - return false; - } - - public String getUserDomain(String username) - { - return DEFAULT_DOMAIN; - } - - public String getCurrentUserDomain() - { - return DEFAULT_DOMAIN; - } - - public String getDomain(String name) - { - return DEFAULT_DOMAIN; - } - - public String getDomainUser(String baseUsername, String tenantDomain) - { - return baseUsername; - } - - public Tenant getTenant(String tenantDomain) - { - return null; - } - - public boolean isEnabled() - { - return false; - } +/* + * Copyright (C) 2005-2009 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.tenant; + +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Empty Tenant Service implementation (for Single-Tenant / Single-Instance) + */ + +public class SingleTServiceImpl implements TenantService +{ + public NodeRef getName(NodeRef nodeRef) + { + return nodeRef; + } + + public NodeRef getName(NodeRef inNodeRef, NodeRef nodeRef) + { + return nodeRef; + } + + public StoreRef getName(StoreRef storeRef) + { + return storeRef; + } + + public ChildAssociationRef getName(ChildAssociationRef childAssocRef) + { + return childAssocRef; + } + + public AssociationRef getName(AssociationRef assocRef) + { + return assocRef; + } + + public StoreRef getName(String username, StoreRef storeRef) + { + return storeRef; + } + + public QName getName(QName name) + { + return name; + } + + public QName getName(NodeRef inNodeRef, QName name) + { + return name; + } + + public String getName(String name) + { + return name; + } + + public QName getBaseName(QName name, boolean forceForNonTenant) + { + return name; + } + + public NodeRef getBaseName(NodeRef nodeRef) + { + return nodeRef; + } + + public StoreRef getBaseName(StoreRef storeRef) + { + return storeRef; + } + + public ChildAssociationRef getBaseName(ChildAssociationRef childAssocRef) + { + return childAssocRef; + } + + public AssociationRef getBaseName(AssociationRef assocRef) + { + return assocRef; + } + + public String getBaseName(String name) + { + return name; + } + + + public String getBaseName(String name, boolean forceForNonTenant) + { + return name; + } + + public String getBaseNameUser(String name) + { + return name; + } + + public void checkDomainUser(String username) + { + // NOOP + } + + public void checkDomain(String name) + { + // NOOP + } + + public NodeRef getRootNode(NodeService nodeService, SearchService searchService, NamespaceService namespaceService, String rootPath, NodeRef rootNodeRef) + { + return rootNodeRef; + } + + public boolean isTenantUser() + { + return false; + } + + public boolean isTenantUser(String username) + { + return false; + } + + public boolean isTenantName(String name) + { + return false; + } + + public String getUserDomain(String username) + { + return DEFAULT_DOMAIN; + } + + public String getCurrentUserDomain() + { + return DEFAULT_DOMAIN; + } + + public String getDomain(String name) + { + return DEFAULT_DOMAIN; + } + + public String getDomainUser(String baseUsername, String tenantDomain) + { + return baseUsername; + } + + public Tenant getTenant(String tenantDomain) + { + return null; + } + + public boolean isEnabled() + { + return false; + } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/tenant/TenantAdminService.java b/source/java/org/alfresco/repo/tenant/TenantAdminService.java index b2189690c0..82b4c5c854 100644 --- a/source/java/org/alfresco/repo/tenant/TenantAdminService.java +++ b/source/java/org/alfresco/repo/tenant/TenantAdminService.java @@ -1,83 +1,90 @@ -/* - * Copyright (C) 2005-2008 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.tenant; - -import java.io.File; -import java.util.List; - -import org.apache.commons.logging.Log; - - -/** - * Tenant Admin Service interface. - *

- * This interface provides administrative methods to provision and administer tenants. - * - */ - -public interface TenantAdminService extends TenantUserService -{ - public void startTenants(); - - public void stopTenants(); - - /* - * Deployer methods - */ - - public void deployTenants(final TenantDeployer deployer, Log logger); - - public void undeployTenants(final TenantDeployer deployer, Log logger); - - public void register(TenantDeployer tenantDeployer); - - public void unregister(TenantDeployer tenantDeployer); - - public List getAllTenants(); - - /* - * Deployer methods - */ - - public void createTenant(String tenantDomain, char[] adminRawPassword); - - public void createTenant(String tenantDomain, char[] adminRawPassword, String rootContentStoreDir); - - public void exportTenant(String tenantDomain, File directoryDestination); - - public void importTenant(String tenantDomain, File directorySource, String rootContentStoreDir); - - public boolean existsTenant(String tenantDomain); - - public void deleteTenant(String tenantDomain); - - public void enableTenant(String tenantDomain); - - public void disableTenant(String tenantDomain); - - public Tenant getTenant(String tenantDomain); - - public boolean isEnabledTenant(String tenantDomain); -} +/* + * Copyright (C) 2005-2009 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.tenant; + +import java.io.File; +import java.util.List; + +import org.alfresco.repo.workflow.WorkflowDeployer; +import org.apache.commons.logging.Log; + + +/** + * Tenant Admin Service interface. + *

+ * This interface provides administrative methods to provision and administer tenants. + * + */ + +public interface TenantAdminService extends TenantUserService +{ + public void startTenants(); + + public void stopTenants(); + + /* + * Tenant Deployer methods + */ + + public void deployTenants(final TenantDeployer deployer, Log logger); + + public void undeployTenants(final TenantDeployer deployer, Log logger); + + public void register(TenantDeployer tenantDeployer); + + public void unregister(TenantDeployer tenantDeployer); + + public List getAllTenants(); + + /* + * Workflow Deployer methods + */ + + public void register(WorkflowDeployer workflowDeployer); + + /* + * Admin methods + */ + + public void createTenant(String tenantDomain, char[] adminRawPassword); + + public void createTenant(String tenantDomain, char[] adminRawPassword, String rootContentStoreDir); + + public void exportTenant(String tenantDomain, File directoryDestination); + + public void importTenant(String tenantDomain, File directorySource, String rootContentStoreDir); + + public boolean existsTenant(String tenantDomain); + + public void deleteTenant(String tenantDomain); + + public void enableTenant(String tenantDomain); + + public void disableTenant(String tenantDomain); + + public Tenant getTenant(String tenantDomain); + + public boolean isEnabledTenant(String tenantDomain); +} diff --git a/source/java/org/alfresco/repo/tenant/TenantService.java b/source/java/org/alfresco/repo/tenant/TenantService.java index aa89e23fdc..932f9d5d7b 100644 --- a/source/java/org/alfresco/repo/tenant/TenantService.java +++ b/source/java/org/alfresco/repo/tenant/TenantService.java @@ -1,141 +1,146 @@ -/* - * Copyright (C) 2005-2008 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.tenant; - -import org.alfresco.service.cmr.repository.AssociationRef; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; - - -/** - * Tenant Service interface. - *

- * This interface provides methods to support either ST or MT implementations. - * - */ -public interface TenantService extends TenantUserService -{ - public static final String SEPARATOR = "@"; - - public static final String DEFAULT_DOMAIN = ""; - - public static final String ADMIN_BASENAME = "admin"; - - /** - * @return the reference with the tenant-specific ID attached - */ - public NodeRef getName(NodeRef nodeRef); - - /** - * @return the reference with the tenant-specific ID attached - */ - public NodeRef getName(NodeRef inNodeRef, NodeRef nodeRef); - - /** - * @return the reference with the tenant-specific ID attached - */ - public StoreRef getName(StoreRef storeRef); - - /** - * @return the reference with the tenant-specific ID attached - */ - public ChildAssociationRef getName(ChildAssociationRef childAssocRef); - - /** - * @return the reference with the tenant-specific ID attached - */ - public AssociationRef getName(AssociationRef assocRef); - - /** - * @return the reference with the tenant-specific ID attached - */ - public StoreRef getName(String username, StoreRef storeRef); - - /** - * @return the reference with the tenant-specific ID attached - */ - public QName getName(NodeRef inNodeRef, QName name); - - /** - * @return the reference with the tenant-specific ID attached - */ - public String getName(String name); - - /** - * @return the reference without the tenant-specific ID attached - */ - public QName getBaseName(QName name, boolean forceIfNonTenant); - - /** - * @return the reference without the tenant-specific ID attached - */ - public NodeRef getBaseName(NodeRef nodeRef); - - /** - * @return the reference without the tenant-specific ID attached - */ - public StoreRef getBaseName(StoreRef storeRef); - - /** - * @return the reference without the tenant-specific ID attached - */ - public ChildAssociationRef getBaseName(ChildAssociationRef childAssocRef); - - /** - * @return the reference without the tenant-specific ID attached - */ - public AssociationRef getBaseName(AssociationRef assocRef); - - /** - * @return the reference without the tenant-specific ID attached - */ - public String getBaseName(String name); - - /** - * @return the reference without the tenant-specific ID attached - */ - public String getBaseName(String name, boolean forceIfNonTenant); - - public void checkDomainUser(String username); - - public void checkDomain(String name); - - public NodeRef getRootNode(NodeService nodeService, SearchService searchService, NamespaceService namespaceService, String rootPath, NodeRef rootNodeRef); - - public boolean isTenantUser(); - - public boolean isTenantUser(String username); - - public boolean isTenantName(String name); - - public String getUserDomain(String username); - - public Tenant getTenant(String tenantDomain); -} +/* + * Copyright (C) 2005-2009 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.tenant; + +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + + +/** + * Tenant Service interface. + *

+ * This interface provides methods to support either ST or MT implementations. + * + */ +public interface TenantService extends TenantUserService +{ + public static final String SEPARATOR = "@"; + + public static final String DEFAULT_DOMAIN = ""; + + public static final String ADMIN_BASENAME = "admin"; + + /** + * @return the reference with the tenant-specific ID attached + */ + public NodeRef getName(NodeRef nodeRef); + + /** + * @return the reference with the tenant-specific ID attached + */ + public NodeRef getName(NodeRef inNodeRef, NodeRef nodeRef); + + /** + * @return the reference with the tenant-specific ID attached + */ + public StoreRef getName(StoreRef storeRef); + + /** + * @return the reference with the tenant-specific ID attached + */ + public ChildAssociationRef getName(ChildAssociationRef childAssocRef); + + /** + * @return the reference with the tenant-specific ID attached + */ + public AssociationRef getName(AssociationRef assocRef); + + /** + * @return the reference with the tenant-specific ID attached + */ + public StoreRef getName(String username, StoreRef storeRef); + + /** + * @return the reference with the tenant-specific ID attached + */ + public QName getName(QName name); + + /** + * @return the reference with the tenant-specific ID attached + */ + public QName getName(NodeRef inNodeRef, QName name); + + /** + * @return the reference with the tenant-specific ID attached + */ + public String getName(String name); + + /** + * @return the reference without the tenant-specific ID attached + */ + public QName getBaseName(QName name, boolean forceIfNonTenant); + + /** + * @return the reference without the tenant-specific ID attached + */ + public NodeRef getBaseName(NodeRef nodeRef); + + /** + * @return the reference without the tenant-specific ID attached + */ + public StoreRef getBaseName(StoreRef storeRef); + + /** + * @return the reference without the tenant-specific ID attached + */ + public ChildAssociationRef getBaseName(ChildAssociationRef childAssocRef); + + /** + * @return the reference without the tenant-specific ID attached + */ + public AssociationRef getBaseName(AssociationRef assocRef); + + /** + * @return the reference without the tenant-specific ID attached + */ + public String getBaseName(String name); + + /** + * @return the reference without the tenant-specific ID attached + */ + public String getBaseName(String name, boolean forceIfNonTenant); + + public void checkDomainUser(String username); + + public void checkDomain(String name); + + public NodeRef getRootNode(NodeService nodeService, SearchService searchService, NamespaceService namespaceService, String rootPath, NodeRef rootNodeRef); + + public boolean isTenantUser(); + + public boolean isTenantUser(String username); + + public boolean isTenantName(String name); + + public String getUserDomain(String username); + + public Tenant getTenant(String tenantDomain); +} diff --git a/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java b/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java index 8e4289e77d..0928f616f7 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2009 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 @@ -40,6 +40,7 @@ import org.alfresco.repo.dictionary.RepositoryLocation; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.tenant.TenantAdminService; import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -85,6 +86,7 @@ public class WorkflowDeployer extends AbstractLifecycleBean private List workflowDefinitions; private List models = new ArrayList(); private List resourceBundles = new ArrayList(); + private TenantAdminService tenantAdminService; private TenantService tenantService; private NodeService nodeService; @@ -145,6 +147,16 @@ public class WorkflowDeployer extends AbstractLifecycleBean this.dictionaryDAO = dictionaryDAO; } + /** + * Sets the tenant admin service + * + * @param tenantService the tenant admin service + */ + public void setTenantAdminService(TenantAdminService tenantAdminService) + { + this.tenantAdminService = tenantAdminService; + } + /** * Sets the tenant service * @@ -432,6 +444,8 @@ public class WorkflowDeployer extends AbstractLifecycleBean return null; } }, AuthenticationUtil.getSystemUserName()); + + tenantAdminService.register(this); } @Override diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java index 3067954f5b..5a9da245be 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java @@ -1261,6 +1261,12 @@ public class JBPMEngine extends BPMEngine public List doInJbpm(JbpmContext context) { Session session = context.getSession(); + + if ((query.getProcessName() != null) && (tenantService.isEnabled())) + { + query.setProcessName(tenantService.getName(query.getProcessName())); + } + Criteria criteria = createTaskQueryCriteria(session, query); List tasks = criteria.list(); @@ -1505,7 +1511,19 @@ public class JBPMEngine extends BPMEngine { process = (process == null) ? root.createCriteria("processInstance") : process; Criteria processDef = process.createCriteria("processDefinition"); - processDef.add(Restrictions.eq("name", query.getProcessName().toPrefixString(namespaceService))); + + String processName = null; + if (tenantService.isEnabled()) + { + QName baseProcessName = tenantService.getBaseName(query.getProcessName(), true); + processName = tenantService.getName(baseProcessName.toPrefixString(namespaceService)); + } + else + { + processName = query.getProcessName().toPrefixString(namespaceService); + } + + processDef.add(Restrictions.eq("name", processName)); } return process; diff --git a/source/test-resources/tenant/mt-admin-context.xml b/source/test-resources/tenant/mt-admin-context.xml index 318d3bee71..81bece177d 100644 --- a/source/test-resources/tenant/mt-admin-context.xml +++ b/source/test-resources/tenant/mt-admin-context.xml @@ -7,26 +7,7 @@ - - - - - - - - - - - - - - - - - - +