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