/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Nuxeo - initial API and implementation * * $Id$ */ package org.eclipse.ecr.core.security; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.Map.Entry; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.ecr.core.api.ClientException; import org.eclipse.ecr.core.api.security.UserVisiblePermission; /** * @author Bogdan Stefanescu * @author Olivier Grisel */ public class DefaultPermissionProvider implements PermissionProviderLocal { @SuppressWarnings("unused") private static final Log log = LogFactory .getLog(DefaultPermissionProvider.class); private final List<PermissionDescriptor> registeredPermissions = new LinkedList<PermissionDescriptor>(); // to be recomputed each time a new PermissionDescriptor is registered - // null means invalidated private Map<String, MergedPermissionDescriptor> mergedPermissions; private Map<String, Set<String>> mergedGroups; private final List<PermissionVisibilityDescriptor> registeredPermissionsVisibility = new LinkedList<PermissionVisibilityDescriptor>(); private Map<String, PermissionVisibilityDescriptor> mergedPermissionsVisibility; public DefaultPermissionProvider() { mergedPermissionsVisibility = null; } @Override public List<UserVisiblePermission> getUserVisiblePermissionDescriptors( String typeName) throws ClientException { if (mergedPermissionsVisibility == null) { try { computeMergedPermissionsVisibility(); } catch (Exception e) { throw new ClientException(e); } } // grab the default items (type is "") PermissionVisibilityDescriptor defaultVisibility = mergedPermissionsVisibility .get(typeName); if (defaultVisibility == null) { // fallback to default defaultVisibility = mergedPermissionsVisibility.get(""); } if (defaultVisibility == null) { throw new ClientException( "no permission visibility configuration registered"); } return defaultVisibility.getSortedUIPermissionDescriptor(); } @Override public List<UserVisiblePermission> getUserVisiblePermissionDescriptors() throws ClientException { return getUserVisiblePermissionDescriptors(""); } private void computeMergedPermissionsVisibility() throws Exception { mergedPermissionsVisibility = new HashMap<String, PermissionVisibilityDescriptor>(); for (PermissionVisibilityDescriptor pvd : registeredPermissionsVisibility) { PermissionVisibilityDescriptor mergedPvd = mergedPermissionsVisibility .get(pvd.getTypeName()); if (mergedPvd == null) { mergedPvd = new PermissionVisibilityDescriptor(pvd); if (!StringUtils.isEmpty(pvd.getTypeName())) { mergedPvd.merge(mergedPermissionsVisibility.get("")); } mergedPermissionsVisibility.put(mergedPvd.getTypeName(), mergedPvd); } else { mergedPvd.merge(pvd); } } } @Override public String[] getSubPermissions(String perm) throws ClientException { List<String> permissions = getPermission(perm).getSubPermissions(); return permissions.toArray(new String[permissions.size()]); } @Override public String[] getAliasPermissions(String perm) throws ClientException { List<String> permissions = getPermission(perm).getSubPermissions(); return permissions.toArray(new String[permissions.size()]); } private MergedPermissionDescriptor getPermission(String perm) throws ClientException { if (mergedPermissions == null) { computeMergedPermissions(); } MergedPermissionDescriptor mpd = mergedPermissions.get(perm); if (mpd == null) { throw new ClientException(perm + " is not a registered permission"); } return mpd; } // OG: this is an awkward method prototype left unchanged for BBB @Override public String[] getPermissionGroups(String perm) { if (mergedGroups == null) { computeMergedGroups(); } Set<String> groups = mergedGroups.get(perm); if (groups != null && !groups.isEmpty()) { // OG: why return null instead of an empty array return groups.toArray(new String[groups.size()]); } return null; } protected synchronized void computeMergedGroups() { if (mergedPermissions == null) { computeMergedPermissions(); } mergedGroups = new HashMap<String, Set<String>>(); // scanning sub permissions to collect direct group membership for (MergedPermissionDescriptor mpd : mergedPermissions.values()) { for (String subPermission : mpd.getSubPermissions()) { Set<String> groups = mergedGroups.get(subPermission); if (groups == null) { groups = new TreeSet<String>(); groups.add(mpd.getName()); mergedGroups.put(subPermission, groups); } else { if (!groups.contains(mpd.getName())) { groups.add(mpd.getName()); } } } } // building the transitive closure on groups membership with a recursive // method Set<String> alreadyProcessed = new HashSet<String>(); for (Entry<String, Set<String>> groupEntry : mergedGroups.entrySet()) { String permissionName = groupEntry.getKey(); Set<String> groups = groupEntry.getValue(); Set<String> allGroups = computeAllGroups(permissionName, alreadyProcessed); groups.addAll(allGroups); } } protected Set<String> computeAllGroups(String permissionName, Set<String> alreadyProcessed) { Set<String> allGroups = mergedGroups.get(permissionName); if (allGroups == null) { allGroups = new TreeSet<String>(); } if (alreadyProcessed.contains(permissionName)) { return allGroups; } else { // marking it processed early to avoid infinite loops in case of // recursive inclusion alreadyProcessed.add(permissionName); for (String directGroupName : new TreeSet<String>(allGroups)) { allGroups.addAll(computeAllGroups(directGroupName, alreadyProcessed)); } return allGroups; } } // OG: this is an awkward method prototype left unchanged for BBB @Override public String[] getPermissions() { if (mergedPermissions == null) { computeMergedPermissions(); } // TODO OG: should we add aliased permissions here as well? return mergedPermissions.keySet().toArray( new String[mergedPermissions.size()]); } protected synchronized void computeMergedPermissions() { mergedPermissions = new HashMap<String, MergedPermissionDescriptor>(); for (PermissionDescriptor pd : registeredPermissions) { MergedPermissionDescriptor mpd = mergedPermissions .get(pd.getName()); if (mpd == null) { mpd = new MergedPermissionDescriptor(pd); mergedPermissions.put(mpd.getName(), mpd); } else { mpd.mergeDescriptor(pd); } } } @Override public synchronized void registerDescriptor( PermissionDescriptor descriptor) throws Exception { // check that all included permission have previously been registered Set<String> alreadyRegistered = new HashSet<String>(); for (PermissionDescriptor registeredPerm : registeredPermissions) { alreadyRegistered.add(registeredPerm.getName()); } for (String includePerm : descriptor.getIncludePermissions()) { if (!alreadyRegistered.contains(includePerm)) { // TODO: OG: use a specific exception sub class instead of the // base type throw new Exception( String.format( "Permission '%s' included by '%s' is not a registered permission", includePerm, descriptor.getName())); } } // invalidate merged permission mergedPermissions = null; mergedGroups = null; // append the new descriptor registeredPermissions.add(descriptor); } @Override public synchronized void unregisterDescriptor(PermissionDescriptor descriptor) { int lastOccurence = registeredPermissions.lastIndexOf(descriptor); if (lastOccurence != -1) { // invalidate merged permission mergedPermissions = null; mergedGroups = null; // remove the last occurrence of the descriptor registeredPermissions.remove(lastOccurence); } } @Override public synchronized void registerDescriptor(PermissionVisibilityDescriptor descriptor) { // invalidate cached merged descriptors mergedPermissionsVisibility = null; registeredPermissionsVisibility.add(descriptor); } @Override public synchronized void unregisterDescriptor(PermissionVisibilityDescriptor descriptor) { int lastOccurence = registeredPermissionsVisibility .lastIndexOf(descriptor); if (lastOccurence != -1) { // invalidate merged descriptors mergedPermissionsVisibility = null; // remove the last occurrence of the descriptor registeredPermissionsVisibility.remove(lastOccurence); } } }