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