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

Tax preparer example #151

Merged
merged 4 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
6 changes: 6 additions & 0 deletions cedar-example-use-cases/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,10 @@ validate "tags_n_roles" "policies.cedar" "policies.cedarschema"
authorize "tags_n_roles" "policies.cedar" "entities.json" "policies.cedarschema"
format "tags_n_roles" "policies.cedar"

# Tax preparer
echo -e "\nTesting Tax preparer..."
validate "tax_preprarer" "policies.cedar" "policies.cedarschema" "linked"
authorize "tax_preprarer" "policies.cedar" "entities.json" "policies.cedarschema" "linked"
format "tax_preprarer" "policies.cedar"

exit "$any_failed"
16 changes: 16 additions & 0 deletions cedar-example-use-cases/tax_preprarer/ALLOW/alice_read_ABC.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"principal": "Taxpreparer::Professional::\"Alice\"",
"action": "Taxpreparer::Action::\"viewDocument\"",
"resource": "Taxpreparer::Document::\"ABC\"",
"context": {
"consent": {
"client": {
"__entity": {
"type": "Taxpreparer::Client",
"id": "Ramon"
}
},
"team_region_list": [ "IAD", "JFK" ]
}
}
}
16 changes: 16 additions & 0 deletions cedar-example-use-cases/tax_preprarer/ALLOW/alice_read_DEF.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"principal": "Taxpreparer::Professional::\"Alice\"",
"action": "Taxpreparer::Action::\"viewDocument\"",
"resource": "Taxpreparer::Document::\"DEF\"",
"context": {
"consent": {
"client": {
"__entity": {
"type": "Taxpreparer::Client",
"id": "Ramon"
}
},
"team_region_list": [ "IAD", "JFK" ]
}
}
}
16 changes: 16 additions & 0 deletions cedar-example-use-cases/tax_preprarer/ALLOW/bob_read_DEF.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"principal": "Taxpreparer::Professional::\"Bob\"",
"action": "Taxpreparer::Action::\"viewDocument\"",
"resource": "Taxpreparer::Document::\"DEF\"",
"context": {
"consent": {
"client": {
"__entity": {
"type": "Taxpreparer::Client",
"id": "Ramon"
}
},
"team_region_list": [ "IAD", "JFK" ]
}
}
}
16 changes: 16 additions & 0 deletions cedar-example-use-cases/tax_preprarer/DENY/alice_read_ABC.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"principal": "Taxpreparer::Professional::\"Alice\"",
"action": "Taxpreparer::Action::\"viewDocument\"",
"resource": "Taxpreparer::Document::\"ABC\"",
"context": {
"consent": {
"client": {
"__entity": {
"type": "Taxpreparer::Client",
"id": "Ramon"
}
},
"team_region_list": [ "JFK" ]
}
}
}
16 changes: 16 additions & 0 deletions cedar-example-use-cases/tax_preprarer/DENY/bob_read_ABC.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"principal": "Taxpreparer::Professional::\"Bob\"",
"action": "Taxpreparer::Action::\"viewDocument\"",
"resource": "Taxpreparer::Document::\"ABC\"",
"context": {
"consent": {
"client": {
"__entity": {
"type": "Taxpreparer::Client",
"id": "Ramon"
}
},
"team_region_list": [ "IAD", "JFK" ]
}
}
}
36 changes: 36 additions & 0 deletions cedar-example-use-cases/tax_preprarer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Taxpreparer policies

This use-case simulates an organization that prepares taxes for its clients.

## Use-case

A `Professional` needs to access a `Client`'s `Document` in order to prepare their taxes. There are two rules that grant access:

1. That the `Professional` has been granted access to the document. This could be because

a. she belongs to an _organization_ whose features (service line, location, etc.) are a match for features of the `Document`, or

b. because she has been granted _ad hoc_ access.
2. That the `Client` has given consent for professionals in a particular country to look at documents they own. A consent is modeled as a `Consent` entity, which is passed in with the authorization request's `context`.

Rules 1a and 2 are expressed as static policies, and rule 1b is expressed as a link to a template.

Rules 2 is expressed as a `forbid` rule so that it affects any _ad hoc_ `permit` policies added later. Alternatively, it could have been expressed as an additional `when` clause in the `permit` policies, both the static one and the template.

## Tests

The test setup defines the following entities, in `entities.json`:

- `Professional`s `Alice` and `Bob`. They are both part of `org-1` in the `corporate` serviceline, and are located at `IAD` and `JFK`, respectively.
- `Client` `Ramon` is contracting with the `corporate` serviceline
- `Document`s `ABC` and `DEF`, owned by `Ramon`, and located at `IAD` and `JFK`, respectively.

The setup also includes file `linked`, which links the ad-hoc access template to grant `Alice` access to document `DEF`.

Then we have five scenarios:

