001/* 002 * (C) Copyright 2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bjalon 018 */ 019package org.nuxeo.ecm.platform.usermanager; 020 021import static org.nuxeo.ecm.directory.localconfiguration.DirectoryConfigurationConstants.DIRECTORY_CONFIGURATION_FACET; 022 023import java.io.Serializable; 024import java.util.Map; 025import java.util.Set; 026 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029import org.nuxeo.ecm.core.api.DocumentModel; 030import org.nuxeo.ecm.core.api.NuxeoException; 031import org.nuxeo.ecm.core.api.localconfiguration.LocalConfigurationService; 032import org.nuxeo.ecm.core.query.QueryParseException; 033import org.nuxeo.ecm.core.query.sql.model.Expression; 034import org.nuxeo.ecm.core.query.sql.model.IdentityQueryTransformer; 035import org.nuxeo.ecm.core.query.sql.model.Literal; 036import org.nuxeo.ecm.core.query.sql.model.MultiExpression; 037import org.nuxeo.ecm.core.query.sql.model.Operator; 038import org.nuxeo.ecm.core.query.sql.model.Predicate; 039import org.nuxeo.ecm.core.query.sql.model.Predicates; 040import org.nuxeo.ecm.core.query.sql.model.QueryBuilder; 041import org.nuxeo.ecm.core.query.sql.model.Reference; 042import org.nuxeo.ecm.core.query.sql.model.StringLiteral; 043import org.nuxeo.ecm.directory.BaseSession.FieldDetector; 044import org.nuxeo.ecm.directory.localconfiguration.DirectoryConfiguration; 045import org.nuxeo.runtime.api.Framework; 046 047/** 048 * @author bjalon 049 */ 050public class DefaultUserMultiTenantManagement implements UserMultiTenantManagement { 051 052 protected static final Log log = LogFactory.getLog(DefaultUserMultiTenantManagement.class); 053 054 protected static final String SUFFIX_SEPARATOR = "-"; 055 056 protected String getDirectorySuffix(DocumentModel documentContext) { 057 LocalConfigurationService localConfigurationService = Framework.getService(LocalConfigurationService.class); 058 DirectoryConfiguration configuration = localConfigurationService.getConfiguration(DirectoryConfiguration.class, 059 DIRECTORY_CONFIGURATION_FACET, documentContext); 060 if (configuration != null && configuration.getDirectorySuffix() != null) { 061 return SUFFIX_SEPARATOR + configuration.getDirectorySuffix(); 062 } 063 return null; 064 } 065 066 @Override 067 public void queryTransformer(UserManager um, Map<String, Serializable> filter, Set<String> fulltext, 068 DocumentModel context) { 069 String groupId = um.getGroupIdField(); 070 if (filter == null || fulltext == null) { 071 throw new NuxeoException("Filter and Fulltext must be not null"); 072 } 073 074 if (getDirectorySuffix(context) == null) { 075 log.debug("Directory Local Configuration is null, don't need to filter"); 076 return; 077 } 078 079 String groupIdSuffix = getDirectorySuffix(context); 080 081 if (!filter.containsKey(groupId)) { 082 log.debug("no filter on group id, need to filter with the directory local " + "configuration suffix : " 083 + groupId + " = %" + groupIdSuffix); 084 filter.put(groupId, "%" + groupIdSuffix); 085 fulltext.add(groupId); 086 return; 087 } 088 089 if (!(filter.get(groupId) instanceof String)) { 090 throw new UnsupportedOperationException("Filter value on " + "group id is not a string : " 091 + filter.get(groupId)); 092 } 093 094 String filterIdValue = (String) filter.get(um.getGroupIdField()); 095 filter.put(groupId, filterIdValue + groupIdSuffix); 096 } 097 098 @Override 099 public QueryBuilder groupQueryTransformer(UserManager um, QueryBuilder queryBuilder, DocumentModel context) { 100 String suffix = getDirectorySuffix(context); 101 if (suffix == null) { 102 log.debug("No tenant configuration"); 103 return queryBuilder; 104 } 105 queryBuilder = new QueryBuilder(queryBuilder); // copy 106 MultiExpression multiExpr = queryBuilder.predicate(); 107 String groupIdField = um.getGroupIdField(); 108 if (FieldDetector.hasField(multiExpr, groupIdField)) { 109 QueryTenantAdder qta = new QueryTenantAdder(groupIdField, suffix); 110 multiExpr = qta.transform(multiExpr); 111 } 112 Predicate predicate = Predicates.like(groupIdField, "%" + suffix); // filter for this tenant 113 queryBuilder.predicate(predicate).and(multiExpr); 114 return queryBuilder; 115 } 116 117 /** 118 * Changes group equality or difference matches to take into account a suffix. 119 * <p> 120 * Throws for any more complex query on groups. 121 * 122 * @since 10.3 123 */ 124 public static class QueryTenantAdder extends IdentityQueryTransformer { 125 126 protected final String groupIdField; 127 128 protected final String suffix; 129 130 protected boolean isGroupPredicate; 131 132 public QueryTenantAdder(String groupIdField, String suffix) { 133 this.groupIdField = groupIdField; 134 this.suffix = suffix; 135 } 136 137 @Override 138 public Expression transform(Expression node) { 139 if (node.lvalue instanceof Reference && ((Reference) node.lvalue).name.equals(groupIdField)) { 140 if (node.operator == Operator.EQ || node.operator == Operator.NOTEQ || node.operator == Operator.IN 141 || node.operator == Operator.NOTIN) { 142 isGroupPredicate = true; 143 node = super.transform(node); 144 isGroupPredicate = false; 145 return node; 146 } 147 } 148 return super.transform(node); 149 } 150 151 @Override 152 public Predicate transform(Predicate node) { 153 if (node.lvalue instanceof Reference && ((Reference) node.lvalue).name.equals(groupIdField)) { 154 if (node.operator == Operator.EQ || node.operator == Operator.NOTEQ || node.operator == Operator.IN 155 || node.operator == Operator.NOTIN) { 156 isGroupPredicate = true; 157 node = super.transform(node); 158 isGroupPredicate = false; 159 return node; 160 } 161 } 162 return super.transform(node); 163 } 164 165 @Override 166 public Literal transform(StringLiteral node) { 167 if (!isGroupPredicate) { 168 return node; 169 } 170 return new StringLiteral(node.value + suffix); 171 } 172 173 @Override 174 public Reference transform(Reference node) { 175 if (isGroupPredicate) { 176 return node; 177 } 178 if (node.name.equals(groupIdField)) { 179 throw new QueryParseException("Cannot evaluate expression in multi-tenant mode"); 180 } 181 return node; 182 } 183 } 184 185 @Override 186 public DocumentModel groupTransformer(UserManager um, DocumentModel group, DocumentModel context) 187 { 188 if (context == null) { 189 return group; 190 } 191 String groupIdValue = group.getPropertyValue(um.getGroupIdField()) + getDirectorySuffix(context); 192 group.setPropertyValue(um.getGroupIdField(), groupIdValue); 193 return group; 194 } 195 196 @Override 197 public String groupnameTranformer(UserManager um, String groupname, DocumentModel context) { 198 String suffix = getDirectorySuffix(context); 199 if (suffix != null) { 200 groupname += suffix; 201 } 202 return groupname; 203 } 204}