Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding quip as a cedar use case #201

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions cedar-example-use-cases/quip/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Quip Policies
Quip is a service thatcombines documents, spreadsheets, chat, into a powerful collaboration platform that integrates with Salesforce. Run `.\run.sh` in order to see the example authz requests for Quip.

## Basic Model

Quip has one principal type, which is a `User`. Users have a unique `id`, a `name`, a boolean value for if they are `disabled` or not, as well as an optional `shared_folder_ids` attribute that contains a set of all folders shared with them.

Quip has two resources, `Threads` and `Folders`.

A `Thread` has a unique `id`, an `author_id` of `User` type, and a `title`. They also have an optional set of Users under `user_ids` that are the individuals a document is shared with directly and not rhouh a folder. Threads have an optional `shared_folder_ids`, for all folders that exist in the shared list. To see the expanded list of users that are members of these shared folders, we have an optional `expanded_user_ids` attribute. The most important attribute here is the `access_levels` attribute, which is a set of `member_ids` as `User` types and their `level_of_access`. Finally, there is an optional parent_folder attribute, that contains the parent `Folder` entity (if there is one) of a thread.

A `Folder` has a unique `id`, a `title`, a `creator_id` of `User` type, and a `member_ids` attribute which contains a set of all `User`s who are members of a folder.


There are four levels of access a principal can have for a `Thread`. Those include `FullAccess`, `Edit`, `View`, and `Comment`. For a `Folder` a `User` can only have `FullAccess` or no access. Specific actions are further defined in the cedar schema.



## Examples
There are a few policies defined in `quip.cedar`, as well as sample entities defined in `entity.json`. The `run.sh` script runs multiple queries to confirm expected behavior for this data. To summarize the information in `entity.json`:

### Entities
There are three `Users` (principals):

* Alice Smith
* Bob Johnson
* Charlie Brown


As well as two documents (`Threads`) and a `Folder` they are in.

* [Document] Budget Report
* [Document] Meeting Minutes
* [Folder] Administration


And then another document that is not in any folder

* [Document] Charlie’s Personal Notes

### Assumptions

* Alice Smith is an administrator, so she has access to the entire Administration folder as a member.

* Bob Johnson is a part of the finance department, so he has individual comment access to the Budget Report, but no other documents in the folder.

* Charlie Brown is an intern with no access to the Administration folder or documents in it. But he created the a Personal Notes document.
110 changes: 110 additions & 0 deletions cedar-example-use-cases/quip/entity.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
[
{
"uid": {
"type": "User",
"id": "alice"
},
"attrs": {
"id": "alice",
"name": "Alice Smith",
"disabled": false,
"shared_folder_ids": [{ "type": "Folder", "id": "administration" }]
},
"parents": []
},
{
"uid": {
"type": "User",
"id": "bob"
},
"attrs": {
"id": "bob",
"name": "Bob Johnson",
"disabled": false
},
"parents": []
},
{
"uid": {
"type": "User",
"id": "charlie"
},
"attrs": {
"id": "charlie",
"name": "Charlie Brown",
"disabled": false
},
"parents": []
},
{
"uid": {
"type": "Thread",
"id": "budget_report"
},
"attrs": {
"id": "budget_report",
"author_id": {"type": "User", "id": "some_user"},
"title": "Budget Report",
"user_ids": [{"type": "User", "id": "bob"}],
"shared_folder_ids": [{ "type": "Folder", "id": "administration" }],
"expanded_user_ids": [{"type": "User", "id": "alice"}],
"access_levels": [
{
"member_id": {"type": "User", "id": "alice"},
"level_of_access": "FullAccess"
},
{
"member_id": {"type": "User", "id": "bob"},
"level_of_access": "Comment"
}
],
"parent_folder":{ "type": "Folder", "id": "administration" }
},
"parents": []
},
{
"uid": {
"type": "Thread",
"id": "meeting_minutes"
},
"attrs": {
"id": "meeting_minutes",
"author_id": {"type": "User", "id": "some_user"},
"title": "Meeting Minutes",
"shared_folder_ids": [{ "type": "Folder", "id": "administration" }],
"expanded_user_ids": [{"type": "User", "id": "alice"}],
"access_levels": [
{
"member_id": {"type": "User", "id": "alice"},
"level_of_access": "FullAccess"
}
],
"parent_folder":{ "type": "Folder", "id": "administration" }
},
"parents": []
},
{
"uid": {
"type": "Thread",
"id": "charlies_personal_notes"
},
"attrs": {
"id": "charlies_personal_notes",
"author_id": {"type": "User", "id": "charlie"},
"title": "Charlie's Personal Notes"
},
"parents": []
},
{
"uid": {
"type": "Folder",
"id": "administration"
},
"attrs": {
"id": "administration",
"title": "Administration",
"creator_id": { "type": "User", "id": "some_user" },
"member_ids": [{ "type": "User", "id": "some_user" }, { "type": "User", "id": "alice" }] },
"parents": []
}
]
84 changes: 84 additions & 0 deletions cedar-example-use-cases/quip/quip.cedar
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
@id("thread_author_full_access")
permit(
principal,
action in [Action::"ThreadFullAccessActions"],
resource
)
when {
principal == resource.author_id
};

