Sharing Data with Fine-grained Control
Vendia Share offers customers the ability to share data with internal and external partners. Our customers often want to share data while also enforcing fine-grained access controls restricting partners to only the data elements they're authorized to access. Vendia Share has had the ability to enforce access controls on files. Today, we're proud to announce the release of these same controls on data elements. With this release, Uni nodes can specify which party has the ability to read or write data on a per-data-element basis.
Let's use an example and explore the mechanics you can use to take advantage of this feature.
Introducing Alice, Bob, and Eve
Let's say we have data - recipes - that needs to be shared among three parties - Alice
, Bob
, and Eve
. Alice
is known for creating amazing recipes and licenses her popular recipes to commercial bakeries. Because she derives income from these sales, it's important for her to control who can see them. Alice
also has a need to share her catalog of recipes to drive her business. The catalog contains a subset of recipe data - everything except for the ingredients and directions. Bob
and Eve
both own bakeries and know that Alice
has a knack for putting together some great recipes for cakes, cupcakes, cookies, pies, and muffins.
In this post, we'll explore how to translate these business requirements into a data sharing model. First, we'll review how Alice
can selectively share recipe attributes with Bob
and Eve
. Second, we'll demonstrate how Alice
can provide read-only access to entire recipes that have been purchased.
Bootstrapping Our Recipe Uni
In order to share our recipe data, we need to first create a Uni. Let's use the following schema to define our data model. Please note we're using a new top-level type in our schema - x-vendia-acls - to indicate we will be allowing fine-grained control in our data model as well as the type we will be allowing the controls on.
NOTE: If we don't include x-vendia-acls, all parties in a Uni will have the ability to read and write data; this is the default behavior.
Recipe Schema
Save the file below as schema.json
.
{
"$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", "cookie", "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"
}
}
}
}
}
}
}
Example Registration File
Save the file below as registration.json
.
NOTE: Take care to update the name of the Uni and the userId and region for each node.
NOTE: Uni names must be uniquely named.
{
"name": "fine-grained-control-example",
"schema": "schema.json",
"nodes": [
{
"name": "Alice",
"userId": "user@vendia.com",
"region": "us-east-2",
"settings": {
"apiSettings": {
"auth": {
"authorizerType": "API_KEY"
}
}
}
},
{
"name": "Bob",
"userId": "user@vendia.com",
"region": "us-east-2",
"settings": {
"apiSettings": {
"auth": {
"authorizerType": "API_KEY"
}
}
}
},
{
"name": "Eve",
"userId": "user@vendia.com",
"region": "us-east-2",
"settings": {
"apiSettings": {
"auth": {
"authorizerType": "API_KEY"
}
}
}
}
]
}
We can use the Vendia CLI to deploy our Uni. Use the following command to get our sample Uni up and running.
share uni create -c registration.json
Selectively Share Recipe Attributes
Alice
wants her recipe catalog to be fully visible to potential clients in order to drive more business. However, she needs to control access to those recipes, limiting the actual directions and ingredients to those bakeries who have purchased them. We need to model the access control to the underlying data. We can specify the recipe attributes and the access privileges to those attributes.
Alice
can use the following GraphQL mutation to add a new cupcake recipe to her catalog. Make note of the new attribute in our mutation - aclInput. This is where Alice
can specify who should have specified permissions applied to her new recipe. Using this simple example, Alice will only let Bob and Eve see the catalog information - the recipe name, price, sku, recipeType, and recipeYield.
addSprinklesCupcake Mutation
Run the following mutation from the Alice
GraphQL Explorer.
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: ["*"]
},
path: "price",
operations: [READ]
},
{
principal: {
nodes: ["*"]
},
path: "recipeType",
operations: [READ]
},
{
principal: {
nodes: ["*"]
},
path: "recipeYield",
operations: [READ]
}
]
}
) {
transaction {
transactionId
}
}
}
In this example, we use "*" for our nodes. This is a short-hand representation of all current and future nodes in our Uni. Alternately, we could have specified each node as an item in a list like so:
principal: {
nodes: [ "Bob", "Eve" ]
}
NOTE: These two approaches are not interchangeable with respect to newly added business partners. If nodes are defined explicitly and need access to existing data then you will need to update the aclInput of those records accordingly.
When Alice
inserts this new recipe, Bob
and Eve
will be able to view some, but not all, properties. Bob
and Eve
will be able to read the catalog data of the Sprinkles Cupcake recipe. However, neither will be able to see the ingredients or directions since Alice didn't give either of them access to those properties. They will also not be able to update or delete the recipe since they both only have READ access. Let's run the following query from the GraphQL Explorer associated with either Bob
or Eve
to confirm the behavior.
listRecipes Query
Run this query from either the Bob
or Eve
GraphQL Explorer.
query listRecipes {
list_RecipeItems {
_RecipeItems {
... on Self_Recipe {
_id
name
price
sku
recipeType
recipeYield
directions
ingredients {
name
quantity
}
}
... on Self_Recipe_Partial_ {
_id
name
price
sku
recipeType
recipeYield
directions
ingredients {
name
quantity
}
}
}
}
}
We will receive results that look similar to the following:
{
"data": {
"listRecipes": {
"Recipes": [
{
"id": "017b9817-ee1e-2ab8-4ec4-44ea4b12ae2b",
"name": "Sprinkles Cupcake",
"price": 5.99,
"sku": "cc001",
"recipeType": "cupcake",
"recipeYield": 100,
"directions": null,
"ingredients": null
}
]
}
}
}
Notice the name, price, sku, recipeType, and recipeYield are visible; the ingredient and directions fields are displayed as null.
Selectively Share Entire Recipes
New Red Velvet Cake
We know that Alice
will only share directions and ingredients with customers who have purchased a recipe. Alice
has come up with a new recipe for Red Velvet Cake. She wants to make sure that both Bob
and Eve have the ability to see this new recipe in the catalog.
We can run the following mutation from Alice
's GraphQL Explorer.
addRedVelvetCake Mutation
Run this query from the Alice
GraphQL Explorer.
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: [ "*" ]
},
path: "name",
operations: [ READ ]
},
{
principal: {
nodes: [ "*" ]
},
path: "sku",
operations: [ READ ]
},
{
principal: {
nodes: [ "*" ]
},
path: "price",
operations: [ READ ]
},
{
principal: {
nodes: [ "*" ]
},
path: "recipeType",
operations: [ READ ]
},
{
principal: {
nodes: [ "*" ]
},
path: "recipeYield",
operations: [ READ ]
}
]
}
) {
transaction {
transactionId
}
}
}
Since no one has yet bought the recipe, only the catalog data is available to Bob and Eve.
In our scenario, Eve
has set up notifications when a new recipe has been added. Eve
has been keeping an eye out for a Red Velvet Cake recipe and decides to buy Alice
's recipe. After going through the purchase process, Alice
can update Eve's permissions to allow full read access to the Red Velvet Cake recipe. Let's update the Red Velvet Cake recipe to allow Eve
to view all the attributes.
Update ACLs on the Existing Red Velvet Cake Recipe
First, we will need to identify the _id of the Red Velvet Cake recipe.
listRecipes - Red Velvet Cake
query listRecipes {
list_RecipeItems(filter: {name: {eq: "Red Velvet Cake"}}) {
_RecipeItems {
... on Self_Recipe {
_id
name
_acl {
principal {
nodes
}
path
operations
}
}
... on Self_Recipe_Partial_ {
_id
name
_acl {
principal {
nodes
}
path
operations
}
}
}
}
}
Once we have the _id of the recipe, we can run another query to update the ACL on the Red Velvet Cake recipe.
NOTE: Your _id value will be different. Replace the _id field in the following examples with the one returned in your actual list.
updateRedVelvetCakeAcl After Eve's Purchase
mutation updateRedVelvetCakeAcl {
put_Recipe(
id: "017cf035-6a71-219b-97ab-3af9e2386f72",
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: [ "Eve" ]
},
operations: [ READ ]
},
{
principal: {
nodes: [ "*" ]
},
path: "name",
operations: [ READ ]
},
{
principal: {
nodes: [ "*" ]
},
path: "sku",
operations: [ READ ]
},
{
principal: {
nodes: [ "*" ]
},
path: "price",
operations: [ READ ]
},
{
principal: {
nodes: [ "*" ]
},
path: "recipeType",
operations: [ READ ]
},
{
principal: {
nodes: [ "*" ]
},
path: "recipeYield",
operations: [ READ ]
},
]
}
) {
transaction {
transactionId
}
}
}
Eve's Data Visibility
Now that Eve
has purchased the Red Velvet Cake recipe and Alice has updated permissions on it, we can run the following listRecipes query from Eve
's GraphQL Explorer.
listRecipes Query
query listRecipes {
list_RecipeItems {
_RecipeItems {
... on Self_Recipe {
_id
name
price
sku
recipeType
recipeYield
directions
ingredients {
name
quantity
}
}
... on Self_Recipe_Partial_ {
_id
name
price
sku
recipeType
recipeYield
directions
ingredients {
name
quantity
}
}
}
}
}
We will receive results that look similar to the following:
{
"data": {
"list_RecipeItems": {
"_RecipeItems": [
{
"_id": "017cf031-8f01-360c-dfc8-c15a17d348dc",
"name": "Sprinkles Cupcake",
"price": 5.99,
"sku": "cc001",
"recipeType": "cupcake",
"recipeYield": 100,
"directions": null,
"ingredients": null
},
{
"_id": "017cf035-6a71-219b-97ab-3af9e2386f72",
"name": "Red Velvet Cake",
"price": 5,
"sku": "ca001",
"recipeType": "cake",
"recipeYield": 1,
"directions": [
"Mix dry ingredients",
"Bake",
"Profit"
],
"ingredients": [
{
"name": "All-purpose Flour",
"quantity": "453 grams"
},
{
"name": "Granulated Sugar",
"quantity": "680.3 grams"
}
]
}
]
}
}
}
Both directions and ingredients are available for the Red Velvet Cake recipe Eve
purchased and permissions to the Sprinkles Cupcake recipe remain unchanged.
Bob's Data Visibility
Bob
hasn't purchased any recipes from Alice
, which is reflected in the data to which he has access. If we run the same listRecipes query from Bob
's GraphQL Explorer, we will see all data for both the Sprinkles Cupcake and Red Velvet Cake recipes except for the directions and ingredients.
{
"data": {
"list_RecipeItems": {
"_RecipeItems": [
{
"_id": "017cf031-8f01-360c-dfc8-c15a17d348dc",
"name": "Sprinkles Cupcake",
"price": 5.99,
"sku": "cc001",
"recipeType": "cupcake",
"recipeYield": 100,
"directions": null,
"ingredients": null
},
{
"_id": "017cf035-6a71-219b-97ab-3af9e2386f72",
"name": "Red Velvet Cake",
"price": 5,
"sku": "ca001",
"recipeType": "cake",
"recipeYield": 1,
"directions": null,
"ingredients": null
}
]
}
}
}
Conclusion
Vendia makes it easy, fast, and fun to share data with partners. With the introduction of fine-grained data access control, it's now even easier for companies to control how their data is used, and for developers to quickly convert those policies into secure Universal Applications (Unis). Review our product documentation for more information.