Skip to content

Commit

Permalink
docs: add check- and expand-API guides (#493)
Browse files Browse the repository at this point in the history
  • Loading branch information
zepatrik authored Mar 22, 2021
1 parent d38618a commit 09a25b4
Show file tree
Hide file tree
Showing 57 changed files with 1,323 additions and 86 deletions.
2 changes: 1 addition & 1 deletion .schema/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@
{
"type": "integer",
"format": "int64",
"name": "depth",
"name": "max-depth",
"in": "query"
}
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash
set -euo pipefail

export KETO_WRITE_REMOTE="127.0.0.1:4467"

echo '// ownership
directories:/photos#owner@maureen
files:/photos/beach.jpg#owner@maureen
files:/photos/mountains.jpg#owner@laura
// maureen granted access to /photos to laura
directories:/photos#access@laura
// the following tuples are defined implicitly through subject set rewrites (not supported yet)
directories:/photos#access@(directories:/photos#owner)
files:/photos/beach.jpg#access@(files:/photos/beach.jpg#owner)
files:/photos/beach.jpg#access@(directories:/photos#access)
files:/photos/mountains.jpg#access@(files:/photos/mountains.jpg#owner)
files:/photos/mountains.jpg#access@(directories:/photos#access)' | \
keto relation-tuple parse - --format json | \
keto relation-tuple create - >/dev/null \
&& echo "Successfully created tuples" \
|| echo "Encountered error"
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash
set -euo pipefail

echo '// ownership
directories:/photos#owner@maureen
files:/photos/beach.jpg#owner@maureen
files:/photos/mountains.jpg#owner@laura
// maureen granted access to /photos to laura
directories:/photos#access@laura
// the following tuples are defined implicitly through subject set rewrites (not supported yet)
directories:/photos#access@(directories:/photos#owner)
files:/photos/beach.jpg#access@(files:/photos/beach.jpg#owner)
files:/photos/beach.jpg#access@(directories:/photos#access)
files:/photos/mountains.jpg#access@(files:/photos/mountains.jpg#owner)
files:/photos/mountains.jpg#access@(directories:/photos#access)' | \
keto relation-tuple parse - --format json | \
jq "[ .[] | { relation_tuple: . , action: \"insert\" } ]" -c | \
curl -X PATCH --silent --fail \
--data @- \
http://127.0.0.1:4467/relationtuple

echo "Successfully created tuples"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Successfully created tuples
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import grpc from '@ory/keto-acl/node_modules/@grpc/grpc-js/build/src/index.js'
import acl from '@ory/keto-acl/acl_pb.js'
import writeService from '@ory/keto-acl/write_service_grpc_pb.js'
import writeData from '@ory/keto-acl/write_service_pb.js'

const writeClient = new writeService.WriteServiceClient(
'127.0.0.1:4467',
grpc.credentials.createInsecure()
)

const writeRequest = new writeData.TransactRelationTuplesRequest()

const insert = (tuple) => {
const tupleDelta = new writeData.RelationTupleDelta()
tupleDelta.setAction(writeData.RelationTupleDelta.Action.INSERT)
tupleDelta.setRelationTuple(tuple)

writeRequest.addRelationTupleDeltas(tupleDelta)
}

const addSimpleTuple = (namespace, object, relation, user) => {
const relationTuple = new acl.RelationTuple()
relationTuple.setNamespace(namespace)
relationTuple.setObject(object)
relationTuple.setRelation(relation)

const sub = new acl.Subject()
sub.setId(user)
relationTuple.setSubject(sub)

insert(relationTuple)
}

// ownership
addSimpleTuple('directories', '/photos', 'owner', 'maureen')
addSimpleTuple('files', '/photos/beach.jpg', 'owner', 'maureen')
addSimpleTuple('files', '/photos/mountains.jpg', 'owner', 'laura')
// granted access
addSimpleTuple('directories', '/photos', 'access', 'laura')

// should be subject set rewrite
// owners have access
;[
['files', '/photos/beach.jpg'],
['files', '/photos/mountains.jpg'],
['directories', '/photos']
].forEach(([namespace, object]) => {
const relationTuple = new acl.RelationTuple()
relationTuple.setNamespace(namespace)
relationTuple.setObject(object)
relationTuple.setRelation('access')

const subjectSet = new acl.SubjectSet()
subjectSet.setNamespace(namespace)
subjectSet.setObject(object)
subjectSet.setRelation('owner')

const sub = new acl.Subject()
sub.setSet(subjectSet)
relationTuple.setSubject(sub)

insert(relationTuple)
})

// should be subject set rewrite
// access on parent means access on child
;['/photos/beach.jpg', '/photos/mountains.jpg'].forEach((file) => {
const relationTuple = new acl.RelationTuple()
relationTuple.setNamespace('files')
relationTuple.setObject(file)
relationTuple.setRelation('access')

const subjectSet = new acl.SubjectSet()
subjectSet.setNamespace('directories')
subjectSet.setObject('/photos')
subjectSet.setRelation('access')

const sub = new acl.Subject()
sub.setSet(subjectSet)
relationTuple.setSubject(sub)

insert(relationTuple)
})

writeClient.transactRelationTuples(writeRequest, (error) => {
if (error) {
console.log('Encountered error', error)
} else {
console.log('Successfully created tuples')
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package main

import (
"context"
"fmt"

"google.golang.org/grpc"

acl "github.com/ory/keto/proto/ory/keto/acl/v1alpha1"
)

func main() {
conn, err := grpc.Dial("127.0.0.1:4467", grpc.WithInsecure())
if err != nil {
panic("Encountered error: " + err.Error())
}

client := acl.NewWriteServiceClient(conn)

//directories:/photos#owner@maureen
//files:/photos/beach.jpg#owner@maureen
//files:/photos/mountains.jpg#owner@laura
//directories:/photos#access@laura
//directories:/photos#access@(directories:/photos#owner)
//files:/photos/beach.jpg#access@(files:/photos/beach.jpg#owner)
//files:/photos/beach.jpg#access@(directories:/photos#access)
//files:/photos/mountains.jpg#access@(files:/photos/mountains.jpg#owner)
//files:/photos/mountains.jpg#access@(directories:/photos#access)

tupleDeltas := []*acl.RelationTupleDelta{
// ownership
{
Action: acl.RelationTupleDelta_INSERT,
RelationTuple: &acl.RelationTuple{
Namespace: "directories",
Object: "/photos",
Relation: "owner",
Subject: &acl.Subject{Ref: &acl.Subject_Id{
Id: "maureen",
}},
},
},
{
Action: acl.RelationTupleDelta_INSERT,
RelationTuple: &acl.RelationTuple{
Namespace: "files",
Object: "/photos/beach.jpg",
Relation: "owner",
Subject: &acl.Subject{Ref: &acl.Subject_Id{
Id: "maureen",
}},
},
},
{
Action: acl.RelationTupleDelta_INSERT,
RelationTuple: &acl.RelationTuple{
Namespace: "files",
Object: "/photos/mountains.jpg",
Relation: "owner",
Subject: &acl.Subject{Ref: &acl.Subject_Id{
Id: "laura",
}},
},
},
// granted access
{
Action: acl.RelationTupleDelta_INSERT,
RelationTuple: &acl.RelationTuple{
Namespace: "directories",
Object: "/photos",
Relation: "access",
Subject: &acl.Subject{Ref: &acl.Subject_Id{
Id: "laura",
}},
},
},
}
// should be subject set rewrite
// owners have access
for _, o := range []struct{ n, o string }{
{"files", "/photos/beach.jpg"},
{"files", "/photos/mountains.jpg"},
{"directories", "/photos"},
} {
tupleDeltas = append(tupleDeltas, &acl.RelationTupleDelta{
Action: acl.RelationTupleDelta_INSERT,
RelationTuple: &acl.RelationTuple{
Namespace: o.n,
Object: o.o,
Relation: "access",
Subject: &acl.Subject{Ref: &acl.Subject_Set{Set: &acl.SubjectSet{
Namespace: o.n,
Object: o.o,
Relation: "owner",
}}},
},
})
}
// should be subject set rewrite
// access on parent means access on child
for _, obj := range []string{"/photos/beach.jpg", "/photos/mountains.jpg"} {
tupleDeltas = append(tupleDeltas, &acl.RelationTupleDelta{
Action: acl.RelationTupleDelta_INSERT,
RelationTuple: &acl.RelationTuple{
Namespace: "files",
Object: obj,
Relation: "access",
Subject: &acl.Subject{Ref: &acl.Subject_Set{Set: &acl.SubjectSet{
Namespace: "directories",
Object: "/photos",
Relation: "access",
}}},
},
})
}

_, err = client.TransactRelationTuples(context.Background(), &acl.TransactRelationTuplesRequest{
RelationTupleDeltas: tupleDeltas,
})
if err != nil {
panic("Encountered error: " + err.Error())
}

fmt.Println("Successfully created tuples")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail

export KETO_READ_REMOTE="127.0.0.1:4466"

keto expand access files /photos/beach.jpg --format json-pretty --max-depth 3
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
set -euo pipefail

curl -G --silent \
--data-urlencode "namespace=files" \
--data-urlencode "relation=access" \
--data-urlencode "object=/photos/beach.jpg" \
--data-urlencode "max-depth=3" \
http://127.0.0.1:4466/expand | \
jq
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"type": "union",
"subject": "files:/photos/beach.jpg#access",
"children": [
{
"type": "union",
"subject": "directories:/photos#access",
"children": [
{
"type": "leaf",
"subject": "directories:/photos#owner"
},
{
"type": "leaf",
"subject": "laura"
}
]
},
{
"type": "union",
"subject": "files:/photos/beach.jpg#owner",
"children": [
{
"type": "leaf",
"subject": "maureen"
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import grpc from '@ory/keto-acl/node_modules/@grpc/grpc-js/build/src/index.js'
import expandService from '@ory/keto-acl/expand_service_grpc_pb.js'
import expandData from '@ory/keto-acl/expand_service_pb.js'
import acl from '@ory/keto-acl/acl_pb.js'

const expandClient = new expandService.ExpandServiceClient(
'127.0.0.1:4466',
grpc.credentials.createInsecure()
)

const subjectSet = new acl.SubjectSet()
subjectSet.setNamespace('files')
subjectSet.setRelation('access')
subjectSet.setObject('/photos/beach.jpg')

const sub = new acl.Subject()
sub.setSet(subjectSet)

const expandRequest = new expandData.ExpandRequest()
expandRequest.setSubject(sub)
expandRequest.setMaxDepth(3)

// helper to get a nice result
const subjectString = (subject) => {
if (subject.hasId()) {
return subject.getId()
}
const set = subject.getSet()
return set.getNamespace() + ':' + set.getObject() + '#' + set.getRelation()
}

// helper to get a nice result
const prettyTree = (tree) => {
const [nodeType, subject, children] = [
tree.getNodeType(),
subjectString(tree.getSubject()),
tree.getChildrenList()
]
switch (nodeType) {
case expandData.NodeType.NODE_TYPE_LEAF:
return { type: 'leaf', subject }
case expandData.NodeType.NODE_TYPE_UNION:
return { type: 'union', subject, children: children.map(prettyTree) }
}
}

expandClient.expand(expandRequest, (error, resp) => {
if (error) {
console.log('Encountered error:', error)
} else {
console.log(JSON.stringify(prettyTree(resp.getTree()), null, 2))
}
})
Loading

0 comments on commit 09a25b4

Please sign in to comment.