Skip to main content

Policy Syntax

Introduction

In the context of Fluree, "Policy" is a term used to describe read/write or access controls enforced on the data in your ledger. However, unlike the limitations enforced in a simple SQL database, policy logic is transacted as data into the ledger itself.

Policy is therefore a self-defence measure implemented by the state of the data itself, limiting who can read, write, and access the contents of your ledger.

Transaction Syntax for Policy

note

The following examples are pulled from the Fluree cookbook, which you can use to recreate these examples and play around with the syntax yourself.

Transaction Object

Policy transactions are JSON with the following keys:

keyrequired?typerequired values
@contextyesstring, array or objectTypically "https://ns.flur.ee", but any context is valid (provided fluree-reserved IRIs like f:allow are expanded appropriately)
ledgeryesstringName of ledger
insertyesarray or objectThis includes the id for the policy, instantiates it's type, describes the permissions associated with the targetClass or targetNode
@idyesstring, array or objectthe name of your policy
@typeyesarray or objectMust always be ["f:Policy"], this is an internal fluree class used to handle access policy evalution for transactions and queries
f:targetNode or f:targetClassyesarray or objectCan be ["f:allNodes] or the IRI of a class, node, or user. Specifies what data is referenced in a policy.
f:allowyesarray or objectArray or object contianing the f:action, and f:targetRole or f:targetNode
f:targetRoleyesarray or object{"@id": <targetRole>}
f:actionyesarray or object"f:view" or "f:modify"
f:propertynoarray or objectTypically contains object for "f:path" and "f:allow"
f:pathnoarray or objectAny valid IRI
f:equalsnoobjectTypically continas an @list key with objects mapping out the logic of data state relations that enforce policy (see more in the f:equals section below)

Use Case: Transacting Root Access Policy

Let's break down the use of these keys in constructing an example policy transaction:

  • Transact policy with "@id": "ex:rootPolicy"

  • Instantiate data will be "@type": ["f:Policy"]

  • Identify which data this policy will pertain to, in this case it's "f:targetNode" will be "f:allNodes"

  • After establishing that we are transacting a new policy that will apply to all nodes, we have to explain what the policy actually does. This logic will always fall inside of the "f:allow" object

    • here we are creating a new line of logic for our policy assocated with the @id value "ex:rootAccessAllow".
    • This new policy adds a "f:targetRole" with the @id value "ex:rootRole"
    • And finally, we make it so that any user with the "ex:rootRole" can perform view and modify actions on all data

{
"@context": "https://ns.flur.ee",
"ledger": "cookbook/base",
"insert": {
"@id": "ex:rootPolicy",
"@type": ["f:Policy"],
"f:targetNode": { "@id": "f:allNodes" },
"f:allow": [
{
"@id": "ex:rootAccessAllow",
"f:targetRole": { "@id": "ex:rootRole" },
"f:action": [{ "@id": "f:view" }, { "@id": "f:modify" }]
}
]
}
}

Once the policy is written, you can transact the users, like ex:andrew, to have assigned roles. Users will be identified with a fluree did, which users can generate from our npm library (NOTE: you will append did:fluree to the value generated).


{
"@context": "https://ns.flur.ee",
"ledger": "cookbook/base",
"insert": {
"@id": "did:fluree:TfCzWTrXqF16hvKGjcYiLxRoYJ1B8a6UMH6",
"ex:user": { "@id": "ex:andrew" },
"f:role": { "@id": "ex:rootRole" }
}
}

From this point forward, you will only be able to query or transact data, using your user did, to your ledger with the role ex:rootRole.


{
"from": "cookbook/base",
"where": {
"@id": "?s",
"schema:name": "?name"
},
"select": {
"?s": ["*"]
},
"opts": {
"role": "ex:rootRole",
"did": "did:fluree:TfCzWTrXqF16hvKGjcYiLxRoYJ1B8a6UMH6"
}
}

danger

Transactions that violate policies will fail with the appropriate error. However, for security reasons, the responses for queries that violate a policy will only return data allowed as determined by the policy. For this reason, you should always endevor to test that your policy does and does not work as expected.

Use Case: Limiting Access

Policies can include complicated logic defining read and write permissions. This example from the cookbook below states:

  • users with a yetiRole

  • are allowed to view data

  • associated with the class ex:Yeti

  • except that which pertains to the schema:age of a user other than the one submitting the query.


{
"@context": "https://ns.flur.ee",
"ledger": "cookbook/base",
"insert": {
"@id": "ex:yetiPolicy",
"@type": ["f:Policy"],
"f:targetClass": { "@id": "ex:Yeti" },
"f:allow": [
{
"@id": "ex:yetiViewAllow",
"f:targetRole": { "@id": "ex:yetiRole" },
"f:action": [{ "@id": "f:view" }]
}
],
"f:property": [
{
"@id": "ex:yetisViewOnlyOwnAge",
"f:path": { "@id": "schema:age" },
"f:allow": [
{
"@id": "ex:ageViewRule",
"f:targetRole": { "@id": "ex:yetiRole" },
"f:action": [{ "@id": "f:view" }],
"f:equals": {
"@list": [{ "@id": "f:$identity" }, { "@id": "ex:user" }]
}
}
]
}
]
}
}

This additional complexity, where a user is only allowed to view data associated with their own profile, is outlined in the f:property object in the query above. Here we use a binding for f:equals where f:$identity is a placeholder for information about the user. This is possible because data and access evaluation happens inside the data, so we can use datastate or relationships that may or may not exist in the data to determine access.

Similarly to our first example, after writing an identity policy we can now transact user roles into our ledger, enabling us to query our newly permissioned data:


{
"@context": "https://ns.flur.ee",
"ledger": "cookbook/base",
"insert": {
"@id": "did:fluree:Tf5M4L7SNkziB4Q5gC8Hjuqu9WQKCwKpU1Y",
"ex:user": { "@id": "ex:freddy" },
"f:role": {
"@id": "ex:yetiRole"
}
}
}

For reference, here's an example query that would return limited results based on the provided role, ex:yetiRole.


{
"from": "cookbook/base",
"where": {
"@id": "?s",
"schema:name": "?name"
},
"select": {
"?s": ["*"]
},
"opts": {
"did": "did:fluree:Tf5M4L7SNkziB4Q5gC8Hjuqu9WQKCwKpU1Y",
"role": "ex:yetiRole"
}
}