Skip to content

Latest commit

 

History

History
267 lines (230 loc) · 15.3 KB

conditions.mdx

File metadata and controls

267 lines (230 loc) · 15.3 KB
sidebar_position slug description
8
/modeling/conditions
Modeling relationships with Conditions

import { AuthzModelSnippetViewer, WriteRequestViewer, CheckRequestViewer, ListObjectsRequestViewer, languageLabelMap, SdkSetupPrerequisite, SupportedLanguage, WriteAuthzModelViewer, } from '@components/Docs';

import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';

Conditions

Overview

Conditions allow you to model more complex authorization modeling scenarios involving attributes and can be used to represent some Attribute-based Access Control (ABAC) policies. Take a look at the Conditions and Conditional Relationship Tuples concepts for a quick overview.

There are various use cases where Conditions can be helpful. These include, but are not limited to:

For more information and background context on why we added this feature, please see our blog post on Conditional Relationship Tuples for OpenFGA.

Defining Conditions in Models

For this example we'll use the following authorization model to demonstrate a temporal based access policy. Namely, a user can view a document if and only if they have been granted the viewer relationship AND their non-expired grant policy is met.

model
  schema 1.1

type user

type document
  relations
    define viewer: [user with non_expired_grant]

condition non_expired_grant(current_time: timestamp, grant_time: timestamp, grant_duration: duration) {
  current_time < grant_time + grant_duration
}

:::note The type restriction for document#viewer requires that any user of type user that is written in the relationship tuple must be accompanied by the non_expired_grant condition. This is denoted by the user with non_expired_grant specification. :::

Write the model to the FGA store: <WriteAuthzModelViewer authorizationModel={{ "schema_version":"1.1", "type_definitions": [ { "type":"user" }, { "type":"document", "relations": { "viewer": { "this": {} } }, "metadata": { "relations": { "viewer": { "directly_related_user_types": [ { "type":"user", "condition":"non_expired_grant" } ] } } } } ], "conditions": { "non_expired_grant": { "name":"non_expired_grant", "expression":"current_time < grant_time + grant_duration", "parameters": { "current_time": { "type_name":"TYPE_NAME_TIMESTAMP" }, "grant_duration": { "type_name":"TYPE_NAME_DURATION" }, "grant_time": { "type_name":"TYPE_NAME_TIMESTAMP" } } } } }} skipSetup={true} allowedLanguages={[ SupportedLanguage.JS_SDK, SupportedLanguage.GO_SDK, SupportedLanguage.DOTNET_SDK, SupportedLanguage.PYTHON_SDK, SupportedLanguage.JAVA_SDK, SupportedLanguage.CLI, SupportedLanguage.CURL, ]} />

Writing Conditional Relationship Tuples

Using the model above, when we Write relationship tuples to the OpenFGA store, then any document#viewer relationship with user objects must be accompanied by the condition non_expired_grant because the type restriction requires it.

For example, we can give user:anne viewer access to document:1 for 10 minutes by writing the following relationship tuple:

<WriteRequestViewer relationshipTuples={[ { user: 'user:anne', relation: 'viewer', object: 'document:1', condition: { name: 'non_expired_grant', context: { grant_time: '2023-01-01T00:00:00Z', grant_duration: '10m', } } }, ]} skipSetup={true} allowedLanguages={[ SupportedLanguage.JS_SDK, SupportedLanguage.GO_SDK, SupportedLanguage.DOTNET_SDK, SupportedLanguage.PYTHON_SDK, SupportedLanguage.JAVA_SDK, SupportedLanguage.CLI, SupportedLanguage.CURL, ]} />

Queries with Condition Context

Now that we have written a Conditional Relationship Tuple, we can query OpenFGA using the Check API to see if user:anne has viewer access to document:1 under certain conditions/context. That is, user:anne should only have access if the current timestamp is less than the grant timestamp (e.g. the time which the tuple was written) plus the duration of the grant (10 minutes). If the current timestamp is less than, then you'll get a permissive decision. For example,

<CheckRequestViewer user={'user:anne'} relation={'viewer'} object={'document:1'} context={{current_time: "2023-01-01T00:09:50Z"}} allowed={true} skipSetup={true} allowedLanguages={[ SupportedLanguage.JS_SDK, SupportedLanguage.GO_SDK, SupportedLanguage.DOTNET_SDK, SupportedLanguage.PYTHON_SDK, SupportedLanguage.JAVA_SDK, SupportedLanguage.CLI, SupportedLanguage.CURL, ]} />

but if the current time is outside the grant window then you get a deny decision. For example,

