Your first OPA policy

What's a Policy?

In Open Policy Agent (OPA), policies are defined as rules or statements that create access to a resource or allow or deny users of specific actions. OPA policies cover multiple aspects of access control, including attribute-based access control (ABAC).

Creating your first Policy

Before jumping into creating your first policy, it's good to familiarize yourself with several key concepts:

  1. A subject identifies the initiator of a request or something that can be authenticated by the system— for example, a user or an api token.
  2. An Asset is a resource in the ecosystem. It can be an API, a document or even a Supehero's dwelling, e.g, a Batcave.
  3. A Rule is a relationship defined by
    1. Subject IDs and/or subject attributes
    2. Asset IDs and/or asset attributes
    3. A scope, for example:
      1. A user (subject), who is a programmer (attribute), can create (scope) an account (asset) in Frontegg.
      2. A subject with the ID 123, can write data for a database
      3. Superman cannot rent the Batcave
  4. A Policy is a set of rules.

Use Case

Let's take an imaginative scenario where two superheroes, Superman and Batman, must find a new home to rent. While both are entitled to search for a new dwelling, each will be excluded from one asset that the other will be eligible to rent. In this case, Superman won't be able to rent the Batcave, and Batman will be barred from renting the Fortress of Solitude. To do that, we'll need to define Superman and Batman and their respective properties, as well as their access to the available assets.

Creating Subjects

To create a subject, you would need to call the Subject creation API, like so:

// seeder.ts
const batman = await this.api.createSubject(tenantId, batmanData);
const superman = await this.api.createSubject(tenantId, supermanData);

Note that it is possible to add any attribute you need for your use case.

🚧

Note

  1. Payload limits may apply on API.
  2. For better OPA performance, it is better to keep the data-set lean.

Create Assets

Let's insert all of the dwellings available for rent by calling the assets' API:

// seeder.ts
const topLevel = await this.api.createAsset(tenantId, {attributes: {name: "Super heroes fortress list"}});
const batcave = await this.api.createAsset(tenantId, {attributes: {name: "Bat cave"}}, topLevel.id);
const fortressOfSolitude = await this.api.createAsset(tenantId, {attributes: {name: "Fortress of Solitude"}}, topLevel.id);
const generalHQ = await this.api.createAsset(tenantId, {attributes: {name: "HQ for HIRE"}}, batcave.id);

📘

Asset hierarchy

Note that the batcave, fortressOfSolitude and generalHq are directly set under topLevel. Assets can have hierarchies.

Example Policy with Rules

Now that we uploaded our heroes as subjects and defined our HQs as assets. Now it's time to link them together by setting aPolicy and a subset of Rules that will define it.

First, create a new policy:

// seeder.ts
const policy = await this.api.createPolicy(
  tenantId,
  {
    name: "Superheroes policy",
    description: "Who can access super headquarters",
    allowByDefault: false
  }
);

📘

allowByDefault

Note that if theallowByDefault parameter is set to true then it will return truefor every query if no rule criteria is matched. For most use cases, your best practice would be to set allowByDefault tofalse.

Our Rule would be that Batman is the only one who can rent the Batcave, and Superman is the only one who can rent the Fortress of Solitude. Our subjects are excluded from specific assets, yet both can search for new places to rent. The rules will be defined in the following way:

// seeder.ts
// Hierarchy rule
const hierarchyRule = await this.api.createRule(
  tenantId,
  policy.id,
  {
    name: "Available for rent",
    description: "All heroes can rent new houses",
    assets: {hierarchical: true, assetIds: [topLevel.id]},
    subjects: {
      attributes: [
        {attribute: "job", op: "equal", value: "hero"}
      ]
    },
    scopes: ["Rent"]
  }
);

// Only Superman can Rent Fortress of solitude
const supermanRent = await this.api.createRule(
  tenantId,
  policy.id,
  {
    name: "superman-rent",
    description: "Only Superman can access the fortress of solitude",
    allow: false,
    assets: {assetIds: [fortressOfSolitude.id]},
    subjects: {
      attributes: [
        {attribute: "subjectId", op: "not_equal", value: 'Superman'}
      ]
    },
    scopes: ["Rent"]
  }
);

// Only Batman can Rent the Batcave
const batmanRent = await this.api.createRule(
  tenantId,
  policy.id,
  {
    name: "batman-rent",
    description: "Only Batman can access the Bat cave",
    allow: false,
    assets: {assetIds: [batcave.id]},
    subjects: {
      attributes: [
        {attribute: "subjectId", op: "not_equal", value: 'Batman'}
      ]
    },
    scopes: ["Rent"]
  }
);

📘

Important Notes

  1. You can use a rule to deny access to a place.
  2. The subject ID provided in the subject API can also be used as an attribute.
  3. A resource access can be hierarchical. Meaning that all resources nested underneath it are subject to the rule as well.

Activate policy

If you look in OPA's logs, you should see that nothing is downloading yet. Currently, we are allowing to download only the main policy. You can create as many policies as you want, but only one can be fetched.

// seeder.ts
await this.api.setMainPolicy(tenantId, policy.id, true);

That's it!
You're all set to check your super policy!