001/*
002 * (C) Copyright 2006-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 *     Nuxeo - initial API and implementation
018 *
019 * $Id$
020 */
021
022package org.nuxeo.ecm.core.api.security.impl;
023
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.HashSet;
027import java.util.Iterator;
028import java.util.List;
029import java.util.ListIterator;
030import java.util.Objects;
031import java.util.Set;
032
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035import org.nuxeo.ecm.core.api.security.ACE;
036import org.nuxeo.ecm.core.api.security.ACL;
037import org.nuxeo.ecm.core.api.security.AdministratorGroupsProvider;
038import org.nuxeo.ecm.core.api.security.SecurityConstants;
039import org.nuxeo.runtime.api.Framework;
040
041import com.google.common.collect.Lists;
042
043/**
044 * An ACL implementation.
045 *
046 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
047 */
048public class ACLImpl extends ArrayList<ACE> implements ACL {
049
050    private static final long serialVersionUID = 5332101749929771434L;
051
052    private static final Log log = LogFactory.getLog(ACLImpl.class);
053
054    private final String name;
055
056    private final boolean isReadOnly;
057
058    public ACLImpl(String name, boolean isReadOnly) {
059        this.name = Objects.requireNonNull(name, "ACL name can't be null");
060        this.isReadOnly = isReadOnly;
061    }
062
063    public ACLImpl() {
064        this(LOCAL_ACL, false);
065    }
066
067    public ACLImpl(String name) {
068        this(name, false);
069    }
070
071    @Override
072    public String getName() {
073        return name;
074    }
075
076    @Override
077    public ACE[] getACEs() {
078        return toArray(new ACE[size()]);
079    }
080
081    @Override
082    public void setACEs(ACE[] aces) {
083        clear();
084        addAll(Arrays.asList(aces));
085        warnForDuplicateACEs(aces);
086    }
087
088    private void warnForDuplicateACEs(ACE[] aces) {
089        if (!log.isWarnEnabled() || ACL.INHERITED_ACL.equals(name)) {
090            return;
091        }
092        Set<ACE> aceSet = new HashSet<>(aces.length);
093        for (ACE ace : aces) {
094            if (!aceSet.add(ace)) {
095                Throwable throwable = null;
096                if (log.isTraceEnabled()) {
097                    throwable = new Throwable();
098                }
099                log.warn("Setting an ACL with at least one duplicate entry: " + ace + ", ACL entries: "
100                        + Arrays.toString(aces), throwable);
101                break;
102            }
103        }
104    }
105
106    public boolean isReadOnly() {
107        return isReadOnly;
108    }
109
110    @Override
111    public boolean blockInheritance(String username) {
112        boolean aclChanged = false;
113        List<ACE> aces = Lists.newArrayList(getACEs());
114        if (!aces.contains(ACE.BLOCK)) {
115            aces.add(ACE.builder(username, SecurityConstants.EVERYTHING).creator(username).build());
116            aces.addAll(getAdminEverythingACES());
117            aces.add(ACE.BLOCK);
118            aclChanged = true;
119            setACEs(aces.toArray(new ACE[aces.size()]));
120        }
121        return aclChanged;
122    }
123
124    @Override
125    public boolean unblockInheritance() {
126        boolean aclChanged = false;
127        List<ACE> aces = Lists.newArrayList(getACEs());
128        if (aces.contains(ACE.BLOCK)) {
129            aces.remove(ACE.BLOCK);
130            aclChanged = true;
131            setACEs(aces.toArray(new ACE[aces.size()]));
132        }
133        return aclChanged;
134    }
135
136    @Override
137    public boolean add(ACE ace) {
138        boolean aclChanged = false;
139        List<ACE> aces = Lists.newArrayList(getACEs());
140        if (!aces.contains(ace)) {
141            int pos = aces.indexOf(ACE.BLOCK);
142            if (pos >= 0) {
143                aces.add(pos, ace);
144            } else {
145                aces.add(ace);
146            }
147            aclChanged = true;
148            setACEs(aces.toArray(new ACE[aces.size()]));
149        }
150
151        return aclChanged;
152    }
153
154    protected List<ACE> getAdminEverythingACES() {
155        List<ACE> aces = new ArrayList<>();
156        AdministratorGroupsProvider provider = Framework.getService(AdministratorGroupsProvider.class);
157        List<String> administratorsGroups = provider.getAdministratorsGroups();
158        for (String adminGroup : administratorsGroups) {
159            aces.add(new ACE(adminGroup, SecurityConstants.EVERYTHING, true));
160        }
161        return aces;
162    }
163
164    @Override
165    public boolean replace(ACE oldACE, ACE newACE) {
166        boolean aclChanged = false;
167        int index = indexOf(oldACE);
168        if (index != -1) {
169            remove(oldACE);
170            add(index, newACE);
171            aclChanged = true;
172        }
173
174        return aclChanged;
175    }
176
177    @Override
178    public boolean removeByUsername(String username) {
179        boolean aclChanged = false;
180
181        List<ACE> aces = Lists.newArrayList(getACEs());
182        for (Iterator<ACE> it = aces.iterator(); it.hasNext();) {
183            ACE ace = it.next();
184            if (ace.getUsername().equals(username)) {
185                it.remove();
186                aclChanged = true;
187            }
188        }
189        setACEs(aces.toArray(new ACE[aces.size()]));
190
191        return aclChanged;
192    }
193
194    @Override
195    public void replacePermission(String oldPerm, String newPerm) {
196        ListIterator<ACE> it = listIterator();
197        while (it.hasNext()) {
198            ACE ace = it.next();
199            if (ace.getPermission().equals(oldPerm)) {
200                ace = ACE.builder(ace).permission(newPerm).build();
201                it.set(ace);
202            }
203        }
204    }
205
206    @Override
207    public Object clone() {
208        ACLImpl copy = new ACLImpl(name, isReadOnly);
209        ACE[] aces = new ACE[size()];
210        for (int i = 0; i < size(); i++) {
211            aces[i] = (ACE) get(i).clone();
212        }
213        copy.setACEs(aces);
214        return copy;
215    }
216
217}