<CheckRequestViewer user={'user:anne'} relation={'viewer'} object={'document:1'} context={{current_time: "2023-01-01T00:10:01Z"}} allowed={false} skipSetup={true} allowedLanguages={[ SupportedLanguage.JS_SDK, SupportedLanguage.GO_SDK, SupportedLanguage.DOTNET_SDK, SupportedLanguage.PYTHON_SDK, SupportedLanguage.JAVA_SDK, SupportedLanguage.CLI, SupportedLanguage.CURL, ]} />

Similarly, we can use the ListObjects API to return all of the documents that user:anne has viewer access to given the current time. For example,

<ListObjectsRequestViewer objectType="document" relation="viewer" user="user:anne" context={{current_time: "2023-01-01T00:09:50Z"}} expectedResults={['document:1']} skipSetup={true} allowedLanguages={[ SupportedLanguage.JS_SDK, SupportedLanguage.GO_SDK, SupportedLanguage.DOTNET_SDK, SupportedLanguage.PYTHON_SDK, SupportedLanguage.JAVA_SDK, SupportedLanguage.CLI, SupportedLanguage.CURL, ]} />

but if the current time is outside the grant window then we don't get the object in the response. For example,

<ListObjectsRequestViewer objectType="document" relation="viewer" user="user:anne" context={{current_time: "2023-01-01T00:10:01Z"}} expectedResults={[]} skipSetup={true} allowedLanguages={[ SupportedLanguage.JS_SDK, SupportedLanguage.GO_SDK, SupportedLanguage.DOTNET_SDK, SupportedLanguage.PYTHON_SDK, SupportedLanguage.JAVA_SDK, SupportedLanguage.CLI, SupportedLanguage.CURL, ]} />

:::note When evaluating a condition at request time, the context written/persisted in the relationship tuple and the context provided at request time are merged together into a single evaluation context.

If you provide a context value in the request context that is also written/persisted in the relationship tuple, then the context values written in the relationship tuple take precedence. That is, the merge strategy is such that persisted context has higher precedence than request context. :::

Examples

For more examples take a look at our Sample Stores repository. There are various examples with ABAC models in that repository.

Supported Parameter Types

The following table enumerates the list of supported parameter types. The more formal list is defined in https://github.com/openfga/openfga/tree/main/internal/condition/types.

Note that some of the types support generics, these types are indicated with <T>.

Friendly Type Name Type Name (Protobuf Enum) Description Examples
int TYPE_NAME_INT A 64-bit signed integer value. -1
"-1"
uint TYPE_NAME_UINT A 64-bit unsigned integer value. 1
"1"
double TYPE_NAME_DOUBLE A double-width floating point value, represented equivalently as a Go float64 value.

If the value is provided as a string we parse it with strconv.ParseFloat(s, 64). See strconv.ParseFloat for more info.
3.14159
-0.75
"1"
"-2.5"
bool TYPE_NAME_BOOL A boolean value. true
false

"true"
"false"
bytes TYPE_NAME_BYTES An array of byte values specified as a byte string. "bytestring"
string TYPE_NAME_STRING A string value. "hello, world"
duration TYPE_NAME_DURATION A value representing a duration of time specified using Go duration string format.

See time.Duration#ParseDuration
"120s"
"2m"
timestamp TYPE_NAME_TIMESTAMP A timestamp value that follows the RFC3339 specification. "2023-01-01T00:00:00Z"
any TYPE_NAME_ANY A variant type which permits any value to be provided. {"x": 1}
"hello"
["a", "b"]
list<T> TYPE_NAME_LIST A list of values of generic type T. list<string> - ["a", "b", "c"]
list<int> - [-1, 1]
list<duration> - ["60s", "1m"]
map<T> TYPE_NAME_MAP A map whose keys are strings and whose values are values of generic type T.

Any map value must have string keys, only the value types can vary.
map<int> - {"x": -1, "y": 1}
map<string> - {"key": "value"}
ipaddress TYPE_NAME_IPADDRESS A custom value type specified as a string representation of an IP Address. "192.168.0.1"

Limitations

  • The size of the condition context parameter that can be written alongside a relationship tuple is limited to 32KB in size.

  • The size of the condition context parameter for query requests (e.g. Check, ListObjects, etc..) is not explicitly limited, but the OpenFGA server has an overall request size limit of 512KB at this time.

  • We're still working on the changes to support Conditions in the official FGA CLI and various OpenFGA SDKs including: .NET and Python. At this moment you cannot Write conditional relationship tuples with these tools and/or query (e.g. Check, ListObjects, etc..) OpenFGA with condition context.

  • We enforce a maximum Google CEL expression evaluation cost of 100 (by default) to protect the server from malicious conditions. The evaluation cost of a CEL expression is a function of the size the input that is being compared and the composition of the expression. For more general information please see the official Language Definition for Google CEL. If you hit these limits with practical use-cases, please reach out to the maintainer team and we can discuss.