001/* 002 * (C) Copyright 2006-2007 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 * Thierry Delprat 018 * 019 */ 020 021package org.nuxeo.ecm.platform.computedgroups; 022 023import java.io.Serializable; 024import java.util.ArrayList; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028 029import org.nuxeo.ecm.core.api.DocumentModel; 030import org.nuxeo.ecm.core.api.DocumentModelList; 031import org.nuxeo.ecm.core.api.NuxeoException; 032import org.nuxeo.ecm.core.api.NuxeoGroup; 033import org.nuxeo.ecm.core.api.NuxeoPrincipal; 034import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl; 035import org.nuxeo.ecm.core.query.sql.model.QueryBuilder; 036import org.nuxeo.ecm.directory.BaseSession; 037import org.nuxeo.ecm.directory.Session; 038import org.nuxeo.ecm.platform.usermanager.NuxeoPrincipalImpl; 039import org.nuxeo.ecm.platform.usermanager.UserManager; 040import org.nuxeo.ecm.platform.usermanager.UserManagerImpl; 041import org.nuxeo.runtime.api.Framework; 042 043/** 044 * {@link UserManager} implementation that is aware of {@link ComputedGroup}. 045 * 046 * @author Thierry Delprat 047 */ 048public class UserManagerWithComputedGroups extends UserManagerImpl { 049 050 private static final long serialVersionUID = 1L; 051 052 protected static Boolean useComputedGroup; 053 054 public static final String VIRTUAL_GROUP_MARKER = "__virtualGroup"; 055 056 protected ComputedGroupsService getService() { 057 return Framework.getService(ComputedGroupsService.class); 058 } 059 060 protected boolean activateComputedGroup() { 061 // NXP-8133: recompute during tests, need to find a cleaner fix 062 if (useComputedGroup == null || Framework.isTestModeSet()) { 063 useComputedGroup = getService().activateComputedGroups(); 064 } 065 return useComputedGroup; 066 } 067 068 @Override 069 protected NuxeoPrincipal makePrincipal(DocumentModel userEntry, boolean anonymous, List<String> groups) { 070 071 NuxeoPrincipal principal = super.makePrincipal(userEntry, anonymous, groups); 072 if (activateComputedGroup() && principal instanceof NuxeoPrincipalImpl) { 073 NuxeoPrincipalImpl nuxPrincipal = (NuxeoPrincipalImpl) principal; 074 075 List<String> vGroups = getService().computeGroupsForUser(nuxPrincipal); 076 077 if (vGroups == null) { 078 vGroups = new ArrayList<>(); 079 } 080 081 List<String> origVGroups = nuxPrincipal.getVirtualGroups(); 082 if (origVGroups == null) { 083 origVGroups = new ArrayList<>(); 084 } 085 086 // MERGE! 087 origVGroups.addAll(vGroups); 088 089 nuxPrincipal.setVirtualGroups(origVGroups); 090 091 // This a hack to work around the problem of running tests 092 if (!Framework.isTestModeSet()) { 093 nuxPrincipal.updateAllGroups(); 094 } else { 095 List<String> allGroups = nuxPrincipal.getGroups(); 096 for (String vGroup : vGroups) { 097 if (!allGroups.contains(vGroup)) { 098 allGroups.add(vGroup); 099 } 100 } 101 nuxPrincipal.setGroups(allGroups); 102 } 103 } 104 return principal; 105 } 106 107 @Override 108 public NuxeoGroup getGroup(String groupName) { 109 NuxeoGroup grp = super.getGroup(groupName); 110 if (activateComputedGroup() && (grp == null || getService().allowGroupOverride())) { 111 NuxeoGroup computed = getService().getComputedGroup(groupName, groupConfig); 112 if (computed != null) { 113 grp = computed; 114 } 115 } 116 return grp; 117 } 118 119 @Override 120 public NuxeoGroup getGroup(String groupName, DocumentModel context) { 121 NuxeoGroup grp = super.getGroup(groupName, context); 122 if (activateComputedGroup() && (grp == null || getService().allowGroupOverride())) { 123 NuxeoGroup computed = getService().getComputedGroup(groupName, groupConfig); 124 if (computed != null) { 125 grp = computed; 126 } 127 } 128 return grp; 129 } 130 131 @Override 132 public List<String> getGroupIds() { 133 List<String> ids = super.getGroupIds(); 134 if (activateComputedGroup()) { 135 List<String> vGroups = getService().computeGroupIds(); 136 for (String vGroup : vGroups) { 137 if (!ids.contains(vGroup)) { 138 ids.add(vGroup); 139 } 140 } 141 } 142 return ids; 143 } 144 145 @Override 146 public DocumentModel getGroupModel(String groupName) { 147 DocumentModel grpDoc = super.getGroupModel(groupName); 148 if (activateComputedGroup() && grpDoc == null) { 149 return getComputedGroupAsDocumentModel(groupName); 150 } 151 return grpDoc; 152 } 153 154 @Override 155 public DocumentModelList searchGroups(Map<String, Serializable> filter, Set<String> fulltext) { 156 return searchGroups(filter, fulltext, null); 157 } 158 159 @Override 160 public DocumentModelList searchGroups(Map<String, Serializable> filter, Set<String> fulltext, 161 DocumentModel context) { 162 163 boolean searchInVirtualGroups = activateComputedGroup(); 164 if (Boolean.FALSE.equals(filter.get(VIRTUAL_GROUP_MARKER))) { 165 searchInVirtualGroups = false; 166 } 167 168 removeVirtualFilters(filter); 169 DocumentModelList groups = super.searchGroups(filter, fulltext, context); 170 171 if (searchInVirtualGroups) { 172 for (String vGroupName : getService().searchComputedGroups(filter, fulltext)) { 173 DocumentModel vGroup = getComputedGroupAsDocumentModel(vGroupName); 174 if (vGroup != null) { 175 if (!groups.contains(vGroup)) { 176 groups.add(vGroup); 177 } 178 } 179 } 180 } 181 return groups; 182 } 183 184 @Override 185 public DocumentModelList searchGroups(QueryBuilder queryBuilder) { 186 return searchGroups(queryBuilder, null); 187 } 188 189 @Override 190 public DocumentModelList searchGroups(QueryBuilder queryBuilder, DocumentModel context) { 191 // TODO find a way to pass a flag to allow callers do disable searching in virtual groups 192 DocumentModelList virtualEntries = new DocumentModelListImpl(); 193 for (String groupName : getService().searchComputedGroups(queryBuilder)) { 194 DocumentModel entry = getComputedGroupAsDocumentModel(groupName); 195 if (entry != null && !virtualEntries.contains(entry)) { 196 virtualEntries.add(entry); 197 } 198 } 199 if (virtualEntries.isEmpty()) { 200 // no computed groups match 201 return super.searchGroups(queryBuilder, context); 202 } else { 203 // we have some computed groups to add, we'll need to do limit/offset/order by post-filtering 204 queryBuilder = multiTenantManagement.groupQueryTransformer(this, queryBuilder, context); 205 try (Session session = dirService.open(groupDirectoryName, context)) { 206 return queryWithVirtualEntries(session, queryBuilder, virtualEntries); 207 } 208 } 209 } 210 211 protected DocumentModel getComputedGroupAsDocumentModel(String grpName) { 212 NuxeoGroup grp = getService().getComputedGroup(grpName, groupConfig); 213 if (grp == null) { 214 return null; 215 } 216 217 String schemaName = getGroupSchemaName(); 218 String id = getGroupIdField(); 219 DocumentModel groupDoc = BaseSession.createEntryModel(null, schemaName, grpName, null); 220 221 groupDoc.setProperty(schemaName, getGroupMembersField(), grp.getMemberUsers()); 222 groupDoc.setProperty(schemaName, id, grp.getName()); 223 groupDoc.setProperty(schemaName, getGroupIdField(), grp.getName()); 224 225 groupDoc.putContextData("virtual", Boolean.TRUE); 226 227 return groupDoc; 228 } 229 230 @Override 231 public DocumentModel createGroup(DocumentModel groupModel, DocumentModel context) { 232 if (activateComputedGroup()) { 233 String groupName = (String) groupModel.getProperty(groupConfig.schemaName, groupConfig.idField); 234 NuxeoGroup computed = getService().getComputedGroup(groupName, groupConfig); 235 if (computed != null) { 236 throw new NuxeoException("Cannot create a computed group"); 237 } 238 } 239 return super.createGroup(groupModel, context); 240 } 241 242 @Override 243 public void updateGroup(DocumentModel groupModel, DocumentModel context) { 244 if (activateComputedGroup()) { 245 String groupName = (String) groupModel.getProperty(groupConfig.schemaName, groupConfig.idField); 246 NuxeoGroup computed = getService().getComputedGroup(groupName, groupConfig); 247 if (computed != null) { 248 throw new NuxeoException("Cannot update a computed group"); 249 } 250 } 251 super.updateGroup(groupModel, context); 252 } 253 254 @Override 255 public void deleteGroup(String groupId, DocumentModel context) { 256 if (activateComputedGroup()) { 257 NuxeoGroup computed = getService().getComputedGroup(groupId, groupConfig); 258 if (computed != null) { 259 throw new NuxeoException("Cannot delete a computed group"); 260 } 261 } 262 super.deleteGroup(groupId, context); 263 } 264}