# Policies

Policies, written as [Open Policy Agent](https://www.openpolicyagent.org/) (OPA) Policies in [Rego](https://www.openpolicyagent.org/docs/latest/#rego), define *if* someone should get access.

There are two types of Policies:

1. **Access Policies** define *if* someone should get access.
2. **Workflow Policies** define if a [Workflow Step](https://docs.abbey.io/reference/grant-workflows#steps) should be skipped.

## Polices Spec

### Access Policies

```hcl
policies = [
    # Access Policies
    {
        bundle = "github://organization/repo/path/to/policies[.tar.gz]"
        # or,
        # query = <<-EOT
        #     package main
        #
        #     ...
        # EOT
    },
    ...   
]
```

### Workflow Policies

```hcl
workflow = {
    steps = [
        {
            ...
            # Workflow Policies
            skip_if = [
                {
                    # bundle = "..."
                    # query = "..."
                }
            ]
        }
    ]
}
```

## Configuring Policies For Your Grant Kit

Access Policies and Workflow Policies are configured in identical ways.

* For Access Policies, put your policies in the `policies` attribute.
* For Workflow Policies, use the `skip_if` attribute.

These attributes represent a list of OPA Policies as either a [`bundle`](#bundles) or a [`query`](#queries).

<details>

<summary>Example Policy Configurations</summary>

{% code lineNumbers="true" %}

```hcl
// Grant Kit with an Access Policy containing a single bundle.
policies = [
    { bundle = "..." }
]

// Grant Kit with an Access Policy containing two bundles.
policies = [
    { bundle = "..." },
    { bundle = "..." },
]

// Grant Kit with an Access Policy containing two bundles and a single policy.
policies = [
    { bundle = "..." },
    { bundle = "..." },
    { query = "..." },
]

// Grant Kit with a single step configured with a Workflow Policy that
// contains a single bundle.
steps = [
    {
        skip_if = [
            { bundle = "..." }
        ]
    }
]

// Grant Kit with two steps, one of which is configured with
// a Workflow Policy that contains a single bundle and the
// other with a Workflow Policy that contain a bundle and a query.
steps = [
    {
        skip_if = [
            { bundle = "..." }
        ]
    },
    {
        skip_if = [
            { bundle = "..." },
            { query = "..." }
        ]
    }
]
```

{% endcode %}

</details>

### Bundles

A Bundle is a native [OPA Bundle](https://www.openpolicyagent.org/docs/latest/management-bundles/#bundle-build) that represents a group of OPA Policies. Bundles are typically either a directory or a gzipped tarball.

Bundles are configured using an RFC 3986 URI string for the `bundle` attribute. This string must point to the location of your bundle in your code repository.

#### Bundle Spec

```shell-session
github://{organization}/{repository}/path/to/my/bundle[.tar.gz]
```

<details>

<summary>Example Bundle Configurations</summary>

```
// Organization: abbeylabs
// Repository: starter-kit-quickstart
// Path to policy bundle: /policies/soc2.tar.gz
//
// This example assumes the soc2.tar.gz was built ahead of time
// using `opa build` and is committed directly to the
// `starter-kit-quickstart` repository.
github://abbeylabs/starter-kit-quickstart/policies/soc2.tar.gz

// Organization: jeffchao (personal, not a GitHub org)
// Repository: my-starter-kit
// Path to policy bundle: /policies/access-policies/hipaa
//
// This example assumes `opa build` was not used and instead prefers
// Abbey to build the bundle instead. Abbey will inspect the `hipaa`
// directory and recursively add all OPA Policies defined with
// the `.rego` extension to the bundle.
github://jeffchao/my-starter-kit/policies/access-policies/hipaa
```

</details>

{% hint style="info" %}
Currently Abbey supports the `github://` scheme. Future schemes such as `file://`, `s3://`, and `https://` coming soon.
{% endhint %}

#### Organizing Bundles

Bundles are great for distributing and reusing policies.

Before building bundles, you should first organize your policies into a directory structure that represents the functionality of the bundle, for example:

```shell-session
❯ tree -a
.
└── policies
    ├── soc2
    │   ├── .manifest
    │   └── soc2-type2
    │       └── example.rego
    └── privacy
        ├── .manifest
        └── privacy-gdpr
            └── example.rego

5 directories, 4 files
```

The above setup has a `policies` directory that contains 2 bundles, each with 1 package in them. The bundles contain a `.manifest` file to define the packages it knows about and is used to avoid package conflicts.

<details>

<summary>Example <code>.manifest</code> file</summary>

```json
{
    "roots": [
        "soc2-type2",
        "privacy-gdpr"
    ]
}
```

</details>

{% hint style="warning" %}
`.manifest` files are **required** if you're using OPA bundles. They help avoid package conflicts. As we add more packages, we add their paths to the `.manifest` file.
{% endhint %}

#### Building Bundles

Once you have your directory set up, you can build your bundles using the [OPA CLI](https://www.openpolicyagent.org/docs/latest/cli/#opa-build), for example:

```shell-session
# Build the `soc2-type2` bundle from the `soc2` directory and
# Output the bundle to the `policies/soc2` directory.
opa build -b policies/soc2/soc2-type2 -o policies/soc2/soc2-type2.tar.gz

# Build the `privacy-gdpr` bundle from the `privacy` directory and
# Output the bundle to the `policies/privacy` directory.
opa build -b policies/privacy/privacy-gdpr -o policies/privacy/privacy-gdpr.tar.gz
```

### Queries

A Query represents a single OPA Policy. Queries are typically used for simple one-off rules that don't require the hierarchical rule organization that Bundles provide and aren't intended to be distributed.

Queries are configured by writing native Rego code directly as a string, typically a multiline [Heredoc string](https://developer.hashicorp.com/terraform/language/expressions/strings#heredoc-strings) for better visibility.

Queries must be defined using the `main` namespace for Abbey to evaluate it.

<details>

<summary>Example Query Configurations</summary>

```hcl
// Policies defined using `query` must use `package main`.

// This example shows the use of a multiline Heredoc string to define a simple
// Policy that always denies access.
query = <<-EOT
    package main
    
    deny[msg] {
        true
        msg := "always deny access because we're always returning true"
    }
EOT

// This example shows the same policy above, but using a single line string.
// The result is the same but the configuration is harder to read.
query = "package main\n\ndeny[msg] {\n\ttrue\n\tmsg := \"always deny access because we're always returning true\"\n}"
```

</details>

## Writing Policies For Your Grant Kit

Policies are implemented using the Abbey OPA Constraint Framework. This Framework is simplified subset of the [OPA Constraint Framework](https://github.com/open-policy-agent/frameworks/tree/master/constraint) that makes working with OPA and Rego easier.

Writing Policies consists of three steps:

1. [Define your logic using Rego](#define-your-logic-using-rego).
2. [Use policy inputs and enriched data in your logic](#use-policy-inputs-and-enriched-data-in-your-logic).

### Define Your Logic Using Rego

Access Policies and Workflow Policies are implemented using identical schemas.

#### Writing Access Policies

Access Policies are written using the following schema:

{% code lineNumbers="true" %}

```rego
allow[msg] {
    // Policy logic goes here.
    msg := "return an explanation for why the policy passes"
}
```

{% endcode %}

<details>

<summary>Example Access Policies</summary>

{% code lineNumbers="true" %}

```rego
// Access Policy that always grants access because the rule will always
// return `true`.
allow[msg] {
    true
    msg := "granting access for non-sensitive resource"
}

// Access Policy that always grants access, but expires after 24 hours.
// Assumes you added `import data.abbey.functions` in your policy.
allow[msg] {
    true; functions.expire_after("24h")
    msg := "always approves access but will eventually expire"
}

// Access Policy that always grants access, but expires at a specific date.
// Assumes you added `import data.abbey.functions` in your policy.
allow[msg] {
    true; functions.expire_at("2023-06-16T07:15:58+00:00"
    msg := "always approves but will expire at a specific time"
}

// Access Policy that grants access to sensitive infrastructure if the
// requester is currently on call.
// `input` comes from infrastructure changes, generally `tfplan.json` via
// your CI's `terraform plan` output.
// `data` is automatically enriched for you by Abbey.
allow[msg] {
    input.resource_changes[_].change.after.database_name == "pii_customers"
    data.system.abbey.pagerduty.isoncall == true
    msg := "for the pii_customers database, grant access only to on-calls. everyone else is denied by default"
}
```

{% endcode %}

</details>

#### Automatic Revocation

Once access is granted, Abbey will continuously monitor your policies and revoke access in realtime if they evaluate to false. This may happen if you set an expiration through `functions.expire_at` or `functions.expire_after`, a user's attribute has changed (e.g., they went off-call), or infrastructure has changed (e.g., a database was now marked as sensitive). You get this functionality out-of-the-box without having to configure anything extra.

#### Writing Workflow Policies

Workflow Policies are written using the following schema:

{% code lineNumbers="true" %}

```rego
skip[msg] {
    // Policy logic goes here.
    msg := "return an explanation for why this step was skipped"
}
```

{% endcode %}

<details>

<summary>Example Workflow Policies</summary>

{% code lineNumbers="true" %}

```rego
// Workflow Policy that always skips its step because the `skip` rule
// always evaluates to `true`.
skip[msg] {
    true
    msg := "always skip this step"
}
```

{% endcode %}

</details>

### Use Policy Inputs and Enriched Data In Your Logic

In order to write meaningful policies, you need to be able to compare properties across different systems.

For example, you might want to create a policy that denies access to a sensitive database table by default, unless they're an engineer and active on an on-call rotation. "Sensitive database table", "engineer", and "active on an on-call rotation" are properties from systems such as Terraform Plan, GitHub Teams, and PagerDuty Schedules.

Abbey provides two categories of external data for you when writing your policies:

1. [Policy Inputs](#policy-inputs)
2. [Enriched Data](#enriched-data)

#### Policy Inputs

Policy Inputs represent the [Terraform Plan](https://developer.hashicorp.com/terraform/cli/commands/plan) output that contains the preview of changes that Terraform plans to make to your infrastructure. This allows you to write policies based on what your infrastructure looks like.

*Usage*

* To use Policy Inputs, use the `input` object in your Rego code.

For example, given the following Terraform Plan output as your `input`:

```json
{
    "resource_changes": [
        {
            "change": {
                "after": {
                    "role_name": "PII_READONLY"
                }
            }
        }
    ]
}
```

You can define an Access Policy that automatically denies access to anyone attempting to get access to the `PII_READONLY` role:

```rego
deny[msg] {
    input.resource_changes.change.after.role_name == "PII_READONLY"
    msg := "auto deny anyone attempting to get access to the PII_READONLY role"
}
```

The `input` schema is native to Terraform and has many other attributes available for you to use.

<details>

<summary>Full Example of <code>input</code></summary>

{% code title="input.json" %}

```json
{
  "format_version": "1.1",
  "terraform_version": "1.4.2",
  "variables": {
    "account": {
      "value": "***"
    },
    "password": "***",
    "username": {
      "value": "***"
    }
  },
  "planned_values": {
    "root_module": {
      "resources": [
        {
          "address": "abbey_grant_kit.role__pii_readonly",
          "mode": "managed",
          "type": "abbey_grant_kit",
          "name": "role__pii_readonly",
          "provider_name": "registry.terraform.io/abbeylabs/abbey",
          "schema_version": 0,
          "values": {
            "description": "Grants access to the PII READONLY Snowflake Role Grant.\n",
            "name": "Abbey Alpha Demo: PII READONLY role grant",
            "output": {
              "append": "resource \"snowflake_role_grants\" \"pii_readonly__{{ .data.system.abbey.secondary_identities.snowflake.username }}\" {\n  role_name = \"PII_READONLY\"\n  users     = [\"{{ .data.system.abbey.secondary_identities.snowflake.username }}\"]\n}\n",
              "location": "github://organization/repo/access.tf",
              "overwrite": null
            },
            "policies": [
              {
                "bundle": null,
                "query": "package main\n\nwarn[msg] {\n  input.resource_changes[_].change.after.database_name == \"DATABASE\"\n  msg := \"be careful granting access to sensitive data\"\n}\n"
              }
            ],
            "workflow": {
              "steps": [
                {
                  "reviewers": {
                    "all_of": null,
                    "one_of": [
                      "replace-me@abbey.so"
                    ]
                  },
                  "skip_if": null
                }
              ]
            }
          },
          "sensitive_values": {
            "output": {},
            "policies": [],
            "workflow": {
              "steps": [
                {
                  "reviewers": {
                    "one_of": [
                      false
                    ]
                  }
                }
              ]
            }
          }
        },
        {
          "address": "snowflake_role_grants.pii_readonly__REPLACE_ME",
          "mode": "managed",
          "type": "snowflake_role_grants",
          "name": "pii_readonly__REPLACE_ME",
          "provider_name": "registry.terraform.io/snowflake-labs/snowflake",
          "schema_version": 0,
          "values": {
            "enable_multiple_grants": false,
            "role_name": "PII_READONLY",
            "roles": null,
            "users": [
              "REPLACE_ME"
            ]
          },
          "sensitive_values": {
            "users": [
              false
            ]
          }
        },
        {
          "address": "snowflake_table_grant.pii_readonly__can_read__pii__table",
          "mode": "managed",
          "type": "snowflake_table_grant",
          "name": "pii_readonly__can_read__pii__table",
          "provider_name": "registry.terraform.io/snowflake-labs/snowflake",
          "schema_version": 0,
          "values": {
            "database_name": "DATABASE",
            "enable_multiple_grants": false,
            "on_future": false,
            "privilege": "SELECT",
            "roles": [
              "PII_READONLY"
            ],
            "schema_name": "SCHEMA",
            "shares": null,
            "table_name": "TABLE",
            "with_grant_option": false
          },
          "sensitive_values": {
            "roles": [
              false
            ]
          }
        }
      ]
    }
  },
  "resource_changes": [
    {
      "address": "abbey_grant_kit.role__pii_readonly",
      "mode": "managed",
      "type": "abbey_grant_kit",
      "name": "role__pii_readonly",
      "provider_name": "registry.terraform.io/abbeylabs/abbey",
      "change": {
        "actions": [
          "create"
        ],
        "before": null,
        "after": {
          "description": "Grants access to the PII READONLY Snowflake Role Grant.\n",
          "name": "Name",
          "output": {
            "append": "resource \"snowflake_role_grants\" \"pii_readonly__{{ .data.system.abbey.secondary_identities.snowflake.username }}\" {\n  role_name = \"PII_READONLY\"\n  users     = [\"{{ .data.system.abbey.secondary_identities.snowflake.username }}\"]\n}\n",
            "location": "github://organization/repo/access.tf",
            "overwrite": null
          },
          "policies": [
            {
              "bundle": null,
              "query": "package main\n\nwarn[msg] {\n  input.resource_changes[_].change.after.database_name == \"DATABASE\"\n  msg := \"be careful granting access to sensitive data\"\n}\n"
            }
          ],
          "workflow": {
            "steps": [
              {
                "reviewers": {
                  "all_of": null,
                  "one_of": [
                    "replace-me@example.com"
                  ]
                },
                "skip_if": null
              }
            ]
          }
        },
        "after_unknown": {
          "id": true,
          "output": {},
          "policies": [],
          "workflow": {
            "steps": [
              {
                "reviewers": {
                  "one_of": [
                    false
                  ]
                }
              }
            ]
          }
        },
        "before_sensitive": false,
        "after_sensitive": {
          "output": {},
          "policies": ,
          "workflow": {
            "steps": [
              {
                "reviewers": {
                  "one_of": [
                    false
                  ]
                }
              }
            ]
          }
        }
      }
    },
    {
      "address": "snowflake_role_grants.pii_readonly__REPLACE_ME",
      "mode": "managed",
      "type": "snowflake_role_grants",
      "name": "pii_readonly__REPLACE_ME",
      "provider_name": "registry.terraform.io/snowflake-labs/snowflake",
      "change": {
        "actions": [
          "create"
        ],
        "before": null,
        "after": {
          "enable_multiple_grants": false,
          "role_name": "PII_READONLY",
          "roles": null,
          "users": [
            "***"
          ]
        },
        "after_unknown": {
          "id": true,
          "users": [
            false
          ]
        },
        "before_sensitive": false,
        "after_sensitive": {
          "users": [
            false
          ]
        }
      }
    },
    {
      "address": "snowflake_table_grant.pii_readonly__can_read__pii__table",
      "mode": "managed",
      "type": "snowflake_table_grant",
      "name": "pii_readonly__can_read__pii__table",
      "provider_name": "registry.terraform.io/snowflake-labs/snowflake",
      "change": {
        "actions": [
          "create"
        ],
        "before": null,
        "after": {
          "database_name": "DATABASE",
          "enable_multiple_grants": false,
          "on_future": false,
          "privilege": "SELECT",
          "roles": [
            "PII_READONLY"
          ],
          "schema_name": "SCHEMA",
          "shares": null,
          "table_name": "TABLE",
          "with_grant_option": false
        },
        "after_unknown": {
          "id": true,
          "roles": [
            false
          ]
        },
        "before_sensitive": false,
        "after_sensitive": {
          "roles": [
            false
          ]
        }
      }
    }
  ],
  "prior_state": {
    "format_version": "1.0",
    "terraform_version": "1.4.2",
    "values": {
      "root_module": {
        "resources": [
          {
            "address": "data.snowflake_database.pii_database",
            "mode": "data",
            "type": "snowflake_database",
            "name": "pii_database",
            "provider_name": "registry.terraform.io/snowflake-labs/snowflake",
            "schema_version": 0,
            "values": {
              "comment": "",
              "created_on": "DATE",
              "id": "DATABASE",
              "is_current": false,
              "is_default": false,
              "name": "DATABASE",
              "options": "",
              "origin": "",
              "owner": "ACCOUNTADMIN",
              "retention_time": 1
            },
            "sensitive_values": {}
          },
          {
            "address": "data.snowflake_role.pii_readonly_role",
            "mode": "data",
            "type": "snowflake_role",
            "name": "pii_readonly_role",
            "provider_name": "registry.terraform.io/snowflake-labs/snowflake",
            "schema_version": 0,
            "values": {
              "comment": "This role allows identities to read tables which contain PII",
              "id": "PII_READONLY",
              "name": "PII_READONLY"
            },
            "sensitive_values": {}
          },
          {
            "address": "data.snowflake_users.my_snowflake_user",
            "mode": "data",
            "type": "snowflake_users",
            "name": "my_snowflake_user",
            "provider_name": "registry.terraform.io/snowflake-labs/snowflake",
            "schema_version": 0,
            "values": {
              "id": "VMB31206.AWS_US_WEST_2",
              "pattern": "REPLACE_ME",
              "users": [
                {
                  "comment": "",
                  "default_namespace": "",
                  "default_role": "ROLE",
                  "default_secondary_roles": [
                    ""
                  ],
                  "default_warehouse": "",
                  "disabled": false,
                  "display_name": "REPLACE_ME",
                  "email": "replace-me-snowflake@abbey.so",
                  "first_name": "Firstname",
                  "has_rsa_public_key": false,
                  "last_name": "Lastname",
                  "login_name": "REPLACE_ME",
                  "name": "REPLACE_ME"
                }
              ]
            },
            "sensitive_values": {
              "users": [
                {
                  "default_secondary_roles": [
                    false
                  ]
                }
              ]
            }
          }
        ]
      }
    }
  },
  "configuration": {
    "provider_config": {
      "abbey": {
        "name": "abbey",
        "full_name": "registry.terraform.io/abbeylabs/abbey",
        "version_constraint": "0.1.2"
      },
      "snowflake": {
        "name": "snowflake",
        "full_name": "registry.terraform.io/snowflake-labs/snowflake",
        "version_constraint": "0.56.5",
        "expressions": {
          "account": {
            "references": [
              "var.account"
            ]
          },
          "password": {
            "references": [
              "var.password"
            ]
          },
          "username": {
            "references": [
              "var.username"
            ]
          }
        }
      }
    },
    "root_module": {
      "resources": [
        {
          "address": "abbey_grant_kit.role__pii_readonly",
          "mode": "managed",
          "type": "abbey_grant_kit",
          "name": "role__pii_readonly",
          "provider_config_key": "abbey",
          "expressions": {
            "description": {
              "constant_value": "Grants access to the PII READONLY Snowflake Role Grant.\n"
            },
            "name": {
              "constant_value": "Name"
            },
            "output": {
              "references": [
                "data.snowflake_role.pii_readonly_role.name",
                "data.snowflake_role.pii_readonly_role"
              ]
            },
            "policies": {
              "references": [
                "data.snowflake_database.pii_database.name",
                "data.snowflake_database.pii_database"
              ]
            },
            "workflow": {
              "constant_value": {
                "steps": [
                  {
                    "reviewers": {
                      "one_of": [
                        "replace-me@abbey.so"
                      ]
                    }
                  }
                ]
              }
            }
          },
          "schema_version": 0
        },
        {
          "address": "snowflake_role_grants.pii_readonly__REPLACE_ME",
          "mode": "managed",
          "type": "snowflake_role_grants",
          "name": "pii_readonly__REPLACE_ME",
          "provider_config_key": "snowflake",
          "expressions": {
            "role_name": {
              "constant_value": "PII_READONLY"
            },
            "users": {
              "constant_value": [
                "REPLACE_ME"
              ]
            }
          },
          "schema_version": 0
        },
        {
          "address": "snowflake_table_grant.pii_readonly__can_read__pii__table",
          "mode": "managed",
          "type": "snowflake_table_grant",
          "name": "pii_readonly__can_read__pii__table",
          "provider_config_key": "snowflake",
          "expressions": {
            "database_name": {
              "references": [
                "data.snowflake_database.pii_database.name",
                "data.snowflake_database.pii_database"
              ]
            },
            "privilege": {
              "constant_value": "SELECT"
            },
            "roles": {
              "references": [
                "data.snowflake_role.pii_readonly_role.name",
                "data.snowflake_role.pii_readonly_role"
              ]
            },
            "schema_name": {
              "constant_value": "SCHEMA"
            },
            "table_name": {
              "constant_value": "TABLE"
            },
            "with_grant_option": {
              "constant_value": false
            }
          },
          "schema_version": 0
        },
        {
          "address": "data.snowflake_database.pii_database",
          "mode": "data",
          "type": "snowflake_database",
          "name": "pii_database",
          "provider_config_key": "snowflake",
          "expressions": {
            "name": {
              "constant_value": "DATABASE"
            }
          },
          "schema_version": 0
        },
        {
          "address": "data.snowflake_role.pii_readonly_role",
          "mode": "data",
          "type": "snowflake_role",
          "name": "pii_readonly_role",
          "provider_config_key": "snowflake",
          "expressions": {
            "name": {
              "constant_value": "PII_READONLY"
            }
          },
          "schema_version": 0
        },
        {
          "address": "data.snowflake_users.my_snowflake_user",
          "mode": "data",
          "type": "snowflake_users",
          "name": "my_snowflake_user",
          "provider_config_key": "snowflake",
          "expressions": {
            "pattern": {
              "constant_value": "REPLACE_ME"
            }
          },
          "schema_version": 0
        }
      ],
      "variables": {
        "account": {
          "description": "Snowflake account",
          "sensitive": true
        },
        "password": {
          "description": "Snowflake password",
          "sensitive": true
        },
        "username": {
          "description": "Snowflake username",
          "sensitive": true
        }
      }
    }
  },
  "relevant_attributes": [
    {
      "resource": "data.snowflake_role.pii_readonly_role",
      "attribute": [
        "name"
      ]
    },
    {
      "resource": "data.snowflake_database.pii_database",
      "attribute": [
        "name"
      ]
    }
  ]
}
```

{% endcode %}

</details>

{% hint style="success" %}
`input` is accessible for both Access Policies and Workflow Policies.
{% endhint %}

#### Enriched Data

Enriched Data represents properties from external applications that generally contain information about who someone is. This allows you to write policies based on roles, relationships, or attributes of a person. Abbey automatically enriches data for you, based on identities you or your admin have imported into Abbey. For more information about importing application data, go to[link-application-identity-data](https://docs.abbey.io/build-a-grant-kit/link-application-identity-data "mention").

*Usage*

* To use Enriched Data, use the `data` object in your code with the `user` namespace.

For example, given the following `data`:

```json
{
    "user": {
        "abbey": {
            "email": "alice@example.com"
        },
        "pagerduty": {
            "isoncall": true
        },
        "github": {
            "username": "Alice"
        }
    }
}
```

You can define an Access Policy that automatically denies access to someone if they're not on-call:

```rego
deny[msg] {
    data.user.pagerduty.isoncall == false
}
```

The `data` schema is a component of the Abbey OPA Constraint Framework. As you connect more systems to the Abbey Platform, Abbey will automatically pick them up and enrich the data for you to use in your policies.

<details>

<summary>Schema of <code>data</code></summary>

{% code title="data.json" %}

```json
{
    "user": {
        "abbey": {
            "abbey":{
                "email":...
            }
            "snowflake": {
                ...
            },
            "pagerduty": {
                ...
            },
            "aws_iam": {
                ...
            },
            "gcp": {
                ...
            },
            "github": {
                ...
            },
            "okta": {
                ...
            },
            "other-identity": {
                ...
            }
        },
        "system": {
            "abbey": {
                "target": {
                    "grant": {
                        "granted_at": "RFC 3339",
                        "revoked_at": "RFC 3339"
                    }
                }            
            }
        },
    }
}
```

{% endcode %}

</details>

{% hint style="success" %}
`data` is accessible in all Policy types: Access, Revocation, and Workflow.
{% endhint %}

## Policy Evaluation

Abbey evaluates your policies using Abbey's distributed Policy Evaluation Engine. This engine will evaluate your Access, Revocation, and Workflow Policies.

<figure><img src="https://1502779850-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FudoTqG501gLo2MLzBG55%2Fuploads%2FKU5KehYHGqI0fioK68bq%2FPolicy_Evaluation_Isoflow_Diagram_3_26_2023.png?alt=media&#x26;token=ab499711-63a0-4423-9e85-61b61aec05a9" alt="Policy Evaluation"><figcaption><p>Policy Evaluation.</p></figcaption></figure>

Policy Evaluation has four stages:

1. Get Policy Input.
   * This is typically the output of your `tfplan.json` from your `terraform plan` command.
2. Fetch and enrich Data.
   * Abbey will automatically fetch and enrich data for any system you have connected.
3. Evaluate your OPA Policies.
   * Pass in your Policy Input and Enriched Data as parameters.
4. Output evaluation results and warning details.
   * Output a single boolean value along with a key-value object of evaluation and warning details.

## Guardrail Policies

Abbey supports reusable policies that can be distributed using [Bundles](#bundles). You can leverage this feature to provide Guardrail Policies. These are policies that can be imported into other policies and cannot be overridden.

There are two primary use cases:

1. You're a member of a security or IT team that wants to instill set guardrails around access for the rest of your company. For example, set some sort of non-overridable policy around PII data.
2. You don't want to write your own policies. Instead, you prefer to leverage existing, battle-tested policies. For example, you can pass in an OPA `bundle` in your Grant Kit that already has all the necessary Rego rules for compliance for a specific target system.

To create a Guardrail Policy, create a policy or bundle as you would normally and import it into any other policy you want to use in your Grant Kits.

## Troubleshooting

A common deployment failure for grant kits is a misconfigured `bundle` field in the `policies` block

* Double check it starts with `github://`
* Double check repository and username or org name is correct
  * These fields are case-sensitive so double check any upper/lowercase letters
* Double check for any extra `:` or `/`'s
* Double check the path to your policies exists
