/* * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Thomas Roger */ package org.nuxeo.ecm.core.security; import static org.nuxeo.ecm.core.api.event.CoreEventConstants.CHANGED_ACL_NAME; import static org.nuxeo.ecm.core.api.event.CoreEventConstants.DOCUMENT_REFS; import static org.nuxeo.ecm.core.api.event.CoreEventConstants.REPOSITORY_NAME; import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.ACE_STATUS_UPDATED; import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.nuxeo.ecm.core.api.DocumentRef; import org.nuxeo.ecm.core.api.IdRef; import org.nuxeo.ecm.core.api.IterableQueryResult; import org.nuxeo.ecm.core.api.event.CoreEventConstants; import org.nuxeo.ecm.core.api.security.ACE; import org.nuxeo.ecm.core.api.security.ACP; import org.nuxeo.ecm.core.event.EventContext; import org.nuxeo.ecm.core.event.EventService; import org.nuxeo.ecm.core.event.impl.EventContextImpl; import org.nuxeo.ecm.core.query.sql.NXQL; import org.nuxeo.ecm.core.work.AbstractWork; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.transaction.TransactionRuntimeException; /** * Work updating ACE status. * * @since 7.4 */ public class UpdateACEStatusWork extends AbstractWork { public static final int DEFAULT_BATCH_SIZE = 20; public static final String ID = "updateACEStatus"; public static final String CATEGORY = "updateACEStatus"; public static final String QUERY = "SELECT ecm:uuid, ecm:acl/*1/principal, ecm:acl/*1/permission," + " ecm:acl/*1/grant, ecm:acl/*1/creator, ecm:acl/*1/begin, ecm:acl/*1/end, ecm:acl/*1/name FROM Document" + " WHERE (ecm:acl/*1/status = 0 AND ecm:acl/*1/begin <= TIMESTAMP '%s')" + " OR (ecm:acl/*1/status = 1 AND ecm:acl/*1/end <= TIMESTAMP '%s')"; public static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); protected int batchSize = DEFAULT_BATCH_SIZE; public UpdateACEStatusWork() { super(ID); } @Override public void work() { setStatus("Updating ACE status"); openSystemSession(); Date now = new Date(); String formattedDate = FORMATTER.format(now); IterableQueryResult result = session.queryAndFetch(String.format(QUERY, formattedDate, formattedDate), NXQL.NXQL); Map<String, List<ACE>> docIdsToACEs = new HashMap<>(); try { for (Map<String, Serializable> map : result) { String docId = (String) map.get("ecm:uuid"); List<ACE> aces = docIdsToACEs.get(docId); if (aces == null) { aces = new ArrayList<>(); docIdsToACEs.put(docId, aces); } String username = (String) map.get("ecm:acl/*1/principal"); String permission = (String) map.get("ecm:acl/*1/permission"); Boolean grant = (Boolean) map.get("ecm:acl/*1/grant"); String creator = (String) map.get("ecm:acl/*1/creator"); Calendar begin = (Calendar) map.get("ecm:acl/*1/begin"); Calendar end = (Calendar) map.get("ecm:acl/*1/end"); String aclName = (String) map.get("ecm:acl/*1/name"); Map<String, Serializable> contextData = new HashMap<>(); contextData.put(CHANGED_ACL_NAME, aclName); ACE ace = ACE.builder(username, permission) .isGranted(grant) .creator(creator) .begin(begin) .end(end) .contextData(contextData) .build(); aces.add(ace); } } finally { result.close(); } int acpUpdatedCount = 0; Map<DocumentRef, List<ACE>> processedRefToACEs = new HashMap<>(); for (Map.Entry<String, List<ACE>> entry : docIdsToACEs.entrySet()) { try { DocumentRef ref = new IdRef(entry.getKey()); ACP acp = session.getACP(ref); // re-set the ACP to actually write the new status session.setACP(ref, acp, true); acpUpdatedCount++; processedRefToACEs.put(ref, entry.getValue()); if (acpUpdatedCount % batchSize == 0) { fireACEStatusUpdatedEvent(processedRefToACEs); commitOrRollbackTransaction(); startTransaction(); processedRefToACEs.clear(); } } catch (TransactionRuntimeException e) { if (e.getMessage().contains("Transaction timeout")) { batchSize = 1; } throw e; } } fireACEStatusUpdatedEvent(processedRefToACEs); setStatus(null); } protected void fireACEStatusUpdatedEvent(Map<DocumentRef, List<ACE>> refToACEs) { EventContext eventContext = new EventContextImpl(session, session.getPrincipal()); eventContext.setProperty(DOCUMENT_REFS, (Serializable) refToACEs); eventContext.setProperty(REPOSITORY_NAME, session.getRepositoryName()); Framework.getService(EventService.class).fireEvent(ACE_STATUS_UPDATED, eventContext); } @Override public String getCategory() { return CATEGORY; } @Override public String getTitle() { return "Updating ACE status"; } @Override public int getRetryCount() { return 10; } }