Shareable Content
If you're building an application that supports user-generated content (docs, notes, images etc), chances are that you'll also need to build functionality that allows users to share and collaborate on that content. Building such a system can get very complicated very fast.
In this guide, we'll go over how you can use Warrant to quickly augment your existing application to add sharing and collaboration functionality.
Prerequisites
This guide exclusively uses the Warrant API and thus assumes you have a Warrant account with API keys. If you don't, please first follow the Quickstart to set up your account.
Creating Object Types for Each Content Type
Each explicit 'content type' (ex. document, note, image) in our application will require an object type definition in Warrant. Let's say for example that we're building a simplified version of Google Docs. This simplified version has exactly 1 type of content: document
. Each 'document' has an 'owner', 'editor(s)' and 'viewer(s)'. Using these relations, we can easily define who can read and/or modify a specific document in our application.
For each distinct 'content type' in your application, you should create an object type with 'owner', 'editor' and 'viewer' relationships. Note that these permissions cascade, with owners having editor and viewer privileges and editors also having viewer privileges:
- JSON
- cURL
{
"type": "document",
"relations": {
"owner": {},
"editor": {
"inheritIf": "owner"
}
"viewer": {
"inheritIf": "editor"
}
}
}
curl "https://api.warrant.dev/v1/object-types" \
-X POST \
-H "Authorization: ApiKey YOUR_KEY" \
--data-raw \
'{
"type": "document",
"relations": {
"owner": {},
"editor": {
"inheritIf": "owner"
}
"viewer": {
"inheritIf": "editor"
}
}
}'
Granting Permissions
With our object types defined, we can create warrants that grant 'editor' and 'viewer' access to specific objects. For example, let's say that we have a 'document' in our application with id 'doc1'. We can grant a user (user1) 'editor' access to this document by creating the following warrant:
- cURL
- Go
- Java
- Node.js
- Python
- Ruby
curl "https://api.warrant.dev/v1/warrants" \
-X POST \
-H "Authorization: ApiKey YOUR_KEY" \
--data-raw \
'{
"objectType": "document",
"objectId": "doc1",
"relation": "editor",
"subject": {
"objectType": "user",
"objectId": "user1"
}
}'
resp, err := client.CreateWarrant(warrant.Warrant{
ObjectType: "document",
ObjectId: "doc1",
Relation: "editor",
Subject: warrant.Subject{
ObjectType: "user",
ObjectId: "user1",
},
})
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(resp)
}
try {
Subject warrantSubject = new Subject("user", "user1")
Warrant warrantToCreate = new Warrant("document", "doc1", "editor", warrantSubject)
client.createWarrant(warrantToCreate);
} catch (WarrantException e) {
// Handle error
}
const newWarrant = await warrantClient.Warrant.create({
object: {
objectType: "document",
objectId: "doc1",
},
relation: "editor",
subject: {
objectType: "user"
objectId: "user1"
},
});
subject = Subject("user", "user1")
client.create_warrant(object_type="document", object_id="doc1", relation="editor", subject=subject)
begin
Warrant::Warrant.create(
object_type: "document",
object_id: "doc1",
relation: "editor",
subject: {
object_type: "user",
object_id: "user1"
}
)
rescue
# Handle error
end
Checking Permissions
With our content permissions defined via warrants, we can start to enforce access to content directly in the app.
For example, if we wanted to check whether 'user1' had the ability to 'edit' 'doc1', we would add the following check in our application code:
- cURL
- Go
- Java
- Node.js
- Python
- Ruby
curl "https://api.warrant.dev/v2/authorize" \
-X POST \
-H "Authorization: ApiKey YOUR_KEY" \
--data-raw \
'{
"warrants": [
{
"objectType": "document",
"objectId": "doc1",
"relation": "editor",
"subject": {
"objectType": "user",
"objectId": "user1"
}
}
]
}'
isAuthorized, _ := client.IsAuthorized(warrant.WarrantCheckParams{
Warrants: []warrant.Warrant{
{
ObjectType: "document",
ObjectId: "doc1",
Relation: "editor",
Subject: warrant.Subject{
ObjectType: "user",
ObjectId: "user1",
},
}
}
})
if isAuthorized {
// 'user1' can edit doc1
} else {
// Fail request
}
Subject warrantSubject = new Subject("user", "user1")
Warrant warrantToCheck = new Warrant("document", "doc1", "editor", warrantSubject)
boolean isAuthorized = client.isAuthorized(new WarrantCheck(Arrays.asList(warrantToCheck)));
if (isAuthorized) {
// 'user1' can edit doc1
} else {
// Fail request
}
const isAuthorized = await warrantClient.Authorization.check({
warrants: [
{
object: {
objectType: "document",
objectId: "doc1",
},
relation: "editor",
subject: {
objectType: "user",
objectId: "user1",
},
},
],
});
if (isAuthorized) {
// 'user1' can edit doc1
} else {
// Fail request
}
is_authorized = client.Authz.check("document", "doc1", "editor", Subject("user", "user1"))
if is_authorized:
# 'user1' can edit doc1
else:
# Fail request
unless Warrant::Warrant.is_authorized?(
warrants: [
{
object_type: "document",
object_id: "doc1",
relation: "editor",
subject: {
object_type: "user",
object_id: "user1"
}
}
])
# User Unauthorized
end
Summary
In this guide, we went over how you can use Warrant to quickly implement an access model for 'shareable content'. By assigning each object type an 'owner', 'editor(s)' and 'viewer(s)', we can easily implement logic in our application to manage and enforce content permissions at runtime.