mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
MT extension
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@6785 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -334,7 +334,13 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
||||
|
||||
NodeRef modelNodeRef = nodeRefs.get(0);
|
||||
|
||||
boolean isActive = ((Boolean)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE)).booleanValue();
|
||||
boolean isActive = false;
|
||||
Boolean value = (Boolean)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE);
|
||||
if (value != null)
|
||||
{
|
||||
isActive = value.booleanValue();
|
||||
}
|
||||
|
||||
QName modelQName = (QName)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_NAME);
|
||||
|
||||
ModelDefinition modelDef = null;
|
||||
@@ -436,7 +442,13 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
||||
|
||||
NodeRef modelNodeRef = nodeRefs.get(0);
|
||||
|
||||
boolean isActive = ((Boolean)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE)).booleanValue();
|
||||
boolean isActive = false;
|
||||
Boolean value = (Boolean)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE);
|
||||
if (value != null)
|
||||
{
|
||||
isActive = value.booleanValue();
|
||||
}
|
||||
|
||||
modelQName = (QName)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_NAME);
|
||||
|
||||
ModelDefinition modelDef = null;
|
||||
|
@@ -83,7 +83,7 @@ public class GenericWorkflowPatch extends AbstractPatch implements ApplicationCo
|
||||
props.put(WorkflowDeployer.REDEPLOY, "true");
|
||||
}
|
||||
deployer.setWorkflowDefinitions(workflowDefinitions);
|
||||
deployer.deploy();
|
||||
deployer.init();
|
||||
|
||||
// done
|
||||
return I18NUtil.getMessage(MSG_DEPLOYED, workflowDefinitions.size());
|
||||
|
139
source/java/org/alfresco/repo/content/TenantRoutingFileContentStore.java
Executable file
139
source/java/org/alfresco/repo/content/TenantRoutingFileContentStore.java
Executable file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.content;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.content.filestore.FileContentStore;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.tenant.Tenant;
|
||||
import org.alfresco.repo.tenant.TenantDeployer;
|
||||
import org.alfresco.repo.tenant.TenantService;
|
||||
|
||||
/**
|
||||
* Content Store that supports tenant routing, if multi-tenancy is enabled.
|
||||
*
|
||||
* Note: Need to initialise before the dictionary service, in the case that models are dynamically loaded for the tenant.
|
||||
*/
|
||||
public class TenantRoutingFileContentStore extends AbstractRoutingContentStore implements TenantDeployer
|
||||
{
|
||||
Map<String, FileContentStore> tenantFileStores = new HashMap<String, FileContentStore>();
|
||||
|
||||
private String defaultRootDirectory;
|
||||
private TenantService tenantService;
|
||||
|
||||
|
||||
public void setDefaultRootDir(String defaultRootDirectory)
|
||||
{
|
||||
this.defaultRootDirectory = defaultRootDirectory;
|
||||
}
|
||||
|
||||
public void setTenantService(TenantService tenantService)
|
||||
{
|
||||
this.tenantService = tenantService;
|
||||
}
|
||||
|
||||
protected ContentStore selectWriteStore(ContentContext ctx)
|
||||
{
|
||||
return getTenantFileStore(tenantService.getCurrentUserDomain());
|
||||
}
|
||||
|
||||
public List<ContentStore> getAllStores()
|
||||
{
|
||||
if (tenantService.isEnabled())
|
||||
{
|
||||
String currentUser = AuthenticationUtil.getCurrentUserName();
|
||||
if ((currentUser == null) || (currentUser.equals(AuthenticationUtil.getSystemUserName())))
|
||||
{
|
||||
// return enabled stores across all tenants, if running as system/null user, for example, ContentStoreCleaner scheduled job
|
||||
List<ContentStore> allEnabledStores = new ArrayList<ContentStore>();
|
||||
for (String tenantDomain : tenantFileStores.keySet())
|
||||
{
|
||||
allEnabledStores.add(tenantFileStores.get(tenantDomain)); // note: cache should only contain enabled stores
|
||||
}
|
||||
return allEnabledStores;
|
||||
}
|
||||
}
|
||||
return Arrays.asList(getTenantFileStore(tenantService.getCurrentUserDomain()));
|
||||
}
|
||||
|
||||
private ContentStore getTenantFileStore(String tenantDomain)
|
||||
{
|
||||
return tenantFileStores.get(tenantDomain);
|
||||
}
|
||||
|
||||
private void putTenantFileStore(String tenantDomain, FileContentStore fileStore)
|
||||
{
|
||||
tenantFileStores.put(tenantDomain, fileStore);
|
||||
}
|
||||
|
||||
private void removeTenantFileStore(String tenantDomain)
|
||||
{
|
||||
tenantFileStores.remove(tenantDomain);
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
String tenantDomain = "";
|
||||
String rootDir = defaultRootDirectory;
|
||||
|
||||
Tenant tenant = tenantService.getTenant(tenantService.getCurrentUserDomain());
|
||||
if (tenant != null)
|
||||
{
|
||||
if (tenant.getRootContentStoreDir() != null)
|
||||
{
|
||||
rootDir = tenant.getRootContentStoreDir();
|
||||
}
|
||||
tenantDomain = tenant.getTenantDomain();
|
||||
}
|
||||
|
||||
putTenantFileStore(tenantDomain, new FileContentStore(new File(rootDir)));
|
||||
}
|
||||
|
||||
public void destroy()
|
||||
{
|
||||
removeTenantFileStore(tenantService.getCurrentUserDomain());
|
||||
}
|
||||
|
||||
public void onEnableTenant()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
public void onDisableTenant()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
public String getDefaultRootDir()
|
||||
{
|
||||
return this.defaultRootDirectory;
|
||||
}
|
||||
}
|
@@ -175,8 +175,12 @@ public class MessageServiceImpl implements MessageService
|
||||
|
||||
try
|
||||
{
|
||||
writeLock.lock();
|
||||
tenantResourceBundleBaseNames.add(resBundlePath);
|
||||
writeLock.lock();
|
||||
|
||||
if (! tenantResourceBundleBaseNames.contains(resBundlePath))
|
||||
{
|
||||
tenantResourceBundleBaseNames.add(resBundlePath);
|
||||
}
|
||||
|
||||
logger.info("Registered message bundle '" + resBundlePath + "'");
|
||||
|
||||
@@ -333,7 +337,10 @@ public class MessageServiceImpl implements MessageService
|
||||
|
||||
try
|
||||
{
|
||||
resourcebundle = new PropertyResourceBundle(resBundleStream);
|
||||
if (resBundleStream != null)
|
||||
{
|
||||
resourcebundle = new PropertyResourceBundle(resBundleStream);
|
||||
}
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
@@ -346,12 +353,15 @@ public class MessageServiceImpl implements MessageService
|
||||
resourcebundle = ResourceBundle.getBundle(resBundlePath, locale);
|
||||
}
|
||||
|
||||
// unload from the cached messages
|
||||
Enumeration<String> enumKeys = resourcebundle.getKeys();
|
||||
while (enumKeys.hasMoreElements() == true)
|
||||
if (resourcebundle != null)
|
||||
{
|
||||
String key = enumKeys.nextElement();
|
||||
props.remove(key);
|
||||
// unload from the cached messages
|
||||
Enumeration<String> enumKeys = resourcebundle.getKeys();
|
||||
while (enumKeys.hasMoreElements() == true)
|
||||
{
|
||||
String key = enumKeys.nextElement();
|
||||
props.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
loadedBundles.remove(resBundlePath);
|
||||
@@ -480,7 +490,10 @@ public class MessageServiceImpl implements MessageService
|
||||
|
||||
try
|
||||
{
|
||||
resourcebundle = new PropertyResourceBundle(resBundleStream);
|
||||
if (resBundleStream != null)
|
||||
{
|
||||
resourcebundle = new PropertyResourceBundle(resBundleStream);
|
||||
}
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
@@ -493,15 +506,18 @@ public class MessageServiceImpl implements MessageService
|
||||
resourcebundle = ResourceBundle.getBundle(resBundlePath, locale);
|
||||
}
|
||||
|
||||
Enumeration<String> enumKeys = resourcebundle.getKeys();
|
||||
while (enumKeys.hasMoreElements() == true)
|
||||
if (resourcebundle != null)
|
||||
{
|
||||
String key = enumKeys.nextElement();
|
||||
props.put(key, resourcebundle.getString(key));
|
||||
Enumeration<String> enumKeys = resourcebundle.getKeys();
|
||||
while (enumKeys.hasMoreElements() == true)
|
||||
{
|
||||
String key = enumKeys.nextElement();
|
||||
props.put(key, resourcebundle.getString(key));
|
||||
}
|
||||
|
||||
loadedBundles.add(resBundlePath);
|
||||
count++;
|
||||
}
|
||||
|
||||
loadedBundles.add(resBundlePath);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,7 +559,8 @@ public class MessageServiceImpl implements MessageService
|
||||
|
||||
if ((nodeRefs == null) || (nodeRefs.size() == 0))
|
||||
{
|
||||
throw new RuntimeException("Could not find message resource bundle " + storeRef + path);
|
||||
logger.debug("Could not find message resource bundle " + storeRef + "/" + path);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -323,8 +323,12 @@ public class ImporterBootstrap extends AbstractLifecycleBean
|
||||
|
||||
UserTransaction userTransaction = transactionService.getUserTransaction();
|
||||
Authentication authentication = authenticationComponent.getCurrentAuthentication();
|
||||
authenticationComponent.setSystemUserAsCurrentUser();
|
||||
|
||||
|
||||
if (authenticationComponent.getCurrentUserName() == null)
|
||||
{
|
||||
authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
userTransaction.begin();
|
||||
|
882
source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java
Executable file
882
source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java
Executable file
@@ -0,0 +1,882 @@
|
||||
/*
|
||||
* 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 java.io.IOException;
|
||||
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.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowService;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.util.AbstractLifecycleBean;
|
||||
import org.alfresco.util.ParameterCheck;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
/**
|
||||
* MT Admin Service Implementation.
|
||||
*
|
||||
*/
|
||||
|
||||
public class MultiTAdminServiceImpl extends AbstractLifecycleBean implements TenantAdminService, TenantDeployerService
|
||||
{
|
||||
// Logger
|
||||
private static Log logger = LogFactory.getLog(MultiTAdminServiceImpl.class);
|
||||
|
||||
// Dependencies
|
||||
private DbNodeServiceImpl nodeService; // TODO - replace with NodeService, when deleteStore is exposed via public API
|
||||
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;
|
||||
|
||||
|
||||
protected final static String REGEX_VALID_TENANT_NAME = "^[a-zA-Z0-9]([a-zA-Z0-9]|.[a-zA-Z0-9])*$"; // note: must also be a valid filename
|
||||
|
||||
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 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_VERSION = "lightWeightVersionStore";
|
||||
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<TenantDeployer> tenantDeployers = new ArrayList<TenantDeployer>();
|
||||
|
||||
|
||||
@Override
|
||||
protected void onBootstrap(ApplicationEvent event)
|
||||
{
|
||||
// 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();
|
||||
authenticationComponent.setSystemUserAsCurrentUser();
|
||||
|
||||
try
|
||||
{
|
||||
userTransaction.begin();
|
||||
|
||||
// bootstrap Tenant Service internal cache
|
||||
List<Tenant> tenants = getAllTenants();
|
||||
|
||||
if (tenants != null)
|
||||
{
|
||||
for (Tenant tenant : tenants)
|
||||
{
|
||||
if (tenant.isEnabled())
|
||||
{
|
||||
// this will also call tenant deployers registered so far ...
|
||||
enableTenant(tenant.getTenantDomain(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// explicitly disable, without calling disableTenant callback
|
||||
disableTenant(tenant.getTenantDomain(), false);
|
||||
}
|
||||
}
|
||||
|
||||
tenantService.register(this); // callback to refresh tenantStatus cache
|
||||
}
|
||||
|
||||
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 bootstrap tenants", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShutdown(ApplicationEvent event)
|
||||
{
|
||||
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)
|
||||
{
|
||||
// Check that all the passed values are not null
|
||||
ParameterCheck.mandatory("tenantDomain", tenantDomain);
|
||||
ParameterCheck.mandatory("tenantAdminRawPassword", tenantAdminRawPassword);
|
||||
|
||||
if (! Pattern.matches(REGEX_VALID_TENANT_NAME, tenantDomain))
|
||||
{
|
||||
throw new IllegalArgumentException(tenantDomain + " is not a valid tenant name (must match " + REGEX_VALID_TENANT_NAME + ")");
|
||||
}
|
||||
|
||||
if (existsTenant(tenantDomain))
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Tenant already exists: " + tenantDomain);
|
||||
}
|
||||
else
|
||||
{
|
||||
authenticationComponent.setSystemUserAsCurrentUser();
|
||||
|
||||
if (rootContentStoreDir == null)
|
||||
{
|
||||
rootContentStoreDir = tenantFileContentStore.getDefaultRootDir();
|
||||
}
|
||||
|
||||
// init - need to enable tenant (including tenant service) before stores bootstrap
|
||||
Tenant tenant = new Tenant(tenantDomain, true, rootContentStoreDir);
|
||||
putTenantAttributes(tenantDomain, tenant);
|
||||
|
||||
AuthenticationUtil.runAs(new RunAsWork<Object>()
|
||||
{
|
||||
public Object doWork()
|
||||
{
|
||||
dictionaryComponent.init();
|
||||
tenantFileContentStore.init();
|
||||
|
||||
// create tenant-specific stores
|
||||
bootstrapUserTenantStore(tenantDomain, tenantAdminRawPassword);
|
||||
bootstrapSystemTenantStore(tenantDomain);
|
||||
bootstrapVersionTenantStore(tenantDomain);
|
||||
bootstrapSpacesArchiveTenantStore(tenantDomain);
|
||||
bootstrapSpacesTenantStore(tenantDomain);
|
||||
|
||||
// notify listeners that tenant has been created & hence enabled
|
||||
for (TenantDeployer tenantDeployer : tenantDeployers)
|
||||
{
|
||||
tenantDeployer.onEnableTenant();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}, getTenantAdminUser(tenantDomain));
|
||||
}
|
||||
|
||||
logger.info("Tenant created: " + 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 (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<Object>()
|
||||
{
|
||||
public Object doWork()
|
||||
{
|
||||
for (TenantDeployer tenantDeployer : tenantDeployers)
|
||||
{
|
||||
tenantDeployer.onEnableTenant();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, getTenantAdminUser(tenantDomain));
|
||||
}
|
||||
|
||||
logger.info("Tenant enabled: " + tenantDomain);
|
||||
}
|
||||
|
||||
public void disableTenant(String 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<Object>()
|
||||
{
|
||||
public Object doWork()
|
||||
{
|
||||
for (TenantDeployer tenantDeployer : tenantDeployers)
|
||||
{
|
||||
tenantDeployer.onDisableTenant();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, getTenantAdminUser(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)
|
||||
{
|
||||
return new Tenant(tenantDomain, isEnabledTenant(tenantDomain), getRootContentStoreDir(tenantDomain));
|
||||
}
|
||||
|
||||
public void bootstrapWorkflows()
|
||||
{
|
||||
// use this to deploy standard workflow process defs to the JBPM engine
|
||||
WorkflowDeployer workflowBootstrap = (WorkflowDeployer)getApplicationContext().getBean("workflowBootstrap");
|
||||
|
||||
String resourceClasspath = null;
|
||||
|
||||
// Workflow process definitions
|
||||
try
|
||||
{
|
||||
List<Properties> workflowDefs = workflowBootstrap.getWorkflowDefinitions();
|
||||
if (workflowDefs != null)
|
||||
{
|
||||
for (Properties workflowDefProps : workflowDefs)
|
||||
{
|
||||
resourceClasspath = workflowDefProps.getProperty(WorkflowDeployer.LOCATION);
|
||||
ClassPathResource resource = new ClassPathResource(resourceClasspath);
|
||||
workflowService.deployDefinition(workflowDefProps.getProperty(WorkflowDeployer.ENGINE_ID), resource.getInputStream(), workflowDefProps.getProperty(WorkflowDeployer.MIMETYPE));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to find workflow process def: " + resourceClasspath);
|
||||
}
|
||||
|
||||
logger.info("Tenant workflows bootstrapped: " + tenantService.getCurrentUserDomain());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TenantAdminService.deleteTenant()
|
||||
*/
|
||||
public void deleteTenant(String tenantDomain)
|
||||
{
|
||||
if (! existsTenant(tenantDomain))
|
||||
{
|
||||
throw new RuntimeException("Tenant does not exist: " + tenantDomain);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
final String tenantAdminUser = getTenantAdminUser(tenantDomain);
|
||||
|
||||
AuthenticationUtil.runAs(new RunAsWork<Object>()
|
||||
{
|
||||
public Object doWork()
|
||||
{
|
||||
List<WorkflowDefinition> workflowDefs = workflowService.getDefinitions();
|
||||
if (workflowDefs != null)
|
||||
{
|
||||
for (WorkflowDefinition workflowDef : workflowDefs)
|
||||
{
|
||||
workflowService.undeployDefinition(workflowDef.getId());
|
||||
}
|
||||
}
|
||||
|
||||
List<String> messageResourceBundles = repoAdminService.getMessageBundles();
|
||||
if (messageResourceBundles != null)
|
||||
{
|
||||
for (String messageResourceBundle : messageResourceBundles)
|
||||
{
|
||||
repoAdminService.undeployMessageBundle(messageResourceBundle);
|
||||
}
|
||||
}
|
||||
|
||||
List<RepoModelDefinition> models = repoAdminService.getModels();
|
||||
if (models != null)
|
||||
{
|
||||
for (RepoModelDefinition model : models)
|
||||
{
|
||||
repoAdminService.undeployModel(model.getRepoName());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}, tenantAdminUser);
|
||||
|
||||
|
||||
// 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_VERSION)));
|
||||
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<Object>()
|
||||
{
|
||||
public Object doWork()
|
||||
{
|
||||
for (TenantDeployer tenantDeployer : tenantDeployers)
|
||||
{
|
||||
tenantDeployer.onDisableTenant();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, tenantAdminUser);
|
||||
|
||||
// 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<Tenant> getAllTenants()
|
||||
{
|
||||
MapAttribute map = (MapAttribute)attributeService.getAttribute(TENANTS_ATTRIBUTE_PATH);
|
||||
|
||||
List<Tenant> tenants = new ArrayList<Tenant>();
|
||||
|
||||
if (map != null)
|
||||
{
|
||||
// note: getAllTenants is called first, by TenantDeployer - hence need to initialise the TenantService status cache
|
||||
Set<String> 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 bootstrapUserTenantStore(String tenantDomain, char[] tenantAdminRawPassword)
|
||||
{
|
||||
// Bootstrap Tenant-Specific User Store
|
||||
StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_USER, tenantService.getName(STORE_BASE_ID_USER, tenantDomain));
|
||||
|
||||
ImporterBootstrap userImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("userBootstrap");
|
||||
userImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());
|
||||
|
||||
// override admin username property
|
||||
String salt = null; // GUID.generate();
|
||||
Properties props = userImporterBootstrap.getConfiguration();
|
||||
|
||||
props.put("alfresco_user_store.adminusername", getTenantAdminUser(tenantDomain));
|
||||
props.put("alfresco_user_store.adminpassword", passwordEncoder.encodePassword(new String(tenantAdminRawPassword), salt));
|
||||
|
||||
// override guest username property
|
||||
props.put("alfresco_user_store.guestusername", getTenantGuestUser(tenantDomain));
|
||||
|
||||
userImporterBootstrap.bootstrap();
|
||||
|
||||
logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef));
|
||||
}
|
||||
|
||||
private void bootstrapSystemTenantStore(String tenantDomain)
|
||||
{
|
||||
// Bootstrap Tenant-Specific System Store
|
||||
StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_SYSTEM, tenantService.getName(STORE_BASE_ID_SYSTEM, tenantDomain));
|
||||
|
||||
ImporterBootstrap systemImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("systemBootstrap");
|
||||
systemImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());
|
||||
|
||||
// override default property (workspace://SpacesStore)
|
||||
List<String> mustNotExistStoreUrls = new ArrayList<String>();
|
||||
mustNotExistStoreUrls.add(new StoreRef(PROTOCOL_STORE_WORKSPACE, tenantService.getName(STORE_BASE_ID_USER, tenantDomain)).toString());
|
||||
systemImporterBootstrap.setMustNotExistStoreUrls(mustNotExistStoreUrls);
|
||||
|
||||
systemImporterBootstrap.bootstrap();
|
||||
|
||||
logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef));
|
||||
}
|
||||
|
||||
private void bootstrapVersionTenantStore(String tenantDomain)
|
||||
{
|
||||
// Bootstrap Tenant-Specific Version Store
|
||||
StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_WORKSPACE, tenantService.getName(STORE_BASE_ID_VERSION, tenantDomain));
|
||||
|
||||
ImporterBootstrap versionImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("versionBootstrap");
|
||||
versionImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());
|
||||
|
||||
versionImporterBootstrap.bootstrap();
|
||||
|
||||
logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef));
|
||||
}
|
||||
|
||||
private void bootstrapSpacesArchiveTenantStore(String tenantDomain)
|
||||
{
|
||||
// Bootstrap Tenant-Specific Spaces Store
|
||||
StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_ARCHIVE, tenantService.getName(STORE_BASE_ID_SPACES, tenantDomain));
|
||||
|
||||
ImporterBootstrap spacesArchiveImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("spacesArchiveBootstrap");
|
||||
spacesArchiveImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());
|
||||
|
||||
// override default property (archive://SpacesStore)
|
||||
List<String> mustNotExistStoreUrls = new ArrayList<String>();
|
||||
mustNotExistStoreUrls.add(new StoreRef(PROTOCOL_STORE_ARCHIVE, tenantService.getName(STORE_BASE_ID_SPACES, tenantDomain)).toString());
|
||||
spacesArchiveImporterBootstrap.setMustNotExistStoreUrls(mustNotExistStoreUrls);
|
||||
|
||||
spacesArchiveImporterBootstrap.bootstrap();
|
||||
|
||||
logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef));
|
||||
}
|
||||
|
||||
private void bootstrapSpacesTenantStore(String tenantDomain)
|
||||
{
|
||||
// Bootstrap Tenant-Specific Spaces Store
|
||||
StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_WORKSPACE, tenantService.getName(STORE_BASE_ID_SPACES, tenantDomain));
|
||||
|
||||
final ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("spacesBootstrap");
|
||||
spacesImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());
|
||||
|
||||
// override admin username property
|
||||
Properties props = spacesImporterBootstrap.getConfiguration();
|
||||
props.put("alfresco_user_store.adminusername", getTenantAdminUser(tenantDomain));
|
||||
|
||||
spacesImporterBootstrap.bootstrap();
|
||||
|
||||
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<Tenant> 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);
|
||||
}
|
||||
|
||||
String currentUser = AuthenticationUtil.getCurrentUserName();
|
||||
|
||||
if (tenants != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
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<Object>()
|
||||
{
|
||||
public Object doWork()
|
||||
{
|
||||
// init the service within tenant context
|
||||
deployer.init();
|
||||
return null;
|
||||
}
|
||||
}, getTenantAdminUser(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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (currentUser != null) { AuthenticationUtil.setCurrentUser(currentUser); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<Tenant> 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);
|
||||
}
|
||||
|
||||
String currentUser = AuthenticationUtil.getCurrentUserName();
|
||||
|
||||
if (tenants != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
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<Object>()
|
||||
{
|
||||
public Object doWork()
|
||||
{
|
||||
// destroy the service within tenant context
|
||||
deployer.destroy();
|
||||
return null;
|
||||
}
|
||||
}, getTenantAdminUser(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
|
||||
{
|
||||
if (currentUser != null) { AuthenticationUtil.setCurrentUser(currentUser); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 boolean isEnabled()
|
||||
{
|
||||
return tenantService.isEnabled();
|
||||
}
|
||||
|
||||
public void resetCache(String tenantDomain)
|
||||
{
|
||||
if (existsTenant(tenantDomain))
|
||||
{
|
||||
if (isEnabledTenant(tenantDomain))
|
||||
{
|
||||
enableTenant(tenantDomain);
|
||||
}
|
||||
else
|
||||
{
|
||||
disableTenant(tenantDomain);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new AlfrescoRuntimeException("No such tenant " + tenantDomain);
|
||||
}
|
||||
}
|
||||
|
||||
// local helper
|
||||
private String getTenantAdminUser(String tenantDomain)
|
||||
{
|
||||
return tenantService.getDomainUser(ADMIN_BASENAME, tenantDomain);
|
||||
}
|
||||
|
||||
// local helper
|
||||
private String getTenantGuestUser(String tenantDomain)
|
||||
{
|
||||
return tenantService.getDomainUser(authenticationComponent.getGuestUserName(), tenantDomain);
|
||||
}
|
||||
}
|
568
source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java
Executable file
568
source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java
Executable file
@@ -0,0 +1,568 @@
|
||||
/*
|
||||
* 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 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.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<String, Tenant> tenantsCache;
|
||||
|
||||
private MultiTAdminServiceImpl tenantAdminService = null; // registered (rather than injected) - to avoid circular dependency
|
||||
|
||||
|
||||
public void setTenantsCache(SimpleCache<String, Tenant> tenantsCache)
|
||||
{
|
||||
this.tenantsCache = tenantsCache;
|
||||
}
|
||||
|
||||
public NodeRef getName(NodeRef nodeRef)
|
||||
{
|
||||
// Check that all the passed values are not null
|
||||
ParameterCheck.mandatory("NodeRef", nodeRef);
|
||||
|
||||
return new NodeRef(nodeRef.getStoreRef().getProtocol(), getName(nodeRef.getStoreRef().getIdentifier()), nodeRef.getId());
|
||||
}
|
||||
|
||||
public NodeRef getName(NodeRef inNodeRef, NodeRef nodeRef)
|
||||
{
|
||||
// Check that all the passed values are not null
|
||||
ParameterCheck.mandatory("InNodeRef", inNodeRef);
|
||||
ParameterCheck.mandatory("NodeRef", nodeRef);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public StoreRef getName(StoreRef storeRef)
|
||||
{
|
||||
// Check that all the passed values are not null
|
||||
ParameterCheck.mandatory("StoreRef", storeRef);
|
||||
|
||||
return new StoreRef(storeRef.getProtocol(), getName(storeRef.getIdentifier()));
|
||||
}
|
||||
|
||||
public StoreRef getName(String username, StoreRef storeRef)
|
||||
{
|
||||
// Check that all the passed values are not null
|
||||
ParameterCheck.mandatory("StoreRef", storeRef);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public String getName(String name)
|
||||
{
|
||||
// Check that all the passed values are not null
|
||||
ParameterCheck.mandatory("name", name);
|
||||
|
||||
String tenantDomain = getCurrentUserDomain();
|
||||
|
||||
if (! tenantDomain.equals(""))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
public QName getBaseName(QName name, boolean forceForNonTenant)
|
||||
{
|
||||
String baseNamespaceURI = getBaseName(name.getNamespaceURI(), forceForNonTenant);
|
||||
return QName.createQName(baseNamespaceURI, name.getLocalName());
|
||||
}
|
||||
|
||||
public NodeRef getBaseName(NodeRef nodeRef)
|
||||
{
|
||||
// Check that all the passed values are not null
|
||||
ParameterCheck.mandatory("NodeRef", nodeRef);
|
||||
|
||||
return new NodeRef(nodeRef.getStoreRef().getProtocol(), getBaseName(nodeRef.getStoreRef().getIdentifier()), nodeRef.getId());
|
||||
}
|
||||
|
||||
public StoreRef getBaseName(StoreRef storeRef)
|
||||
{
|
||||
// Check that all the passed values are not null
|
||||
ParameterCheck.mandatory("StoreRef", storeRef);
|
||||
|
||||
return new StoreRef(storeRef.getProtocol(), getBaseName(storeRef.getIdentifier()));
|
||||
}
|
||||
|
||||
public String getBaseName(String name)
|
||||
{
|
||||
// get base name, but don't force for non-tenant user (e.g. super admin)
|
||||
return getBaseName(name, false);
|
||||
}
|
||||
|
||||
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("")) && (! tenantDomain.equals(nameDomain)))
|
||||
{
|
||||
throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain);
|
||||
}
|
||||
|
||||
if ((! tenantDomain.equals("")) || (forceForNonTenant))
|
||||
{
|
||||
// remove tenant domain
|
||||
name = name.substring(idx2+1);
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public void checkDomainUser(String username)
|
||||
{
|
||||
// Check that all the passed values are not null
|
||||
ParameterCheck.mandatory("Username", username);
|
||||
|
||||
String tenantDomain = getCurrentUserDomain();
|
||||
|
||||
if (! tenantDomain.equals(""))
|
||||
{
|
||||
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 = <none>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(""))) ||
|
||||
((nameDomain != null) && (! nameDomain.equals(tenantDomain))))
|
||||
{
|
||||
throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain);
|
||||
}
|
||||
}
|
||||
|
||||
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.getCurrentUserName();
|
||||
StoreRef storeRef = getName(username, rootNodeRef.getStoreRef());
|
||||
|
||||
AuthenticationUtil.RunAsWork<NodeRef> action = new GetRootNode(nodeService, searchService, namespaceService, rootPath, rootNodeRef, storeRef);
|
||||
return getBaseName(AuthenticationUtil.runAs(action, AuthenticationUtil.getSystemUserName()));
|
||||
}
|
||||
|
||||
private class GetRootNode implements AuthenticationUtil.RunAsWork<NodeRef>
|
||||
{
|
||||
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<NodeRef> 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;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTenantUser()
|
||||
{
|
||||
return isTenantUser(AuthenticationUtil.getCurrentUserName());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public String getCurrentUserDomain()
|
||||
{
|
||||
String user = AuthenticationUtil.getCurrentUserName();
|
||||
|
||||
// can be null (e.g. for System user / during app ctx init)
|
||||
if (user != null)
|
||||
{
|
||||
int idx = user.lastIndexOf(SEPARATOR);
|
||||
if ((idx > 0) && (idx < (user.length()-1)))
|
||||
{
|
||||
String tenantDomain = user.substring(idx+1);
|
||||
|
||||
checkTenantEnabled(tenantDomain);
|
||||
|
||||
return tenantDomain;
|
||||
}
|
||||
}
|
||||
|
||||
return ""; // default domain - non-tenant user
|
||||
}
|
||||
|
||||
public String getDomain(String name)
|
||||
{
|
||||
// Check that all the passed values are not null
|
||||
ParameterCheck.mandatory("name", name);
|
||||
|
||||
String tenantDomain = getCurrentUserDomain();
|
||||
|
||||
String nameDomain = "";
|
||||
|
||||
int idx1 = name.indexOf(SEPARATOR);
|
||||
if (idx1 == 0)
|
||||
{
|
||||
int idx2 = name.indexOf(SEPARATOR, 1);
|
||||
nameDomain = name.substring(1, idx2);
|
||||
|
||||
if ((! tenantDomain.equals("")) && (! tenantDomain.equals(nameDomain)))
|
||||
{
|
||||
throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain);
|
||||
}
|
||||
}
|
||||
|
||||
return nameDomain;
|
||||
}
|
||||
|
||||
public String getDomainUser(String baseUsername, String tenantDomain)
|
||||
{
|
||||
// Check that all the passed values are not null
|
||||
ParameterCheck.mandatory("baseUsername", baseUsername);
|
||||
ParameterCheck.mandatory("tenantDomain", tenantDomain);
|
||||
|
||||
if (! tenantDomain.equals(""))
|
||||
{
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
return baseUsername;
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkTenantEnabled(String tenantDomain)
|
||||
{
|
||||
if (getTenant(tenantDomain).isEnabled() == false)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Tenant is not enabled: " + tenantDomain);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@@ -112,11 +112,6 @@ public class SingleTServiceImpl implements TenantService
|
||||
{
|
||||
return rootNodeRef;
|
||||
}
|
||||
|
||||
public NodeRef getCompanyHomeNode(NodeService nodeService, String username, StoreRef storeRef)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isTenantUser()
|
||||
{
|
||||
|
60
source/java/org/alfresco/repo/tenant/TenantAdminService.java
Executable file
60
source/java/org/alfresco/repo/tenant/TenantAdminService.java
Executable file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Tenant Admin Service interface.
|
||||
* <p>
|
||||
* This interface provides administrative methods to provision and administer tenants.
|
||||
*
|
||||
*/
|
||||
|
||||
public interface TenantAdminService extends TenantDeployerService
|
||||
{
|
||||
public void createTenant(String tenantDomain, char[] adminRawPassword);
|
||||
|
||||
public void createTenant(String tenantDomain, char[] adminRawPassword, String rootContentStoreDir);
|
||||
|
||||
public boolean existsTenant(String tenantDomain);
|
||||
|
||||
public void bootstrapWorkflows();
|
||||
|
||||
public void deleteTenant(String tenantDomain);
|
||||
|
||||
public List<Tenant> getAllTenants();
|
||||
|
||||
public void enableTenant(String tenantDomain);
|
||||
|
||||
public void disableTenant(String tenantDomain);
|
||||
|
||||
public Tenant getTenant(String tenantDomain);
|
||||
|
||||
public boolean isEnabledTenant(String tenantDomain);
|
||||
|
||||
public boolean isEnabled();
|
||||
}
|
332
source/java/org/alfresco/repo/tenant/TenantInterpreter.java
Executable file
332
source/java/org/alfresco/repo/tenant/TenantInterpreter.java
Executable file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* 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 java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.i18n.I18NUtil;
|
||||
import org.alfresco.repo.admin.BaseInterpreter;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.service.cmr.security.AuthenticationService;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
/**
|
||||
* An interactive console for Tenants.
|
||||
*
|
||||
*/
|
||||
public class TenantInterpreter extends BaseInterpreter
|
||||
{
|
||||
// Service dependencies
|
||||
|
||||
private TenantAdminService tenantAdminService;
|
||||
|
||||
private AuthenticationService authenticationService;
|
||||
|
||||
public void setTenantAdminService(TenantAdminService tenantAdminService)
|
||||
{
|
||||
this.tenantAdminService = tenantAdminService;
|
||||
}
|
||||
|
||||
public void setAuthenticationService(AuthenticationService authenticationService)
|
||||
{
|
||||
this.authenticationService = authenticationService;
|
||||
}
|
||||
|
||||
public static BaseInterpreter getConsoleBean(ApplicationContext context)
|
||||
{
|
||||
return (TenantInterpreter)context.getBean("tenantInterpreter");
|
||||
}
|
||||
|
||||
|
||||
protected boolean hasAuthority(String username)
|
||||
{
|
||||
// must be super "admin" for tenant administrator
|
||||
return ((username != null) && (username.equals(BaseInterpreter.DEFAULT_ADMIN)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a single command using the BufferedReader passed in for any data needed.
|
||||
*
|
||||
* TODO: Use decent parser!
|
||||
*
|
||||
* @param line The unparsed command
|
||||
* @return The textual output of the command.
|
||||
*/
|
||||
public String executeCommand(String line)
|
||||
throws IOException
|
||||
{
|
||||
String[] command = line.split(" ");
|
||||
if (command.length == 0)
|
||||
{
|
||||
command = new String[1];
|
||||
command[0] = line;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
PrintStream out = new PrintStream(bout);
|
||||
|
||||
// repeat last command?
|
||||
if (command[0].equals("r"))
|
||||
{
|
||||
if (lastCommand == null)
|
||||
{
|
||||
return "No command entered yet.";
|
||||
}
|
||||
return "repeating command " + lastCommand + "\n\n" + executeCommand(lastCommand);
|
||||
}
|
||||
|
||||
// remember last command
|
||||
lastCommand = line;
|
||||
|
||||
// execute command
|
||||
if (command[0].equals("help"))
|
||||
{
|
||||
String helpFile = I18NUtil.getMessage("tenant_console.help");
|
||||
ClassPathResource helpResource = new ClassPathResource(helpFile);
|
||||
byte[] helpBytes = new byte[500];
|
||||
InputStream helpStream = helpResource.getInputStream();
|
||||
try
|
||||
{
|
||||
int read = helpStream.read(helpBytes);
|
||||
while (read != -1)
|
||||
{
|
||||
bout.write(helpBytes, 0, read);
|
||||
read = helpStream.read(helpBytes);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
helpStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
else if (command[0].equals("show"))
|
||||
{
|
||||
if (command.length < 2)
|
||||
{
|
||||
return "Syntax Error, try 'help'.\n";
|
||||
}
|
||||
|
||||
else if (command[1].equals("tenants"))
|
||||
{
|
||||
List<Tenant> tenants = tenantAdminService.getAllTenants();
|
||||
|
||||
for (Tenant tenant : tenants)
|
||||
{
|
||||
if (tenant.isEnabled())
|
||||
{
|
||||
String rootContentStoreDir = tenant.getRootContentStoreDir();
|
||||
out.println("Enabled - Tenant: " + tenant.getTenantDomain() + " (" + rootContentStoreDir + ")");
|
||||
}
|
||||
}
|
||||
|
||||
out.println("");
|
||||
|
||||
for (Tenant tenant : tenants)
|
||||
{
|
||||
if (! tenant.isEnabled())
|
||||
{
|
||||
String rootContentStoreDir = tenant.getRootContentStoreDir();
|
||||
out.println("Disabled - Tenant: " + tenant.getTenantDomain() + " (" + rootContentStoreDir + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (command[1].equals("tenant"))
|
||||
{
|
||||
if (command.length != 3)
|
||||
{
|
||||
return "Syntax Error, try 'help'.\n";
|
||||
}
|
||||
|
||||
String tenantDomain = new String(command[2]).toLowerCase();
|
||||
Tenant tenant = tenantAdminService.getTenant(tenantDomain);
|
||||
|
||||
String rootContentStoreDir = tenant.getRootContentStoreDir();
|
||||
if (tenant.isEnabled())
|
||||
{
|
||||
out.println("Enabled - Tenant: " + tenant.getTenantDomain() + " (" + rootContentStoreDir + ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
out.println("Disabled - Tenant: " + tenant.getTenantDomain() + " (" + rootContentStoreDir + ")");
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return "No such sub-command, try 'help'.\n";
|
||||
}
|
||||
}
|
||||
|
||||
else if (command[0].equals("create"))
|
||||
{
|
||||
if ((command.length != 3) && (command.length != 4))
|
||||
{
|
||||
return "Syntax Error, try 'help'.\n";
|
||||
}
|
||||
|
||||
String newTenant = new String(command[1]).toLowerCase();
|
||||
String tenantAdminRawPassword = new String(command[2]);
|
||||
|
||||
String createTenantArgs = newTenant + " " + tenantAdminRawPassword;
|
||||
if (command.length == 4)
|
||||
{
|
||||
createTenantArgs = createTenantArgs + " " + new String(command[3]);
|
||||
}
|
||||
|
||||
out.print(executeCommand("createWithoutWorkflows " + createTenantArgs));
|
||||
out.print(executeCommand("bootstrapWorkflows " + newTenant));
|
||||
}
|
||||
|
||||
else if (command[0].equals("createWithoutWorkflows"))
|
||||
{
|
||||
if ((command.length != 3) && (command.length != 4))
|
||||
{
|
||||
return "Syntax Error, try 'help'.\n";
|
||||
}
|
||||
|
||||
String newTenant = new String(command[1]).toLowerCase();
|
||||
char[] tenantAdminRawPassword = new String(command[2]).toCharArray();
|
||||
String rootContentStoreDir = null;
|
||||
if (command.length == 4)
|
||||
{
|
||||
rootContentStoreDir = new String(command[3]);
|
||||
}
|
||||
|
||||
tenantAdminService.createTenant(newTenant, tenantAdminRawPassword, rootContentStoreDir);
|
||||
|
||||
out.println("created tenant: " + newTenant);
|
||||
}
|
||||
|
||||
else if (command[0].equals("bootstrapWorkflows"))
|
||||
{
|
||||
if (command.length != 2)
|
||||
{
|
||||
return "Syntax Error, try 'help'.\n";
|
||||
}
|
||||
|
||||
String newTenant = new String(command[1]).toLowerCase();
|
||||
|
||||
String tenantAdminUsername = tenantService.getDomainUser(TenantService.ADMIN_BASENAME, newTenant);
|
||||
AuthenticationUtil.runAs(new RunAsWork<Object>()
|
||||
{
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
tenantAdminService.bootstrapWorkflows();
|
||||
return null;
|
||||
}
|
||||
}, tenantAdminUsername);
|
||||
|
||||
out.println("bootstrap workflows deployed for tenant: " + newTenant);
|
||||
}
|
||||
|
||||
// TODO - not fully working yet
|
||||
else if (command[0].equals("delete"))
|
||||
{
|
||||
if (command.length != 2)
|
||||
{
|
||||
return "Syntax Error, try 'help'.\n";
|
||||
}
|
||||
|
||||
String tenantDomain = new String(command[1]).toLowerCase();
|
||||
|
||||
tenantAdminService.deleteTenant(tenantDomain);
|
||||
out.println("Deleted tenant: " + tenantDomain);
|
||||
}
|
||||
|
||||
else if (command[0].equals("enable"))
|
||||
{
|
||||
if (command.length != 2)
|
||||
{
|
||||
return "Syntax Error, try 'help'.\n";
|
||||
}
|
||||
|
||||
String tenantDomain = new String(command[1]).toLowerCase();
|
||||
|
||||
tenantAdminService.enableTenant(tenantDomain);
|
||||
out.println("Enabled tenant: " + tenantDomain);
|
||||
}
|
||||
|
||||
else if (command[0].equals("disable"))
|
||||
{
|
||||
if (command.length != 2)
|
||||
{
|
||||
return "Syntax Error, try 'help'.\n";
|
||||
}
|
||||
|
||||
String tenantDomain = new String(command[1]).toLowerCase();
|
||||
|
||||
tenantAdminService.disableTenant(tenantDomain);
|
||||
out.println("Disabled tenant: " + tenantDomain);
|
||||
}
|
||||
|
||||
else if (command[0].equals("changeAdminPassword"))
|
||||
{
|
||||
if (command.length != 3)
|
||||
{
|
||||
return "Syntax Error, try 'help'.\n";
|
||||
}
|
||||
|
||||
String tenantDomain = new String(command[1]).toLowerCase();
|
||||
|
||||
final String newPassword = new String(command[2]);
|
||||
final String tenantAdminUsername = tenantService.getDomainUser(TenantService.ADMIN_BASENAME, tenantDomain);
|
||||
|
||||
AuthenticationUtil.runAs(new RunAsWork<Object>()
|
||||
{
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
RetryingTransactionCallback<Object> txnWork = new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
public Object execute() throws Exception
|
||||
{
|
||||
authenticationService.setAuthentication(tenantAdminUsername, newPassword.toCharArray());
|
||||
return null;
|
||||
}
|
||||
};
|
||||
return transactionService.getRetryingTransactionHelper().doInTransaction(txnWork);
|
||||
}
|
||||
}, tenantAdminUsername);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return "No such command, try 'help'.\n";
|
||||
}
|
||||
|
||||
out.flush();
|
||||
String retVal = new String(bout.toByteArray());
|
||||
out.close();
|
||||
return retVal;
|
||||
}
|
||||
}
|
@@ -35,7 +35,7 @@ import org.alfresco.service.namespace.QName;
|
||||
/**
|
||||
* Tenant Service interface.
|
||||
* <p>
|
||||
* This interface provides methods to support either Single-Tenant or Multi-Tenant implementations.
|
||||
* This interface provides methods to support either ST or MT implementations.
|
||||
*
|
||||
*/
|
||||
public interface TenantService
|
||||
@@ -74,8 +74,6 @@ public interface TenantService
|
||||
|
||||
public NodeRef getRootNode(NodeService nodeService, SearchService searchService, NamespaceService namespaceService, String rootPath, NodeRef rootNodeRef);
|
||||
|
||||
public NodeRef getCompanyHomeNode(NodeService nodeService, String username, StoreRef storeRef);
|
||||
|
||||
public boolean isTenantUser();
|
||||
|
||||
public boolean isTenantUser(String username);
|
||||
|
@@ -34,6 +34,9 @@ import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.dictionary.DictionaryBootstrap;
|
||||
import org.alfresco.repo.dictionary.DictionaryDAO;
|
||||
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.TenantService;
|
||||
import org.alfresco.service.cmr.view.ImporterException;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowDeployment;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowException;
|
||||
@@ -71,6 +74,7 @@ public class WorkflowDeployer extends AbstractLifecycleBean
|
||||
private List<Properties> workflowDefinitions;
|
||||
private List<String> models = new ArrayList<String>();
|
||||
private List<String> resourceBundles = new ArrayList<String>();
|
||||
private TenantService tenantService;
|
||||
|
||||
|
||||
/**
|
||||
@@ -123,6 +127,16 @@ public class WorkflowDeployer extends AbstractLifecycleBean
|
||||
this.dictionaryDAO = dictionaryDAO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tenant service
|
||||
*
|
||||
* @param tenantService the tenant service
|
||||
*/
|
||||
public void setTenantService(TenantService tenantService)
|
||||
{
|
||||
this.tenantService = tenantService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Workflow Definitions
|
||||
*
|
||||
@@ -144,7 +158,7 @@ public class WorkflowDeployer extends AbstractLifecycleBean
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial list of Workflow reosurce bundles to bootstrap with
|
||||
* Sets the initial list of Workflow resource bundles to bootstrap with
|
||||
*
|
||||
* @param modelResources the model names
|
||||
*/
|
||||
@@ -152,11 +166,17 @@ public class WorkflowDeployer extends AbstractLifecycleBean
|
||||
{
|
||||
this.resourceBundles = labels;
|
||||
}
|
||||
|
||||
// used by TenantAdminService when creating a new tenant and bootstrapping the pre-defined workflows
|
||||
public List<Properties> getWorkflowDefinitions()
|
||||
{
|
||||
return this.workflowDefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy the Workflow Definitions
|
||||
*/
|
||||
public void deploy()
|
||||
public void init()
|
||||
{
|
||||
if (transactionService == null)
|
||||
{
|
||||
@@ -171,24 +191,30 @@ public class WorkflowDeployer extends AbstractLifecycleBean
|
||||
throw new ImporterException("Workflow Service must be provided");
|
||||
}
|
||||
|
||||
String currentUser = authenticationComponent.getCurrentUserName();
|
||||
if (currentUser == null)
|
||||
{
|
||||
authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
|
||||
}
|
||||
|
||||
UserTransaction userTransaction = transactionService.getUserTransaction();
|
||||
authenticationComponent.setSystemUserAsCurrentUser();
|
||||
|
||||
try
|
||||
{
|
||||
userTransaction.begin();
|
||||
|
||||
// bootstrap the workflow models and labels
|
||||
// bootstrap the workflow models and static labels (from classpath)
|
||||
if (models != null && resourceBundles != null)
|
||||
{
|
||||
DictionaryBootstrap dictionaryBootstrap = new DictionaryBootstrap();
|
||||
dictionaryBootstrap.setDictionaryDAO(dictionaryDAO);
|
||||
dictionaryBootstrap.setTenantService(tenantService);
|
||||
dictionaryBootstrap.setModels(models);
|
||||
dictionaryBootstrap.setLabels(resourceBundles);
|
||||
dictionaryBootstrap.bootstrap();
|
||||
dictionaryBootstrap.bootstrap(); // also registers with dictionary
|
||||
}
|
||||
|
||||
// bootstrap the workflow definitions
|
||||
// bootstrap the workflow definitions (from classpath)
|
||||
if (workflowDefinitions != null)
|
||||
{
|
||||
for (Properties workflowDefinition : workflowDefinitions)
|
||||
@@ -239,19 +265,29 @@ public class WorkflowDeployer extends AbstractLifecycleBean
|
||||
{
|
||||
// rollback the transaction
|
||||
try { if (userTransaction != null) {userTransaction.rollback();} } catch (Exception ex) {}
|
||||
try {authenticationComponent.clearCurrentSecurityContext(); } catch (Exception ex) {}
|
||||
throw new AlfrescoRuntimeException("Workflow deployment failed", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
authenticationComponent.clearCurrentSecurityContext();
|
||||
if (currentUser == null)
|
||||
{
|
||||
authenticationComponent.clearCurrentSecurityContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBootstrap(ApplicationEvent event)
|
||||
{
|
||||
deploy();
|
||||
// run as System on bootstrap
|
||||
AuthenticationUtil.runAs(new RunAsWork<Object>()
|
||||
{
|
||||
public Object doWork()
|
||||
{
|
||||
init();
|
||||
return null;
|
||||
}
|
||||
}, AuthenticationUtil.getSystemUserName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Reference in New Issue
Block a user