Stitch provides the ability to manage access to data using authorization policies. These policies are attached to schema fields that require authorized access.
Policy is Stitch a resource like schema. It can be created, altered or removed by using the Registry API.
The policy object has the following properties:
property | description | mandatory |
---|---|---|
metadata | Contains two fields name and namespace. Namespace is a logical group of policies. Namespace and name pair is the policy's unique identifier |
true |
type | Policy type. Currently only OPA policies are supported |
true |
code | Policy body, with syntax based on the chosen Policy type. For OPA, Rego language is supported. |
true |
args | Argument definitions. These arguments are available in the policy code and query. They support the Stitch Parameter Injection syntax. |
false |
query | Graphql query to provide additional data to policy. The results are available in the policy code. query has two properties: gql that defines the graphql query and variables for the query. variables can be the constant values or expressions using policy args . |
false |
metadata:
namespace: billing
name: adminOnly
type: opa
code: |
default allow = false
allow {
input.args.userRoles[_] == "admin"
}
args:
userRoles:
type: [String!]
default: '{jwt?.roles}'
Explanation:
-
metadata: The policy name and namespace.
-
type: Policy type
-
code: The Rego language code block. The
input
variable has 2 optional fields.args
: derived from@policy
directive arguments evaluation.query
: the result ofquery
policy property execution. The query is executed on the Stitch schema without effect of authorization policies (a.k.a. admin privileges). See examples below.
-
args: Arguments mapping. The key is argument name, the value is the options for the argument. In this example the
userRole
argument gets the value of theroles
claim from the request JWT by default. The argument can be given a different value when setting the policy on a field or object, which will override the default. The options for an argument are:type
: The Graphql type of this argument. If the type is nullable type the argument is optional.default
: The default value (argument injection supported) for the argument. Can be overridden when setting the policy on a field/object.
metadata:
namespace: admin_ns
name: adminOnly
type: opa
code: |
default allow = false
allow {
input.query.user.roles[_] == "admin"
}
args:
userId:
type: ID
query:
gql: |
query($id: ID!) {
user(id: $id) {
roles
}
}
variables:
id: '{userId}'
-
query: Graphql query definition. It has 2 properties:
Each policy is also available for evaluation as graphql query. It can be tested while debugging.
If one policy is dependent on another the query
property can include the following fragment:
{
policy.another_policy_namespace___another_policy_name(<another_policy_args>) {
allow # As mentioned above the policy result is always object with the single boolean field "allow"
}
}
The policy arguments should be set as the query arguments.
Note that due to graphql naming limitations, the name of the policy query has dashes replaced with underscores for both the namespace and the policy name (e.g. namespace some-ns
and policy name some-policy
will be called using policy.some_ns___some_policy
)
In the example below the query has 2 parts: the first one fetches roles
field from user
and the second part evaluates userIsActive
.
metadata:
namespace: admin_ns
name: adminOnly
type: opa
code: |
default allow = false
allow {
input.query.user.roles[_] == "admin"
input.query.policy.another_ns___userIsActive.allow
}
args:
userId:
type: ID
query:
gql: |
query($id: ID!) {
user(id: $id) {
roles
},
policy {
another_ns___userIsActive(userId: $id) {
allow
}
}
}
variables:
id: '{userId}'
The policies are set using graphql directives. In order to attach policy to field the @policy
directive should be set on the field.
So if in type User
there is field phone
that should be accessible for user with admin role only the type definition should be as following:
type User {
id: ID!
name: String!
phone: String @policy(namespace: "billing", name: "adminOnly", args: { userRoles: "{jwt?.roles}" })
}
The policy is defined as in example 1 (See above). Note that since the policy definition has the same default value for the userRoles
argument, the args
parameter could have been omitted in this case.
In the example above, the roles
argument value is received by using the Argument Injection mechanism. In this example, it is received from the roles
claim in the request's JWT.
Note: See Arguments Injection for more information about configuration of policy arguments.
@policy
directive has optional postResolve
argument. If the value of the argument is true
the policy validation will be executed after the execution of field resolver. This argument should be used in case the policy logic depends on the result of field resolver execution.
For example if we don't want to allow to query photos marked as private and the policy and the schema should be like this:
type Photo {
id: ID!
owner: String!
private: Boolean!
}
type Query {
photo(id: ID!) @rest(url: ...) @policy(namespace: "ns", name: "public-only", postResolve: true, args: { private: "{ result.private }" })
}
The @rest
directive will be executed before the @policy
one. And its result will be available in the policy argument injection as result
.
metadata:
namespace: ns
name: public-only
type: opa
code: |
default allow = false
allow {
input.args.private == false
}
args:
private:
type: Boolean!
Important! The order of directives does matter! The directives are applied in order they appear in schema. Object type directives are applied before field definition directives.
The @policy
directive is required to be set the last either on object type or field definition.
Any policy (that doesn't contain query) can be set as base policy. Base policy is applied automatically to every single field in query.
If field has @policy
directive both base policy and the attached policy will be checked. Base policy will be checked first. If the attached policy is defined with the shouldOverrideBasePolicy
option the base policy won't be checked.
In development and testing environment there is option to disable policies restrictions by setting "true"
value to IGNORE_POLICIES
environment variable.