# Fine-Grained Data Permissions

Vendia has controls to allow data producers to limit the actions of data consumers on stored data.

Vendia’s fine-grained data permissions are designed to be both simple to use and highly flexible. This feature enables administrators to easily limit who can take action on data written to a **project**.

**NOTE:** Defining fine-grained permissions on data is optional. Not including the Access Control List (ACL) will result in the default behavior of giving full Create, Read, Update and Delete (CRUD) access to all **workspace** partners in the **project** network. This default behavior can be overridden by applying a [Sharing Policy](https://docs.vendia.com/platform/operational/secure-data-sharing/fine-grained-data-permissions/#sharing-policies).

## Scope of Fine-grained Data Permissions

Fine-grained data permissions serve to constrain the visibility of data in a **project**. They also set boundaries on the ability to modify data in a **project**. It does _not_ affect how users interact with Vendia APIs. If you’re interested in defining how to control how Vendia users create and manage **projects**, please refer to [Role-based Access Controls](https://docs.vendia.com/platform/operational/secure-data-sharing/rbac).

## Key Terms

- **ACL:** Access Control List (ACL) is a list of objects that contain the fine-grained definitions that control access to data in a **project**.
- **Principal:** Principal is a list of **workspaces** to which the ACL is applied.
- **Path:** \[Optional\] The path defines the attribute of the object to apply the ACL operations. This field is optional. If it is not specified, the specified operations are applied to the entire object.
- **Operations:** Operations define what action a Principal can take on data.
- **aclInput:** aclInput is a GraphQL object that contains a list of Access Control List (ACL) definitions that are applied when data is added or updated in a **project**.

## Operations

Vendia allows the following operations to be assigned to Principals when data is added or updated in a **project**:

- **`READ`:** Permits the Principal to list and get data.
- **`WRITE`:** Permits the Principal to update, put, and delete data.
- **`ALL`:** Permits the Principal to both `READ` and `WRITE` data.
- **`UPDATE_ACL`:** Permits the Principal to update the ACL. This permission can only be granted by the Principal who added the data.

**NOTE:** The `ALL` operation does _not_ include `UPDATE_ACL`.

## Schema Definition Requirement

In order to take advantage of fine-grained data permissions, the JSON schema must include the `x-vendia-acls` top-level type. For example, if my JSON schema as two distinct types - `Foo` and `Bar` - and I wish to enable fine-grained permissions on each type, I would set `x-vendia-acls` as the following:

```
"x-vendia-acls": {

"FooAcl": {

"type": "Foo"

},

"BarAcl": {

"type": "Bar"

}

}
```

**NOTE:** The `x-vendia-acls` top-level type must be defined when the **project** is created. Schemas cannot be updated to include this definition after the **project** has been created.

## Restricting Data Access

When creating data on the Vendia platform, data producers can optionally restrict access to **workspace** partners in the Network. The restriction can be either global (across all fields), or fine-grained (different for each field).

## Sharing Policies

Instead of relying on data producers to set the appropriate ACL on every item entry, each **workspace** partner can configure sharing policies. Sharing policies are ACLs that are globally defined. If an item is created without an explicit ACL, the matching sharing policy for the entity will be used.

### Creating a Sharing Policy

Sharing policies are managed per entity within the Vendia UI. To create a sharing policy, navigate to the data entity you want to configure and select the **Sharing Policy** tab. If ACLs are enabled for that entity, you can click the button to create a sharing policy and configure the default access controls. To manage sharing policies for different entities, navigate to each entity and use its **Sharing Policy** tab.

Sharing policies can also be created through the [GraphQL API](https://docs.vendia.com/platform/operational/scalar-data/graphql), [CLI](https://docs.vendia.com/platform/cli/commands), and [SDK](https://docs.vendia.com/platform/cli/vendia-client-sdk).

### Using a Sharing Policy

When configured, sharing policies are used without any additional configuration for API requests. A sharing policy’s configuration will be used has the default ACL when an explicit ACL is not defined in the GraphQL mutation.

GraphQL Request

```
add_Product(input: {price: 10, sku: "test-123"} ) {

result {

... on Self_Product {

_acl {

operations

path

principal {

nodes {

}

}

}

sku

price

}

}

}
```

Response

```
{

"data": {
    "add_Product": {
      "result": {
        "_acl": [
          {
            "operations": ["READ"],
            "path": null,
            "principal": {
              "nodes": ["Acme"]
            }
          },
          {
            "operations": ["ALL"],
            "path": "title",
            "principal": {
              "nodes": ["Helix"]
            }
          }
        ],
        "sku": "test-123",
        "price": 10
      }
    }
  }
}
```

## API Usage Examples

Now that we have a sense of the operations that can be allowed, how to define our list of principals, and how to specify that our JSON schema types will use fine-grained permissions, let’s go through a very simple example to illustrate how to assign controls when adding or updating data in a **project**.

We will provide an example to demonstrate how **workspaces** can share recipe information in a controlled manner in a **project**. We will also demonstrate how to query data that has controls associated with it.

### Data Model and Project Configuration

Below is the JSON schema that will be used as the basis for our queries.

Click to view Recipe Schema

```
{

"$schema": "http://json-schema.org/draft-07/schema#",

"$id": "http://vendia.com/schemas/blog/fine_grained_control.json",

"title": "Sample schema for setting fine-grained access controls",

"description": "Model bakery recipes",

"x-vendia-acls": {

"RecipeAcl": {

"type": "Recipe"

}

},

"type": "object",

"properties": {

"Recipe": {

"description": "Recipe information",

"type": "array",

"items": {

"type": "object",

"properties": {

"name": {

"description": "The common name of recipe",

"type": "string"

},

"sku": {

"description": "SKU of the recipe",

"type": "string"

},

"price": {

"description": "Sales price of the recipe in USD",

"type": "number"

},

"recipeType": {

"description": "Type of recipe",

"type": "string",

"enum": ["cake", "cupcake", "pie", "muffin"]

},

"recipeYield": {

"description": "Quantity yielded by the recipe",

"type": "number"

},

"ingredients": {

"description": "Ingredient listing needed for the recipe",

"type": "array",

"items": {

"type": "object",

"properties": {

"name": {

"description": "Name of the ingredient",

"type": "string"

},

"quantity": {

"description": "Quantity of the ingredient needed for the yield",

"type": "string"

}

}

}

},

"directions": {

"description": "Steps to make the recipe",

"type": "array",

"items": {

"description": "Discrete step",

"type": "string"

}

}

}

}

}

}

}
```

Click to view sample Recipe workspace configuration

**Note:** This example uses a 3- **workspace** **project** configuration.

Our **project** is made of 3 **workspaces** - `Alice`, `Bob`, and `Eve`. All queries will make references to these **workspace** names.

```
[
  {
    "name": "Alice",
    "userId": "alice@recipe-creator.com",
    "region": "us-east-2"
  },
  {
    "name": "Bob",
    "userId": "bob@bobs-bakery.com",
    "region": "us-west-2"
  },
  {
    "name": "Eve",
    "userId": "eve@eves-bakery.com",
    "region": "us-east-1"
  }
]
```

**NOTE:** Be sure to update the `userId` and `region` appropriately if you are deploying the example.

### Writing Data with Fine-grained Controls

The following mutations should be run from the `Alice` **workspace**.

Sample Mutation - Allow Read-Only Access to a Recipe

In this example, we will specify that all **workspaces** in a **project** will have the ability to `READ` all attributes of our `Red Velvet Cake` recipe. However, only the **workspace**`Alice` will have the ability to `WRITE` changes.

This mutation adds our `aclInput` in addition to our standard `input`.

```
mutation addRedVelvetCake {

add_Recipe(

input: {

name: "Red Velvet Cake"

sku: "ca001"

price: 5.00

recipeType: "cake"

recipeYield: 1

ingredients: [
        { name: "All-purpose Flour", quantity: "453 grams" }
        { name: "Granulated Sugar", quantity: "680.3 grams" }
      ]

directions: ["Mix dry ingredients", "Bake", "Profit"]

}

aclInput: { acl: [{ principal: { nodes: ["*"] }, operations: [READ] }] }

syncMode: ASYNC

) {

transaction {

_id

_owner

}

}

}
```

Sample Mutation - Allow Different Operators By Workspace

In this mutation, we will specify that the `Bob` **workspace** has the ability to view all attributes of our `Sprinkles Cupcake` recipe. `Eve` will only be able to read the `name`, `price`, `recipeType`, and `recipeYield`. The attributes `ingredients` and `directions` will not be visible in query results from `Eve`.

This mutation adds our `aclInput` in addition to our standard `input`.

```
mutation addSprinklesCupcake {

add_Recipe(

input: {

name: "Sprinkles Cupcake"

sku: "cc001"

price: 5.99

recipeType: cupcake

recipeYield: 100

ingredients: [
        { name: "All-purpose Flour", quantity: "783.33 grams" }
        { name: "Granulated Sugar", quantity: "833 grams" }
      ]

directions: [
        "Mix dry ingredients"
        "Bake"
        "Let cupcakes cool for 20min"
        "Make icing"
        "Put icing on cupcakes"
        "Profit"
      ]

}

aclInput: {

acl: [
        { principal: { nodes: ["*"] }, path: "name", operations: [READ] }
        { principal: { nodes: ["*"] }, path: "sku", operations: [READ] }
        { principal: { nodes: ["NodeOne"] }, path: "price", operations: [READ] }
        {
          principal: { nodes: ["NodeOne"] }
          path: "recipeType"
          operations: [READ]
        }
        {
          principal: { nodes: ["NodeOne"] }
          path: "recipeYield"
          operations: [READ]
        }
      ]

}

syncMode: ASYNC

) {

transaction {

_id

_owner

}

}

}
```

In the example above, all **workspaces** have the ability to read the name and sku, while only WorkspaceOne has the ability to read the price and recipe information.

### Reading Data with Fine-grained Controls

Let’s go ahead and issue queries from our **workspaces** and see how the results change depending upon the **workspace** from which the query was issued.

Sample Query - Read data that may or may not be shared

```
query listRecipes {

list_RecipeItems {

_RecipeItems {

_id

name

price

sku

recipeType

recipeYield

directions

ingredients {

name

quantity

}

}

}

}
```

#### Alice

Since `Alice` added the recipes, the **workspace** is the owner of the two `Recipes`. The query will return all attributes of each `Recipe`.

Sample Query Result - Alice Results

**NOTE:** The `_id` of each `Recipe` will be different in your results.

```
{

"data": {
    "listRecipes": {
      "Recipes": [
        {
          "_id": "017b3bc0-fe35-893f-5c88-ac73eddd88df",
          "name": "Sprinkles Cupcake",
          "sku": "cc001",
          "price": 5.99,
          "recipeType": "cupcake",
          "ingredients": [
            {
              "name": "All-purpose Flour",
              "quantity": "783.33 grams"
            },
            {
              "name": "Granulated Sugar",
              "quantity": "833 grams"
            }
          ],
          "directions": ["Mix dry ingredients", "Bake", "Profit"]
        },
        {
          "_id": "017b3bf0-43e6-26ac-119f-81d5a60ef574",
          "name": "Red Velvet Cake",
          "sku": "ca001",
          "price": 5,
          "recipeType": "cake",
          "ingredients": [
            {
              "name": "All-purpose Flour",
              "quantity": "453 grams"
            },
            {
              "name": "Granulated Sugar",
              "quantity": "680.3 grams"
            }
          ],
          "directions": ["Mix dry ingredients", "Bake", "Profit"]
        }
      ]

}

}
}
```

#### Bob Results

`Bob` has been granted `READ` access to all attributes of both `Recipes` - implicitly for `Red Velvet Cake` and explicitly for `Sprinkles Cupcake`. The results of our query will match the results of `Alice`.

Sample Query Result - Bob Results

**NOTE:** The `_id` of each `Recipe` will be different in your results.

```
{

}

}
}
```

#### Eve Results

`Eve` has been granted `READ` access to all attributes of `Red Velvet Cake` but can only view a subset of `Sprinkles Cupcake` attributes. `Eve` cannot view the data for `sku`, `ingredients`, or `directions`.

Sample Query Result - Eve Results

**NOTE:** The `_id` of each `Recipe` will be different in your results.

```
{

"data": {
    "listRecipes": {
      "Recipes": [
        {
          "_id": "017b3bc0-fe35-893f-5c88-ac73eddd88df",
          "name": "Sprinkles Cupcake",
          "sku": null,
          "price": 5.99,
          "recipeType": "cupcake",
          "ingredients": null,
          "directions": null
        },
        {
          "_id": "017b3bf0-43e6-26ac-119f-81d5a60ef574",
          "name": "Red Velvet Cake",
          "sku": "ca001",
          "price": 5,
          "recipeType": "cake",
          "ingredients": [
            {
              "name": "All-purpose Flour",
              "quantity": "453 grams"
            },
            {
              "name": "Granulated Sugar",
              "quantity": "680.3 grams"
            }
          ],
          "directions": ["Mix dry ingredients", "Bake", "Profit"]
        }
      ]

}

}
}
```

### Updating Data with Fine-grained Controls

Since `Alice` created both `Recipe` records, `Alice` has full access and can run a `updateRecipe` mutation to update any of the attributes of both records.

Since neither `Bob` nor `Eve` have the ability to write data for either record, both will receive an `Unauthorized` exception when running the `updateRecipe` mutation.

Sample mutation - updating data without access

**NOTE:** The `_id` you use will be different in your **project**.

```
mutation updateSprinklesCupcake {

updateRecipe(

id: "017b3bc0-fe35-893f-5c88-ac73eddd88df"

input: { name: "Super Awesome Sprinkles Cupcake" }

syncMode: ASYNC

) {

transaction {

_id

_owner

}

}

}
```

Sample Mutation Result - Bob Results

```
{

"data": {
    "updateRecipe": null
  },

"errors": [
    {
      "message": "unauthorized",
      "locations": [
        {
          "line": 2,
          "column": 5
        }
      ],
      "path": ["updateRecipe"]
    }
  ]
}
```

Sample Mutation Result - Eve Results

```
{

"data": {
    "updateRecipe": null
  },

"errors": [
    {
      "message": "unauthorized",
      "locations": [
        {
          "line": 33,
          "column": 5
        }
      ],
      "path": ["updateRecipe"]
    }
  ]
}
```

## Effects of Fine-grained Controls on Blocks

Vendia will take fine-grained permissions into account when writing transactions into a [block](https://docs.vendia.com/platform/vendia-terminology#block). The block record for each **workspace** will reflect the operations permitted.

If a **workspace** has permission to the data then it will be visible in the `transactions`. The list of `mutations` will contain clear transparency regarding what was changed in the **project**. The `redactedTxHash` value will be null. If, however, a **workspace** does not have full visibility into the data that is part of the block then it will be reflected. The `mutations` will only display the subset of data to which the **workspace** should have access. The `redactedTxHash` will not be null.

For example, if the following `list_Blocks` query is run from `Eve`, we will only see the transaction data that should be visible.

```
query listBlocks {

listVendia_BlockItems {

Vendia_BlockItems {

_id

redactedBlockHash

previousRedactedBlockHash

previousBlockHash

previousBlockId

transactions {

mutations

owner

redactedTxHash

}

}

}

}
```

Eve Results

The `Mutations` for the addition of the `Sprinkles Cupcake` only includes data `Eve` has access to.

```
{

"data": {
    "listVendia_BlockItems": {
      "Vendia_BlockItems": [
        ...
        {
          "_id": "000000000000002",
          "redactedBlockHash": "20d1968ddf5bee2831830c7353e62aefb33cbbd4b726cb9a0524508593af810d",
          "previousRedactedBlockHash": "7b4ebbf3052b5300bf4315364c00d1582abc892bf94d1abd9c7ff6ec09d698d2",
          "previousBlockHash": "ff0b499d269bbd5e2255c340da466f22376dc32b3429a7d324a00a49c8db53a9",
          "previousBlockId": "000000000000001",
          "transactions": [
            {
              "mutations": [
                "mutation m{addRecipe(id:\"017b3bc0-fe35-893f-5c88-ac73eddd88df\",input: {name: \"Sprinkles Cupcake\", price: 5.99, recipeType: \"cupcake\", recipeYield: 100},aclInput: {acl: [{principal: {nodes: [\"Bob\"]}, operations: [READ]}, {principal: {nodes: [\"Eve\"]}, path: \"name\", operations: [READ]}, {principal: {nodes: [\"Eve\"]}, path: \"price\", operations: [READ]}, {principal: {nodes: [\"Eve\"]}, path: \"recipeType\", operations: [READ]}, {principal: {nodes: [\"Eve\"]}, path: \"recipeYield\", operations: [READ]}]}){error}"
              ],
              "_owner": "Alice",
              "redactedTxHash": "2fc17018d68bc5852d4912cf95f55a82d0e04a18e94ef7dd2bb629f9eb12136b"
            }
          ]
        },
        {
          "_id": "000000000000003",
          "redactedBlockHash": "d83b3edde7b0079bae9fc3dbc814891f11a85d0104d12b7c6ad3f63b10466272",
          "previousRedactedBlockHash": "20d1968ddf5bee2831830c7353e62aefb33cbbd4b726cb9a0524508593af810d",
          "previousBlockHash": "5bb22779e063938fed2cc32f335dab2f3b0092881874b328c9c3bd7a359a2245",
          "previousBlockId": "000000000000002",
          "transactions": [
            {
              "mutations": [
                "addRecipe(id:\"017b3bf0-43e6-26ac-119f-81d5a60ef574\",input: {directions: [\"Mix dry ingredients\", \"Bake\", \"Profit\"], ingredients: [{name: \"All-purpose Flour\", quantity: \"453 grams\"}, {name: \"Granulated Sugar\", quantity: \"680.3 grams\"}], name: \"Red Velvet Cake\", price: 5, recipeType: \"cake\", recipeYield: 1, sku: \"ca001\"},aclInput: {acl: [{principal: {nodes: [\"*\"]}, operations: [READ]}]}){error}"
              ],
              "_owner": "Alice",
              "redactedTxHash": null
            }
          ]
        }
      ]
    }
  }
}
```
