001/*
002 * (C) Copyright 2015 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
016 */
017
018package org.nuxeo.ecm.admin.permissions;
019
020import java.io.Serializable;
021import java.util.ArrayList;
022import java.util.Calendar;
023import java.util.GregorianCalendar;
024import java.util.List;
025import java.util.Map;
026
027import org.nuxeo.ecm.core.api.DocumentModel;
028import org.nuxeo.ecm.core.api.DocumentRef;
029import org.nuxeo.ecm.core.api.IdRef;
030import org.nuxeo.ecm.core.api.IterableQueryResult;
031import org.nuxeo.ecm.core.api.security.ACE;
032import org.nuxeo.ecm.core.api.security.ACL;
033import org.nuxeo.ecm.core.api.security.ACP;
034import org.nuxeo.ecm.core.query.sql.NXQL;
035import org.nuxeo.ecm.core.work.AbstractWork;
036import org.nuxeo.ecm.platform.query.api.PageProviderDefinition;
037import org.nuxeo.ecm.platform.query.api.PageProviderService;
038import org.nuxeo.ecm.platform.query.nxql.NXQLQueryBuilder;
039import org.nuxeo.runtime.api.Framework;
040import org.nuxeo.runtime.transaction.TransactionRuntimeException;
041
042/**
043 * Work archiving ACEs based on a query.
044 *
045 * @since 7.4
046 */
047public class PermissionsPurgeWork extends AbstractWork {
048
049    private static final long serialVersionUID = 1L;
050
051    public static final int DEFAULT_BATCH_SIZE = 20;
052
053    public static final String CATEGORY = "permissionsPurge";
054
055    protected DocumentModel searchDocument;
056
057    protected int batchSize = DEFAULT_BATCH_SIZE;
058
059    public PermissionsPurgeWork(DocumentModel searchDocument) {
060        this.searchDocument = searchDocument;
061    }
062
063    @Override
064    public String getTitle() {
065        return String.format("Permissions purge for: %s, %s", searchDocument.getPropertyValue("rs:ace_username"),
066                searchDocument.getPropertyValue("rs:ecm_path"));
067    }
068
069    @Override
070    public String getCategory() {
071        return CATEGORY;
072    }
073
074    @Override
075    public void work() {
076        setStatus("Purging");
077        initSession();
078
079        PageProviderService pageProviderService = Framework.getService(PageProviderService.class);
080        PageProviderDefinition def = pageProviderService.getPageProviderDefinition("permissions_purge");
081        String query = NXQLQueryBuilder.getQuery(searchDocument, def.getWhereClause(), null);
082
083        IterableQueryResult result = session.queryAndFetch(query, NXQL.NXQL);
084        List<String> docIds = new ArrayList<>();
085        try {
086            for (Map<String, Serializable> map : result) {
087                docIds.add((String) map.get("ecm:uuid"));
088            }
089        } finally {
090            result.close();
091        }
092
093        List<String> usernames = (List<String>) searchDocument.getPropertyValue("rs:ace_username");
094        int acpUpdatedCount = 0;
095        for (String docId : docIds) {
096            DocumentRef ref = new IdRef(docId);
097            ACP acp = session.getACP(ref);
098            // cleanup acp for all principals
099            boolean changed = false;
100            for (String username : usernames) {
101                for (ACL acl : acp.getACLs()) {
102                    for (ACE ace : acl) {
103                        if (username.equals(ace.getUsername())) {
104                            Calendar now = new GregorianCalendar();
105                            ace.setEnd(now);
106                            changed = true;
107                        }
108                    }
109                }
110            }
111
112            try {
113                if (changed) {
114                    session.setACP(ref, acp, true);
115                    acpUpdatedCount++;
116                    if (acpUpdatedCount % batchSize == 0) {
117                        commitOrRollbackTransaction();
118                        startTransaction();
119                    }
120                }
121            } catch (TransactionRuntimeException e) {
122                if (e.getMessage().contains("Transaction timeout")) {
123                    batchSize = 1;
124                }
125                throw e;
126            }
127
128        }
129        setStatus(null);
130    }
131
132    @Override
133    public int getRetryCount() {
134        return 10;
135    }
136}