@id("folder_member_full_access")
permit(
principal,
action in [Action::"FolderFullAccessActions"],
resource
)
when {
resource.member_ids.contains(principal)
};

@id("thread_comment_access_if_user_access_level_comment")
permit(
principal,
action in [Action::"ThreadCommentActions"],
resource
)
when {
resource has access_levels &&
resource.access_levels.contains(
{
"member_id": principal,
"level_of_access": "Comment"
}
)};


@id("thread_full_access_if_user_access_level_full")
permit(
principal,
action in [Action::"ThreadFullAccessActions"],
resource
)
when {
resource has access_levels &&

resource.access_levels.contains(
{
"member_id": principal,
"level_of_access": "FullAccess"
}
)};

@id("thread_edit_access_if_user_access_level_edit")
permit(
principal,
action in [Action::"ThreadEditActions"],
resource
)
when {
resource has access_levels &&

resource.access_levels.contains(
{
"member_id": principal,
"level_of_access": "Edit"
}
)};


@id("thread_view_access_if_user_access_level_view")
permit(
principal,
action in [Action::"ThreadViewActions"],
resource
)
when {
resource has access_levels &&

resource.access_levels.contains(
{
"member_id": principal,
"level_of_access": "View"
}
)};
78 changes: 78 additions & 0 deletions cedar-example-use-cases/quip/quip.cedarschema
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Entities
// All enums are modeled as just String here

entity User {
id: String,
name: String,
disabled: Bool,
shared_folder_ids?: Set<Folder>, // folders that are explicitly shared with the user
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious: why is this field (and other Set-typed fields below) optional? If there are no folders shared with a user, this can be modeled as a required Set attribute, where the Set is empty.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick conjectured answer (but curious about the correct answer): Maybe since it is possible to test that shared_folder_ids is present, but not (at the moment) possible to set whether a set is empty?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good point; that makes sense.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats a good point! I cant remember if I had tried the empty set but had remembered seeing the has keyword for policies with optional fields so went that route. Im still fairly novice with cedar syntax, but like Mike said I'm unsure if theres a way to test whether a set is empty.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A quick update. We've recently added the .isEmpty operator on sets, so if that was the main reason for avoiding empty sets, we now have a nice solution for it :)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(The new operator is coming in version 4.3.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed this PR was still open and wanted to let you know that version 4.3, which includes the .isEmpty() function, is available now. The function is also documented here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Apologies, did not follow up on this. Will update this PR using isEmpty().

};

entity Folder {
id: String,
title: String,
creator_id: User,
member_ids: Set<User>,
};

entity Thread {
id: String,
author_id: User,
title: String,
user_ids?: Set<User>, // the individuals a document is shared with (not through a folder)
shared_folder_ids?: Set<Folder>, // the folders added to a document (including the folder the document is in)
expanded_user_ids?: Set<User>, // all the users in the folders
access_levels?:
Set<{
member_id: User,
level_of_access: String
}>,
// access_levels will have all the necessary information about who has access to what (consolidates the user_ids, shared_folder_ids,expanded_user_ids in one place)
// it does not however include author, so there has to be another policy for that
parent_folder?: Folder
};

//Actions
action ThreadFullAccessActions;
action ThreadEditActions;
action ThreadCommentActions;
action ThreadViewActions;
action FolderFullAccessActions;

action viewThread
in [ThreadFullAccessActions, ThreadEditActions,
ThreadCommentActions, ThreadViewActions]
appliesTo {
principal: User,
resource: Thread,
};

