All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.security.fences.policy.Policy Maven / Gradle / Ivy

There is a newer version: 1.9-beta
Show newest version
package com.google.security.fences.policy;

import java.util.Map;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.security.fences.config.Fence;
import com.google.security.fences.config.FenceVisitor;
import com.google.security.fences.config.Frenemies;
import com.google.security.fences.namespace.Namespace;
import com.google.security.fences.namespace.NamespaceTrie;

/**
 * Makes access decisions based on a configuration.
 */
public final class Policy {
  /**
   * Maps packages and classes that might access an API element to
   * API elements and the access level they have.
   */
  private final NamespaceTrie trie
      = new NamespaceTrie(
          NamespacePolicy.EMPTY_SUPPLIER,
          FOLD_POLICIES_TOGETHER);

  private static final
  Function>
    FOLD_POLICIES_TOGETHER
  = new Function>() {
    public Function apply(
        final NamespacePolicy policies) {
      return new Function() {
        public NamespacePolicy apply(AccessControlDecision onePolicy) {
          policies.restrictAccess(onePolicy);
          return policies;
        }
      };
    }
  };

  /** The access policies for ns from most-specific to least. */
  public ImmutableList forNamespace(Namespace ns) {
    ImmutableList.Builder b = ImmutableList.builder();
    NamespaceTrie.Entry d = trie.getDeepest(ns);
    for (Optional> e = Optional.of(d);
         e.isPresent();
         e = e.get().getParent()) {
      Optional accessLevels = e.get().getValue();
      if (accessLevels.isPresent()) {
        b.add(accessLevels.get());
      }
    }
    return b.build();
  }

  /**
   * An access control decision for a single API element.
   * This is a cell in the Namespace x ApiElement access control matrix.
   */
  public static final class AccessControlDecision {
    /**
     * The API element to which access is controlled.
     */
    public final ApiElement apiElement;
    /**
     * The access level granted to {@link #apiElement}.
     */
    public final AccessLevel accessLevel;
    /**
     * The reason if any for controlling access.
     */
    public final Optional rationale;

    AccessControlDecision(
        ApiElement apiElement, AccessLevel accessLevel,
        Optional rationale) {
      this.apiElement = apiElement;
      this.accessLevel = accessLevel;
      this.rationale = rationale;
    }

    @Override
    public String toString() {
      return "{" + apiElement + " " + accessLevel + "}";
    }

    @Override
    public boolean equals(Object o) {
      if (!(o instanceof AccessControlDecision)) {
        return false;
      }
      AccessControlDecision that = (AccessControlDecision) o;
      return this.accessLevel == that.accessLevel
          && this.apiElement.equals(that.apiElement)
          && this.rationale.equals(that.rationale);
    }

    @Override
    public int hashCode() {
      return Objects.hashCode(accessLevel, apiElement, rationale);
    }

    static AccessControlDecision mostRestrictive(
        AccessControlDecision a, AccessControlDecision b) {
      AccessLevel mostRestrictiveLevel = AccessLevel.mostRestrictive(
          a.accessLevel, b.accessLevel);
      if (a.accessLevel == mostRestrictiveLevel) {
        if (b.accessLevel == mostRestrictiveLevel) {
          Preconditions.checkState(a.apiElement.equals(b.apiElement));
          if (a.rationale.isPresent()) {
            if (b.rationale.isPresent()) {
              String aRationale = a.rationale.get();
              String bRationale = b.rationale.get();
              if (aRationale.contains(bRationale)) {
                return a;
              } else if (bRationale.contains(aRationale)) {
                return b;
              } else {
                return new AccessControlDecision(
                    a.apiElement,
                    mostRestrictiveLevel,
                    Optional.of(aRationale + "\n\n" + bRationale)
                    );
              }
            } else {
              return a;
            }
          } else {
            return b;
          }
        } else {
          return a;
        }
      } else {
        Preconditions.checkState(b.accessLevel == mostRestrictiveLevel);
        return b;
      }
    }
  }

