Access Policies

Introduction

Access Policies control the circumstances in which data can be retrieved or edited. Practically, access policies are functions that receive contextual data and return true or false according to whether access is allowed or denied. Default context is generated from the server. The server context can be combined with additional context, which can be sent from the client.

Access Policies are used in three places in UserClouds:

  • Every accessor (read API) is associated with an access policy that controls access for each target user record, and filters the records in the response accordingly
  • Every mutator (write API) is associated with an access policy that governs whether the write is allowed
  • Every token is associated with an access policy that governs the circumstances in which the token can be exchanged for the original data ("resolved")

Access policies allow you to govern data access centrally, even giving you the ability to modify contracts on data use after tokenized data is shared.

Access policies give you central, fine-grained control and visibility over sensitive data access. Policies can evaluate purpose, identity, authorization, location, expiration timelines and much more.

Access policies give you central, fine-grained control and visibility over sensitive data access. Policies can evaluate purpose, identity, authorization, location, expiration timelines and much more.

Access policies can be as simple as "always allow resolution" to complex Javascript evaluations including locations, credentials and purpose.

The simplest access policy returns true (i.e. Access Allowed) in all circumstances. This should only be used in transition, when best practices in security are enforced outside of UserClouds on the given code path.

The simplest access policy returns true (i.e. Access Allowed) in all circumstances. This should only be used in combination with security best practices off-platform.

Examples

Let’s look at four possible access policies applied to a phone number token, to see how they work.

Example PolicyUse case
Only allow resolution for integrity and operational purposesMarketing department cannot resolve phone number for SMS-based marketing
Only allow resolution by users with role = employeeContractor/third-party engineers cannot download phone number
Only allow resolution after 30 days for fraud purposesPhone number is effectively deleted from all systems after 30 days, but retained for fraud investigation
Only allow resolution on trusted IP addressesPhone number cannot be downloaded outside of company VPN

Access Policy Structure

An access policy consists of:

  • Name: The policy's name.
  • Description: A brief overview of what the policy does.
  • ID: A unique identifier used when creating accessors and tokens.
  • Function: A function that takes a token, parameters, and context to determine access.
  • Parameters: Static JSON objects available at runtime for parameterizing and reusing functions.

Built-in Functions

We provide a number of built-in functions available as global variables in your access policy templates (and transformers!). They are:

  • networkRequest
  • checkAttribute
  • getCountryNameForPhoneNumber

networkRequest

The built-in networkRequest function allows you to reach external services via HTTPS requests. This example shows how to make a network request to verify the user's country based on their IP address. Note that the interface for networkRequest is synchronous, as in the example below:

Example:

function policy(context, params) {  
  let countryCode = null;  
  const resp = networkRequest({url: `https://api.iplocation.net/?ip=${context.server.ip_address}`, method: 'GET' });  
  if (resp) {  
    countryCode = JSON.parse(resp).country_code2;  
  }  
  return countryCode === "US";  
}

checkAttribute

The checkAttribute function runs a permission check against the UserClouds authorization graph. If you are using UserClouds for authorization as a service, this can verify if a user has the necessary permissions. In short, it asks whether a given object (usually a user) has an attribute (e.g. "can-read" or "is-admin") on another object (which could be just about any entity in your system). You can read more about this in the [Authorization Documentation](https://docs.userclouds.com/edit/3-run-permissions-checks).

Example:

Use Case: Does the calling user have view permission on the target user?

function policy(context) {
    const callingUserId = context.user.id;
    const targetUserId = context.params.targetUserId;
    const attribute = "viewPermission";
    if (!callingUserId || !targetUserId || !attribute) {
        return false;
    }
    return checkAttribute(callingUserId, targetUserId, attribute);
}

getCountryNameForPhoneNumber

getCountryNameForPhoneNumber takes a phone number with country code as a string and returns a JavaScript object with the following attributes, all with string values:

  • alpha_2: the ISO 3166 two-letter country code for the number
  • alpha_3: the ISO 3166 three-letter country code
  • country_name: the ISO 3166 country name, as defined here ; and
  • country_code: the numeric telephone country code

Example:

function policy(context, params) {  
  let { phone_numer } = params; // e.g. "+12065551234"
  const country = getCountryNameForPhoneNumber(phone_number);  
   
  return country.alpha_2 === "US";  
}

Parametrizable Access Policy Templates

Access Policy Templates are parametrizable functions that can be parametrized to create multiple access policies with parallel logic. For example, you might create a template "User is over X years old". You may use this template to create several access policy instances, allowing you to create conditional logic on a user's age group.

Example 1: Check Attribute

function policy(context, params) {
    const id1 = params.userIDUsage === "id1" ? context.user.id : params.id1;
    const id2 = params.userIDUsage === "id2" ? context.user.id : params.id2;
    const attribute = params.attribute;
    if (!id1 || !id2 || !attribute) {
        return false;
    }
    return checkAttribute(id1, id2, attribute);
}

Example 2: User is over N Years Old

function getAge(DOB) {  
    const today = new Date();  
    const birthDate = new Date(DOB);  
    let age = today.getFullYear() - birthDate.getFullYear();  
    const m = today.getMonth() - birthDate.getMonth();  
    if (m \< 0 || (m === 0 && today.getDate() \< birthDate.getDate())) {  
        age--;  
    }  
    return age;  
}

function policy(context, params) {  
  return getAge(context.user[params.column_name]) >= params.expected_years_old;  
}

Parametrized Version

  • Policy Template Name: UserIsNYearsOld
  • Parameters: { expected_years_old: 16, column_name: "birthdate" }

Managing access policies

UserClouds has several built-in access policies for common use cases, like role-based and time-based expiration of data. However you can also create custom policies, in two ways:

  • Call the CreateAccessPolicy API
  • Compose a new policy from existing policies and parametrizable templates in the UserClouds Console

To learn more about creating access policies, see our How to Guide on Creating Access Policies.


What’s Next