//Theres only full access or no access for folders
action viewFolder, editFolder, shareFolder
in [FolderFullAccessActions]
appliesTo {
principal: User,
resource: Folder,
};

action editThread
in [ThreadFullAccessActions, ThreadEditActions]
appliesTo {
principal: User,
resource: Thread,
};

action shareThread
in [ThreadFullAccessActions]
appliesTo {
principal: User,
resource: Thread,
};

action commentThread
in [ThreadFullAccessActions, ThreadEditActions,
ThreadCommentActions]
appliesTo {
principal: User,
resource: Thread,
};
62 changes: 62 additions & 0 deletions cedar-example-use-cases/quip/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
echo "Testing that Alice can do all actions on the Administration Folder as a member"
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"viewFolder\"" --resource "Folder::\"administration\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"editFolder\"" --resource "Folder::\"administration\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"shareFolder\"" --resource "Folder::\"administration\""


echo "Testing that Alice can do all actions on all Documents in the Administration Folder"
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"viewThread\"" --resource "Thread::\"meeting_minutes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"editThread\"" --resource "Thread::\"meeting_minutes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"commentThread\"" --resource "Thread::\"meeting_minutes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"shareThread\"" --resource "Thread::\"meeting_minutes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"viewThread\"" --resource "Thread::\"budget_report\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"editThread\"" --resource "Thread::\"budget_report\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"commentThread\"" --resource "Thread::\"budget_report\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"shareThread\"" --resource "Thread::\"budget_report\""


echo "Testing that Bob and Charlie can not do any actions on the folder"
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"viewFolder\"" --resource "Folder::\"administration\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"editFolder\"" --resource "Folder::\"administration\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"shareFolder\"" --resource "Folder::\"administration\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"viewFolder\"" --resource "Folder::\"administration\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"editFolder\"" --resource "Folder::\"administration\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"shareFolder\"" --resource "Folder::\"administration\""

echo "Testing that Bob can access Budget Report with Comment actions"
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"viewThread\"" --resource "Thread::\"budget_report\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"editThread\"" --resource "Thread::\"budget_report\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"commentThread\"" --resource "Thread::\"budget_report\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"shareThread\"" --resource "Thread::\"budget_report\""

echo "Testing that Bob can not access Meeting Minutes"
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"viewThread\"" --resource "Thread::\"meeting_minutes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"editThread\"" --resource "Thread::\"meeting_minutes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"commentThread\"" --resource "Thread::\"meeting_minutes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"shareThread\"" --resource "Thread::\"meeting_minutes\""

echo "Testing that Charlie can not access Budget Report or Meeting Minutes"
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"viewThread\"" --resource "Thread::\"budget_report\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"editThread\"" --resource "Thread::\"budget_report\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"commentThread\"" --resource "Thread::\"budget_report\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"shareThread\"" --resource "Thread::\"budget_report\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"viewThread\"" --resource "Thread::\"meeting_minutes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"editThread\"" --resource "Thread::\"meeting_minutes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"commentThread\"" --resource "Thread::\"meeting_minutes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"shareThread\"" --resource "Thread::\"meeting_minutes\""

echo "Testing that Charlie has full access to Charlie's Personal Notes as the author"
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"viewThread\"" --resource "Thread::\"charlies_personal_notes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"editThread\"" --resource "Thread::\"charlies_personal_notes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"commentThread\"" --resource "Thread::\"charlies_personal_notes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"charlie\"" --action "Action::\"shareThread\"" --resource "Thread::\"charlies_personal_notes\""

echo "Testing that Alice and Bob do not have access to Charlie's Personal Notes"
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"viewThread\"" --resource "Thread::\"charlies_personal_notes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"editThread\"" --resource "Thread::\"charlies_personal_notes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"commentThread\"" --resource "Thread::\"charlies_personal_notes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"alice\"" --action "Action::\"shareThread\"" --resource "Thread::\"charlies_personal_notes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"viewThread\"" --resource "Thread::\"charlies_personal_notes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"editThread\"" --resource "Thread::\"charlies_personal_notes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"commentThread\"" --resource "Thread::\"charlies_personal_notes\""
cedar authorize --policies quip.cedar --schema quip.cedarschema --schema-format cedar --entities entity.json --principal "User::\"bob\"" --action "Action::\"shareThread\"" --resource "Thread::\"charlies_personal_notes\""
Loading