diff --git a/cedar-example-use-cases/quip/README.md b/cedar-example-use-cases/quip/README.md new file mode 100644 index 0000000..75eca5a --- /dev/null +++ b/cedar-example-use-cases/quip/README.md @@ -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. diff --git a/cedar-example-use-cases/quip/entity.json b/cedar-example-use-cases/quip/entity.json new file mode 100644 index 0000000..83d7e05 --- /dev/null +++ b/cedar-example-use-cases/quip/entity.json @@ -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": [] + } + ] \ No newline at end of file diff --git a/cedar-example-use-cases/quip/quip.cedar b/cedar-example-use-cases/quip/quip.cedar new file mode 100644 index 0000000..481f948 --- /dev/null +++ b/cedar-example-use-cases/quip/quip.cedar @@ -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" + } +)}; \ No newline at end of file diff --git a/cedar-example-use-cases/quip/quip.cedarschema b/cedar-example-use-cases/quip/quip.cedarschema new file mode 100644 index 0000000..9e21850 --- /dev/null +++ b/cedar-example-use-cases/quip/quip.cedarschema @@ -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, // folders that are explicitly shared with the user +}; + +entity Folder { + id: String, + title: String, + creator_id: User, + member_ids: Set, + }; + +entity Thread { + id: String, + author_id: User, + title: String, + user_ids?: Set, // the individuals a document is shared with (not through a folder) + shared_folder_ids?: Set, // the folders added to a document (including the folder the document is in) + expanded_user_ids?: Set, // 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, + }; \ No newline at end of file diff --git a/cedar-example-use-cases/quip/run.sh b/cedar-example-use-cases/quip/run.sh new file mode 100755 index 0000000..3a3ef14 --- /dev/null +++ b/cedar-example-use-cases/quip/run.sh @@ -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\""