  /**
   * {@link com.google.security.fences.policy.Policy.AccessControlDecision}s
   * relevant to a particular namespace.
   * This is a row in the Namespace x ApiElement access control matrix.
   */
  public static final class NamespacePolicy {
    /** Supplies new instances for Trie nodes. */
    public static final Supplier EMPTY_SUPPLIER =
        new Supplier() {
      public NamespacePolicy get() {
        return new NamespacePolicy();
      }
    };

    private final Map apiElementToPolicy =
        Maps.newLinkedHashMap();

    /**
     * The access level for the given element if any.
     * This is based on looking for the most specific rule that applies to that
     * element or any containing api element.
     */
    public Optional accessPolicyForApiElement(
        ApiElement element) {
      for (Optional e = Optional.of(element);
           e.isPresent();
           e = e.get().parent) {
        ApiElement el = e.get();
        AccessControlDecision p = apiElementToPolicy.get(el);
        if (p != null) {
          return Optional.of(p);
        }
      }
      return Optional.absent();
    }

    AccessControlDecision getAccessPolicy(ApiElement el) {
      return apiElementToPolicy.get(el);
    }

    void restrictAccess(AccessControlDecision p) {
      AccessControlDecision newPolicy = Preconditions.checkNotNull(p);
      ApiElement el = newPolicy.apiElement;
      AccessControlDecision oldPolicy = apiElementToPolicy.get(el);
      if (oldPolicy != null) {
        newPolicy = AccessControlDecision.mostRestrictive(oldPolicy, newPolicy);
      }
      apiElementToPolicy.put(el, newPolicy);
    }

    @VisibleForTesting
    static NamespacePolicy fromMap(Map m) {
      NamespacePolicy al = new NamespacePolicy();
      al.apiElementToPolicy.putAll(m);
      return al;
    }

    @VisibleForTesting
    static NamespacePolicy fromAccessLevelMap(Map m) {
      ImmutableMap.Builder b =
          ImmutableMap.builder();
      for (Map.Entry e : m.entrySet()) {
        ApiElement k = e.getKey();
        AccessLevel v = e.getValue();
        b.put(k, new AccessControlDecision(k, v, Optional.absent()));
      }
      return fromMap(b.build());
    }


    @Override
    public String toString() {
      return apiElementToPolicy.toString();
    }

    @Override
    public boolean equals(Object o) {
      if (!(o instanceof NamespacePolicy)) {
        return false;
      }
      NamespacePolicy that = (NamespacePolicy) o;
      return this.apiElementToPolicy.equals(that.apiElementToPolicy);
    }

    @Override
    public int hashCode() {
      return this.apiElementToPolicy.hashCode();
    }
  }

  /**
   * Produces a policy from beans typically populated from a POM
   * {@code } element.
   */
  public static Policy fromFences(Iterable fences) {
    final Policy policy = new Policy();
    FenceVisitor buildFencesVisitor = new FenceVisitor() {
      public void visit(Fence f, ApiElement apiElement) {
        Frenemies frenemies = f.getFrenemies();
        addToPolicy(
            frenemies.friends, AccessLevel.ALLOWED, apiElement,
            Optional.absent());
        addToPolicy(
            frenemies.enemies, AccessLevel.DISALLOWED, apiElement,
            frenemies.rationale);
      }

      @SuppressWarnings("synthetic-access")
      private void addToPolicy(
          Iterable nss, AccessLevel lvl, ApiElement el,
          Optional rationale) {
        for (Namespace ns : nss) {
          policy.trie.put(ns, new AccessControlDecision(el, lvl, rationale));
        }
      }
    };
    for (Fence f : fences) {
      f.visit(buildFencesVisitor);
    }
    return policy;
  }

  @Override
  public String toString() {
    return trie.toTree();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy