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}