Skip to content

Latest commit

 

History

History
178 lines (157 loc) · 6.31 KB

access-enforcement.md

File metadata and controls

178 lines (157 loc) · 6.31 KB
/**
 * Evaluate all the acp:Policy statements in each acp:AccessControl
 * in the ACR for the Resource.
 * Accumulate all of the acp:AccessModes for the acp:allow and
 * acp:deny statements for each acp:Policy that is satisfied.
 * Subtracting the accumulated deny modes from the accumulated allow
 * modes provides the access modes permitted.
 *
 * The access mode required is determined by the request method used.
 * The table in section 'HTTP Method To Access Mode' provides the
 * mapping from request method to access mode.
 *
 * agent    : The Agent attempting to access the Resource
 * resource : The Resource being accessed
 * method   : the HTTP method used by the Agent
 *
 * HTTPMethodToAccessMode is the table the in section 'HTTP Method To Access Mode'
 *
 * Return: true if access is allowed, otherwise false
 **/

export function isAccessAllowed(agent: Agent, resource: Resource, method: HTTPMethod): boolean {
  const acr: ACR = resource.getAccessControlResource();
  const accessModes: AccessModes = new AccessModes();

  acr.getAccessControls().forEach((ac: AccessControl) => {
    accessModes.add(evaluatePolicies(ac.apply, agent, resource));
    accessModes.add(evaluatePolicies(ac.applyProtected, agent, resource));
    accessModes.add(evaluatePolicies(ac.applyLocked, agent, resource));
  });

  const requiredMode: string = HTTPMethodToAccessMode.get(method);
  if (accessModes.contains(requiredMode)) return true;

  return false;
}

/**
 * Evaluate all the acp:Policy statements using acp:access
 * in the ACR for the Resource.
 * Accumulate all of the acp:AccessModes for the acp:allow and
 * acp:deny statements for each acp:Policy that is satisfied.
 * Subtracting the accumulated deny modes from the accumulated allow
 * modes provides the access modes permitted.
 *
 * The access mode required is determined by the request method used.
 * The table in section 'HTTP Method To ACR Access Mode' provides the
 * mapping from request method to access mode.
 *
 * agent    : The Agent attempting to access the ACR
 * resource : The Resource associated with the ACR being accessed
 * method   : the HTTP method used by the Agent
 *
 * Return: true if access is allowed, otherwise false
 */
export function isAcrAccessAllowed(agent: Agent, resource: Resource, method: HTTPMethod) {
  return isAcrAccessPoliciesAllowed(agent, resource, method, resource.getAccessControlResource().access);
}

export function isAcrAccessMembersAllowed(agent: Agent, resource: Resource, method: HTTPMethod) {
  return isAcrAccessPoliciesAllowed(agent, resource, method, resource.getAccessControlResource().accessMembers);
}

export function isAcrAccessProtectedAllowed(agent: Agent, resource: Resource, method: HTTPMethod) {
  return isAcrAccessPoliciesAllowed(agent, resource, method, resource.getAccessControlResource().accessProtected);
}

export function isAcrAccessMembersProtectedAllowed(agent: Agent, resource: Resource, method: HTTPMethod) {
  return isAcrAccessPoliciesAllowed(
    agent,
    resource,
    method,
    resource.getAccessControlResource().accessMembersProtected
  );
}

export function isAcrAccessLockedAllowed(agent: Agent, resource: Resource, method: HTTPMethod) {
  return isAcrAccessPoliciesAllowed(agent, resource, method, resource.getAccessControlResource().accessLocked);
}

export function isAcrAccessMembersLockedAllowed(agent: Agent, resource: Resource, method: HTTPMethod) {
  return isAcrAccessPoliciesAllowed(agent, resource, method, resource.getAccessControlResource().accessMembersLocked);
}

// Helper functions

/**
 * agent    : The Agent attempting to access the ACR
 * resource : The Resource being accessed
 * method   : The HTTP method used by the Agent
 * policies : The list of Policies to evaluate to determine whether access should be provided
 *
 * HTTPMethodToAcrAccessMode is the table the in section 'HTTP Method To ACR Access Mode'
 *
 * Return: true if access is allowed, otherwise false
 */
function isAcrAccessPoliciesAllowed(agent: Agent, resource: Resource, method: HTTPMethod, policies: Policy[]) {
  const acr: ACR = resource.getAccessControlResource();
  const requiredMode = HTTPMethodToAcrAccessMode.get(method);

  if (evaluatePolicies(policies, agent, acr).contains(requiredMode)) return true;

  return false;
}

/**
 * policies : The list of Policies to be evaluated
 * agent    : The Agent attempting to access the Resource
 * resource : The Resource the Agent is attempting to access
 *
 * Return   : An Access object specifying the access modes permitted on
 *            the Resource by this Agent.
 *            The permitted modes are Read, Write, Append.
 *            If the Resource is an ACR then the Append mode is not used.
 */
function evaluatePolicies(policies: Policy[], agent: Agent, resource: Resource) {
  const allowModes = new AccessModes();
  const denyModes = new AccessModes();

  policies.forEach((p: Policy) => {
    if (isPolicySatisfied(p, agent, resource)) {
      allowModes.add(p.allow);
      denyModes.add(p.deny);
    }
  });

  return allowModes.delete(denyModes);
}

/**
 * policy   : The Policy to evaluate to determine whether the specified agent satisfies the policy
 * agent    : The Agent attempting to access the resource
 * resource : The Resource being accessed
 *
 * Return   : true if the Policy is satisfied, otherwise false
 **/

function isPolicySatisfied(policy: Policy, agent: Agent, resource: Resource) {
  if (
    policy.anyOf.some((r: Rule) => {
      isRuleSatisfied(r, agent, resource);
    }) &&
    policy.allOf.every((r: Rule) => {
      isRuleSatisfied(r, agent, resource);
    }) &&
    !policy.noneOf.some((r: Rule) => {
      isRuleSatisfied(r, agent, resource);
    })
  ) {
    return true;
  }
  return false;
}

/**
 * rule     : The Rule to evaluate to determine whether the specified agent satisfies the rule
 * agent    : The Agent attempting to access the resource
 * resource : The Resource being accessed
 *
 * Return   : true if the Rule is satisfied, otherwise false
 **/

function isRuleSatisfied(rule: Rule, agent: Agent, resource: Resource) {
  if (
    rule.includes(ACP.PUBLIC_AGENT) ||
    (rule.includes(ACP.AUTHENTICATED_AGENT) && agent.isAuthenticated()) ||
    rule.includes(agent) ||
    (rule.includes(ACP.CREATOR_AGENT) && resource.creator().sameAs(agent)) ||
    rule.groups().some((g: Group) => {
      g.hasMember(agent);
    })
  ) {
    return true;
  }
  return false;
}