1. Alice requests access to ABC -- this is allowed per rules 1a and 2: Alice is part of the appropriate serviceline, organization, and location, and the request shows that her particular location has been consented to
2. Alice requests access to DEF -- this is allowed per rules 1b and 2: She has been granted ad hoc access, and the request shows that her particular location has been consented to
3. Bob requests access to DEF -- this is allowed per rules 1a and 2.
4. Alice requests access to ABC -- this time the request is denied because rule 2 is not satisfied: the provided consent does not include Alice's location
5. Bob requests access to ABC -- this is not allowed because neither rules 1a nor 1b are satisfied.
76 changes: 76 additions & 0 deletions cedar-example-use-cases/tax_preprarer/entities.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
[
{
"uid": {
"type": "Taxpreparer::Professional",
"id": "Alice"
},
"attrs": {
"location": "IAD",
"assigned_orgs": [
{
"organization": "org-1",
"serviceline": "corporate",
"location": "IAD"
}
]
},
"parents": []
},
{
"uid": {
"type": "Taxpreparer::Professional",
"id": "Bob"
},
"attrs": {
"location": "JFK",
"assigned_orgs": [
{
"organization": "org-1",
"serviceline": "corporate",
"location": "JFK"
}
]
},
"parents": []
},
{
"uid": {
"type": "Taxpreparer::Client",
"id": "Ramon"
},
"attrs": {
"organization": "org-1"
},
"parents": []
},
{
"uid": {
"type": "Taxpreparer::Document",
"id": "ABC"
},
"attrs": {
"serviceline": "corporate",
"location": "IAD",
"owner": {
"type": "Taxpreparer::Client",
"id": "Ramon"
}
},
"parents": []
},
{
"uid": {
"type": "Taxpreparer::Document",
"id": "DEF"
},
"attrs": {
"serviceline": "corporate",
"location": "JFK",
"owner": {
"type": "Taxpreparer::Client",
"id": "Ramon"
}
},
"parents": []
}
]
10 changes: 10 additions & 0 deletions cedar-example-use-cases/tax_preprarer/linked
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"template_id": "adhoc-access",
"link_id": "AliceView",
"args": {
"?principal": "Taxpreparer::Professional::\"Alice\"",
"?resource": "Taxpreparer::Document::\"DEF\""
}
}
]
39 changes: 39 additions & 0 deletions cedar-example-use-cases/tax_preprarer/policies.cedar
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Rule 1a: organization-level access
permit (
principal,
action == Taxpreparer::Action::"viewDocument",
resource
)
when
{
principal.assigned_orgs
.contains
(
{organization
:resource.owner.organization,
serviceline
:resource.serviceline,
location
:resource.location}
)
};

// Rule 1b: ad hoc access (per linked template)
@id("adhoc-access")
permit (
principal == ?principal,
action == Taxpreparer::Action::"viewDocument",
resource == ?resource
);

// Rule 2: consent must be given by a document's owner
forbid (
principal,
action == Taxpreparer::Action::"viewDocument",
resource
)
unless
{
context.consent.client == resource.owner &&
context.consent.team_region_list.contains(principal.location)
};
33 changes: 33 additions & 0 deletions cedar-example-use-cases/tax_preprarer/policies.cedarschema
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace Taxpreparer {
type orgInfo = {
organization: String,
serviceline: String,
location: String,
};
// A tax-preparing professional
entity Professional = {
assigned_orgs: Set<orgInfo>,
location: String,
};
// A client's tax document
entity Document = {
serviceline: String,
location: String,
owner: Client,
};
// A client
entity Client = {
organization: String
};
// The record of consent from a client to view a doc
type Consent = {
client: Client,
team_region_list: Set<String>
};

action viewDocument appliesTo {
principal: [Professional],
resource: [Document],
context: { consent: Consent }
};
}
16 changes: 13 additions & 3 deletions test_utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@ validate() {
local folder=$1
local policies=$2
local schema=$3
local links=$4
echo " Running validation on ${policies}"
res="$(cedar validate --policies "$folder/$policies" --schema "$folder/$schema" --schema-format human)"
if [ -z "$links" ]
then
res="$(cedar validate --policies "$folder/$policies" --schema "$folder/$schema" --schema-format human)"
else
res="$(cedar validate --policies "$folder/$policies" --schema "$folder/$schema" --schema-format human -k "$folder/$links" )"
fi
if [[ $? == 0 ]]
then
passed "validate succeeded"
Expand All @@ -44,16 +50,20 @@ authorize() {
local policies=$2
local entities=$3
local schema=$4
local links=$5
echo " Running authorization on ${policies}"
for decision in ALLOW DENY
do
for file in "$folder/$decision"/*.json
do
if [ -z "$schema" ]
if [ -z "$schema" -a -z "$links" ]
then
IFS=$'\n' read -r -d '' -a tmp_array < <(cedar authorize --policies "$folder/$policies" --entities "$folder/$entities" --request-json "$file" -v && printf '\0')
else
elif [ -z "$links" ]
then
IFS=$'\n' read -r -d '' -a tmp_array < <(cedar authorize --policies "$folder/$policies" --schema "$folder/$schema" --schema-format human --entities "$folder/$entities" --request-json "$file" -v && printf '\0')
else
IFS=$'\n' read -r -d '' -a tmp_array < <(cedar authorize --policies "$folder/$policies" -k "$folder/$links" --schema "$folder/$schema" --schema-format human --entities "$folder/$entities" --request-json "$file" -v && printf '\0')
fi
res="${tmp_array[0]}"
unset tmp_array[0]
Expand Down
Loading