/* * Copyright (c) 2013, 2014, 2016 Eike Stepper (Berlin, Germany) 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: * Eike Stepper - initial API and implementation */ package org.eclipse.emf.cdo.security.impl; import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDUtil; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.CDORevisionProvider; import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; import org.eclipse.emf.cdo.eresource.CDOResourceNode; import org.eclipse.emf.cdo.eresource.EresourcePackage; import org.eclipse.emf.cdo.security.PatternStyle; import org.eclipse.emf.cdo.security.ResourceFilter; import org.eclipse.emf.cdo.security.SecurityPackage; import org.eclipse.emf.cdo.security.impl.PermissionImpl.CommitImpactContext; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; import org.eclipse.net4j.util.StringUtil; import org.eclipse.net4j.util.WrappedException; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.ecore.EClass; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; /** * <!-- begin-user-doc --> * An implementation of the model object '<em><b>Resource Filter</b></em>'. * @since 4.3 * <!-- end-user-doc --> * <p> * The following features are implemented: * </p> * <ul> * <li>{@link org.eclipse.emf.cdo.security.impl.ResourceFilterImpl#getPath <em>Path</em>}</li> * <li>{@link org.eclipse.emf.cdo.security.impl.ResourceFilterImpl#getPatternStyle <em>Pattern Style</em>}</li> * <li>{@link org.eclipse.emf.cdo.security.impl.ResourceFilterImpl#isFolders <em>Folders</em>}</li> * <li>{@link org.eclipse.emf.cdo.security.impl.ResourceFilterImpl#isTextResources <em>Text Resources</em>}</li> * <li>{@link org.eclipse.emf.cdo.security.impl.ResourceFilterImpl#isBinaryResources <em>Binary Resources</em>}</li> * <li>{@link org.eclipse.emf.cdo.security.impl.ResourceFilterImpl#isModelResources <em>Model Resources</em>}</li> * <li>{@link org.eclipse.emf.cdo.security.impl.ResourceFilterImpl#isModelObjects <em>Model Objects</em>}</li> * <li>{@link org.eclipse.emf.cdo.security.impl.ResourceFilterImpl#isIncludeParents <em>Include Parents</em>}</li> * <li>{@link org.eclipse.emf.cdo.security.impl.ResourceFilterImpl#isIncludeRoot <em>Include Root</em>}</li> * </ul> * * @generated */ public class ResourceFilterImpl extends PermissionFilterImpl implements ResourceFilter { private static final String USER_TOKEN = "${user}"; private static final int USER_TOKEN_NONE = -1; private static final int USER_TOKEN_UNINITIALIZED = -2; private transient int userTokenPos = USER_TOKEN_UNINITIALIZED; private transient BasicEList<Matcher> matchers; private transient int lastVersion; /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ protected ResourceFilterImpl() { super(); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ @Override protected EClass eStaticClass() { return SecurityPackage.Literals.RESOURCE_FILTER; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public String getPath() { return (String)eGet(SecurityPackage.Literals.RESOURCE_FILTER__PATH, true); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public ResourceFilter setPath(String newPath) { eSet(SecurityPackage.Literals.RESOURCE_FILTER__PATH, newPath); return this; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public PatternStyle getPatternStyle() { return (PatternStyle)eGet(SecurityPackage.Literals.RESOURCE_FILTER__PATTERN_STYLE, true); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public ResourceFilter setPatternStyle(PatternStyle newPatternStyle) { eSet(SecurityPackage.Literals.RESOURCE_FILTER__PATTERN_STYLE, newPatternStyle); return this; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public boolean isFolders() { return (Boolean)eGet(SecurityPackage.Literals.RESOURCE_FILTER__FOLDERS, true); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public ResourceFilter setFolders(boolean newFolders) { eSet(SecurityPackage.Literals.RESOURCE_FILTER__FOLDERS, newFolders); return this; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public boolean isModelResources() { return (Boolean)eGet(SecurityPackage.Literals.RESOURCE_FILTER__MODEL_RESOURCES, true); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public ResourceFilter setModelResources(boolean newModelResources) { eSet(SecurityPackage.Literals.RESOURCE_FILTER__MODEL_RESOURCES, newModelResources); return this; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public boolean isModelObjects() { return (Boolean)eGet(SecurityPackage.Literals.RESOURCE_FILTER__MODEL_OBJECTS, true); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public ResourceFilter setModelObjects(boolean newModelObjects) { eSet(SecurityPackage.Literals.RESOURCE_FILTER__MODEL_OBJECTS, newModelObjects); return this; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public boolean isIncludeParents() { return (Boolean)eGet(SecurityPackage.Literals.RESOURCE_FILTER__INCLUDE_PARENTS, true); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public ResourceFilter setIncludeParents(boolean newIncludeParents) { eSet(SecurityPackage.Literals.RESOURCE_FILTER__INCLUDE_PARENTS, newIncludeParents); return this; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public boolean isIncludeRoot() { return (Boolean)eGet(SecurityPackage.Literals.RESOURCE_FILTER__INCLUDE_ROOT, true); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public ResourceFilter setIncludeRoot(boolean newIncludeRoot) { eSet(SecurityPackage.Literals.RESOURCE_FILTER__INCLUDE_ROOT, newIncludeRoot); return this; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public boolean isTextResources() { return (Boolean)eGet(SecurityPackage.Literals.RESOURCE_FILTER__TEXT_RESOURCES, true); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public ResourceFilter setTextResources(boolean newTextResources) { eSet(SecurityPackage.Literals.RESOURCE_FILTER__TEXT_RESOURCES, newTextResources); return this; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public boolean isBinaryResources() { return (Boolean)eGet(SecurityPackage.Literals.RESOURCE_FILTER__BINARY_RESOURCES, true); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public ResourceFilter setBinaryResources(boolean newBinaryResources) { eSet(SecurityPackage.Literals.RESOURCE_FILTER__BINARY_RESOURCES, newBinaryResources); return this; } public boolean isImpacted(CommitImpactContext context) { return isResourceTreeImpacted(context); } public String format() { String label = "?"; String path = getPath(); if (path != null) { if (!path.startsWith("/")) { path = "/" + path; } label = path; } String operator = formatOperator(); return "resource" + operator + label; } protected String formatOperator() { PatternStyle patternStyle = getPatternStyle(); switch (patternStyle) { case EXACT: return " == "; case TREE: return " >= "; case ANT: case REGEX: return " ~= "; default: throw new IllegalStateException("Unhandled pattern style: " + patternStyle); } } @Override protected boolean filter(CDORevision revision, CDORevisionProvider revisionProvider, CDOBranchPoint securityContext, int level) throws Exception { if (!preChecks(revision, revisionProvider)) { return false; } String revisionPath = CDORevisionUtil.getResourceNodePath(revision, revisionProvider); BasicEList<Matcher> list = getMatchers(); Object[] matchers = list.data(); int length = list.size(); for (int i = 0; i < length; i++) { Matcher matcher = (Matcher)matchers[i]; if (matcher.matches(revisionPath)) { return true; } } return false; } private boolean preChecks(CDORevision revision, CDORevisionProvider revisionProvider) { if (revisionProvider == null) { return false; } EClass eClass = revision.getEClass(); boolean resourceNode = false; // Check folders if (eClass == EresourcePackage.Literals.CDO_RESOURCE_FOLDER) { resourceNode = true; if (!isFolders()) { return false; } } // Check model resources if (eClass == EresourcePackage.Literals.CDO_RESOURCE) { resourceNode = true; boolean rootResource = CDOIDUtil.isNull((CDOID)((InternalCDORevision)revision).getContainerID()); if (rootResource) { if (!isIncludeRoot()) { return false; } } else { if (!isModelResources()) { return false; } } } // Check text resources if (eClass == EresourcePackage.Literals.CDO_TEXT_RESOURCE) { resourceNode = true; if (!isTextResources()) { return false; } } // Check binary resources if (eClass == EresourcePackage.Literals.CDO_BINARY_RESOURCE) { resourceNode = true; if (!isBinaryResources()) { return false; } } // Check model objects boolean modelObject = !resourceNode; if (modelObject && !isModelObjects()) { return false; } return true; } private BasicEList<Matcher> getMatchers() { InternalCDORevision revision = cdoRevision(); if (revision != null) { int currentVersion = revision.getVersion(); if (currentVersion > lastVersion) { userTokenPos = USER_TOKEN_UNINITIALIZED; matchers = null; lastVersion = currentVersion; } if (matchers != null) { return matchers; } } String path = getPath(); PatternStyle patternStyle = getPatternStyle(); boolean includeParents = isIncludeParents(); if (userTokenPos == USER_TOKEN_UNINITIALIZED || revision == null) { userTokenPos = path.indexOf(USER_TOKEN); } if (userTokenPos != USER_TOKEN_NONE) { String user = getUser(); if (user == null || user.length() == 0) { throw new IllegalStateException("User required for evaluation of path " + path); } path = path.substring(0, userTokenPos) + user + path.substring(userTokenPos + USER_TOKEN.length()); } BasicEList<Matcher> list = new BasicEList<Matcher>(1); getMatchers(list, path, patternStyle, includeParents); if (userTokenPos == USER_TOKEN_NONE) { // Cache matchers if no user token is specified matchers = list; } return list; } private void getMatchers(BasicEList<Matcher> matchers, String path, PatternStyle patternStyle, boolean includeParents) { Matcher matcher = createMatcher(path, patternStyle); matchers.add(matcher); if (includeParents) { int pos = path.lastIndexOf("/"); if (pos != -1) { path = path.substring(0, pos); patternStyle = matcher.getParentPatternStyle(); getMatchers(matchers, path, patternStyle, includeParents); } } } private Matcher createMatcher(String path, PatternStyle patternStyle) { switch (patternStyle) { case EXACT: return new ExactMatcher(path); case TREE: return new TreeMatcher(path); case ANT: return new AntMatcher(path); case REGEX: return new RegexMatcher(path); default: throw new IllegalStateException("Unhandled pattern style: " + patternStyle); } } public static boolean isResourceTreeImpacted(CommitImpactContext context) { InternalCDORevisionDelta[] revisionDeltas = (InternalCDORevisionDelta[])context.getDirtyObjectDeltas(); for (int i = 0; i < revisionDeltas.length; i++) { InternalCDORevisionDelta revisionDelta = revisionDeltas[i]; // Any tree move might impact this permission CDOFeatureDelta containerDelta = revisionDelta.getFeatureDelta(CDOContainerFeatureDelta.CONTAINER_FEATURE); if (containerDelta != null) { return true; } // Any change of a resource node name might impact this permission CDOFeatureDelta nameDelta = revisionDelta.getFeatureDelta(EresourcePackage.Literals.CDO_RESOURCE_NODE__NAME); if (nameDelta != null) { return true; } } return false; } /** * Matches the path of a {@link CDOResourceNode resource node}. * * @author Eike Stepper */ public interface Matcher { public boolean matches(String revisionPath); public PatternStyle getParentPatternStyle(); } /** * Matches the path of a {@link CDOResourceNode resource node} against a {@link #path} string. * * @author Eike Stepper */ protected static abstract class PathMatcher implements Matcher { protected final String path; public PathMatcher(String path) { this.path = path == null || path.length() == 0 ? "/" : path; } @Override public String toString() { return getClass().getSimpleName() + "[" + path + "]"; } } /** * @author Eike Stepper */ protected static class ExactMatcher extends PathMatcher { public ExactMatcher(String path) { super(path); } public boolean matches(String revisionPath) { return path.equals(revisionPath); } public PatternStyle getParentPatternStyle() { return PatternStyle.EXACT; } } /** * @author Eike Stepper */ protected static class TreeMatcher extends PathMatcher { public TreeMatcher(String path) { super(path); } public boolean matches(String revisionPath) { if (revisionPath == null) { return path.length() == 0; } return revisionPath.startsWith(path); } public PatternStyle getParentPatternStyle() { return PatternStyle.EXACT; } } /** * @author Eike Stepper */ protected static class AntMatcher extends PathMatcher { public AntMatcher(String path) { super(path); } public boolean matches(String revisionPath) { return StringUtil.glob(path, revisionPath); } public PatternStyle getParentPatternStyle() { return PatternStyle.ANT; } } /** * @author Eike Stepper */ protected static class RegexMatcher implements Matcher { private final Pattern pattern; public RegexMatcher(String path) { try { pattern = Pattern.compile(path); } catch (PatternSyntaxException ex) { throw WrappedException.wrap(ex); } } public boolean matches(String revisionPath) { return pattern.matcher(revisionPath).matches(); } public PatternStyle getParentPatternStyle() { return PatternStyle.REGEX; } @Override public String toString() { return getClass().getSimpleName() + "[" + pattern.pattern() + "]"; } } } // ResourceFilterImpl