001/* 002 * (C) Copyright 2006-2012 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Thomas Roger <troger@nuxeo.com> 016 */ 017 018package org.nuxeo.ecm.multi.tenant; 019 020import static org.nuxeo.ecm.multi.tenant.Constants.TENANT_ADMINISTRATORS_GROUP_SUFFIX; 021import static org.nuxeo.ecm.multi.tenant.Constants.TENANT_CONFIG_FACET; 022import static org.nuxeo.ecm.multi.tenant.Constants.TENANT_GROUP_PREFIX; 023import static org.nuxeo.ecm.multi.tenant.Constants.TENANT_MEMBERS_GROUP_SUFFIX; 024 025import java.security.Principal; 026import java.util.ArrayList; 027import java.util.List; 028import java.util.concurrent.TimeUnit; 029 030import org.nuxeo.ecm.core.api.CoreSession; 031import org.nuxeo.ecm.core.api.DocumentModel; 032import org.nuxeo.ecm.core.api.SystemPrincipal; 033import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 034import org.nuxeo.ecm.core.api.local.ClientLoginModule; 035import org.nuxeo.ecm.platform.usermanager.UserManager; 036import org.nuxeo.runtime.api.Framework; 037 038import com.google.common.cache.Cache; 039import com.google.common.cache.CacheBuilder; 040 041/** 042 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a> 043 * @since 5.6 044 */ 045public class MultiTenantHelper { 046 047 protected static final Integer CACHE_CONCURRENCY_LEVEL = 10; 048 049 protected static final Integer CACHE_MAXIMUM_SIZE = 1000; 050 051 protected static final Integer CACHE_TIMEOUT = 10; 052 053 protected static final String NO_TENANT = "NO_TENANT"; 054 055 protected final static Cache<String, String> pathCache = CacheBuilder.newBuilder().concurrencyLevel( 056 CACHE_CONCURRENCY_LEVEL).maximumSize(CACHE_MAXIMUM_SIZE).expireAfterWrite(CACHE_TIMEOUT, TimeUnit.MINUTES).build(); 057 058 protected final static Cache<String, String> tenantBinding = CacheBuilder.newBuilder().concurrencyLevel( 059 CACHE_CONCURRENCY_LEVEL).maximumSize(CACHE_MAXIMUM_SIZE).expireAfterWrite(CACHE_TIMEOUT, TimeUnit.MINUTES).build(); 060 061 private MultiTenantHelper() { 062 // helper class 063 } 064 065 public static String computeTenantAdministratorsGroup(String tenantId) { 066 return TENANT_GROUP_PREFIX + tenantId + TENANT_ADMINISTRATORS_GROUP_SUFFIX; 067 } 068 069 public static String computeTenantMembersGroup(String tenantId) { 070 return TENANT_GROUP_PREFIX + tenantId + TENANT_MEMBERS_GROUP_SUFFIX; 071 } 072 073 /** 074 * Returns the current tenantId for the given {@code principal}, or from the principal stored in the login stack. 075 * <p> 076 * The {@code principal} is used if it is a {@link SystemPrincipal}, then the tenantId is retrieved from the 077 * Principal matching the {@link SystemPrincipal#getOriginatingUser()}. 078 */ 079 public static String getCurrentTenantId(Principal principal) { 080 if (principal instanceof SystemPrincipal) { 081 String originatingUser = ((SystemPrincipal) principal).getOriginatingUser(); 082 if (originatingUser != null) { 083 return getTenantId(originatingUser); 084 } else { 085 return null; 086 } 087 088 } else { 089 return ClientLoginModule.getCurrentPrincipal().getTenantId(); 090 } 091 } 092 093 /** 094 * Returns the tenantId for the given {@code username} if any, {@code null} otherwise. 095 */ 096 public static String getTenantId(String username) { 097 UserManager userManager = Framework.getLocalService(UserManager.class); 098 String tenantId = null; 099 DocumentModel userModel = userManager.getUserModel(username); 100 if (userModel != null) { 101 tenantId = (String) userModel.getPropertyValue("user:tenantId"); 102 } 103 return tenantId; 104 } 105 106 /** 107 * Returns the path of the tenant document matching the {@code tenantId}, or {@code null} if there is no document 108 * matching. 109 */ 110 public static String getTenantDocumentPath(CoreSession session, final String tenantId) { 111 final List<String> paths = new ArrayList<String>(); 112 String path = pathCache.getIfPresent(tenantId); 113 if (path == null) { 114 new UnrestrictedSessionRunner(session) { 115 @Override 116 public void run() { 117 String query = String.format("SELECT * FROM Document WHERE tenantconfig:tenantId = '%s'", tenantId); 118 List<DocumentModel> docs = session.query(query); 119 if (!docs.isEmpty()) { 120 paths.add(docs.get(0).getPathAsString()); 121 } 122 } 123 }.runUnrestricted(); 124 path = paths.isEmpty() ? null : paths.get(0); 125 if (path != null) { 126 pathCache.put(tenantId, path); 127 } 128 } 129 return path; 130 } 131 132 /** 133 * Return the Tenant containing the provided DocumentModel if any 134 * 135 * @param doc 136 * @return DocumentModel corresponding to the Tenant container, null otherwise 137 */ 138 public static String getOwningTenantId(final DocumentModel doc) { 139 String tenantId = tenantBinding.getIfPresent(doc.getId()); 140 if (NO_TENANT.equals(tenantId)) { 141 return null; 142 } 143 144 if (tenantId == null) { 145 TenantIdFinder finder = new TenantIdFinder(doc); 146 finder.runUnrestricted(); 147 tenantId = finder.getTenantId(); 148 149 if (tenantId == null) { 150 tenantBinding.put(doc.getId(), NO_TENANT); 151 } else { 152 tenantBinding.put(doc.getId(), tenantId); 153 } 154 } 155 return tenantId; 156 } 157 158 protected static class TenantIdFinder extends UnrestrictedSessionRunner { 159 160 protected String tenantId; 161 162 protected final DocumentModel target; 163 164 protected TenantIdFinder(DocumentModel target) { 165 super(target.getCoreSession()); 166 this.target = target; 167 } 168 169 @Override 170 public void run() { 171 List<DocumentModel> parents = session.getParentDocuments(target.getRef()); 172 for (int i = parents.size() - 1; i >= 0; i--) { 173 DocumentModel parent = parents.get(i); 174 if (parent.hasFacet(TENANT_CONFIG_FACET)) { 175 tenantId = (String) parent.getPropertyValue(Constants.TENANT_ID_PROPERTY); 176 return; 177 } 178 } 179 } 180 181 public String getTenantId() { 182 return tenantId; 183 } 184 185 } 186}