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

Sales orgs example #152

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

# Sales org static
echo -e "\nTesting Sales Orgs (static)..."
validate "sales_orgs/static" "policies.cedar" "policies.cedarschema"
authorize "sales_orgs/static" "policies.cedar" "entities.json" "policies.cedarschema"
#format "sales_orgs/static" "policies.cedar"

# Sales org templated
echo -e "\nTesting Sales Orgs (templated)..."
validate "sales_orgs/templated" "policies.cedar" "policies.cedarschema" "linked"
authorize "sales_orgs/templated" "policies.cedar" "entities.json" "policies.cedarschema" "linked"
#format "sales_orgs/templated" "policies.cedar"

# Hotel chains
echo -e "\nTesting Hotels (static)..."
validate "hotel_chains/static" "policies.cedar" "policies.cedarschema"
Expand Down
28 changes: 28 additions & 0 deletions cedar-example-use-cases/sales_orgs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Sales orgs policies

ABC has a contracted sales force, and sales folk have access to resources based on their role and the type of resource.

## Use-case

ABC has one principal type, which is a `User`. Users are distinguished by their `job` (an attribute), which is an enumeration. The job can either be _internal_, or some other _external_ type, which includes _distributor_ and _customer_. A customer is assigned to a particular distributor if it shares the distributor's customer ID. Users can be a member of zero or more `Market`s.

ABC has two protected resources, `Presentation` and `Template` (unfortunate name clash). The creator of a resource is its `owner`, who is permitted to carry out any action on the resource. Other users are granted _direct_ access to a resource by being added to an ACL as one of two roles, _viewer_ or _editor_. A `User` can also be granted access to a `Template` via a `Market` the user is a member of, again as either a viewer or editor. In other words, a `Template` has viewer and editor users, directly, but also viewer and editor markets, which grant access to the users within them. The permissions gained by being a viewer/editor depend on whether the `User` in question has an internal or external job.

There are some rules limiting how access can be shared. Only _distributor_ `User`s can share with _customer_ `User`s, and in particular those with their customer ID. And only _internal_ `User`s can be granted editor access to resources.

## Approaches

The `static/` directory contains policies and a schema for that encodes the _viewer_ and _editor_ relations on resources, both for presentations and templates, as `Set`-typed attributes `viewers` and `editors` on the resources.

The `templated/` directory uses Cedar templates instead. We drop the `viewers` and `editors` attributes and follow a simple pattern: Whenever you would add a `User` to _resource_`.viewers`, instead link a template with `?principal` as the user and `?resource` as the viewer. Do likewise for editors. And, do similarly with viewer/editor status of `Market`s on ABC (not Cedar) `Template` resources.

These are the only differences in the encodings.

## Examples

In each directory is a `run.sh` file that carries out three authorization examples asking whether a principal, either `User::"Alce"`, `User::"Bob"`, or `User::"Charlie"`, in that order, is allowed to `Action::"viewPresentation"` on `Presentation::"proposal"`. They all use the `entities.json` file, and the `templated/` policies also use the `linked` file that expresses two template links.

The answers are, respectively, `ALLOW`, `ALLOW`, and `DENY`, for the following reasons:
* Alice is the owner of the presentation
* Bob is an allowed viewer of the presentation. In the `static/` policies this fact is expressed in the `entities.json` file as part of the `Presentation::"proposal"` entity. In the `templated\` policies this fact is expressed via template links, expressed in the `linked` file
* Charlie is neither or these things (nor is he an editor of the presentation)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"principal":"User::\"Alice\"",
"action":"Action::\"viewPresentation\"",
"resource":"Presentation::\"proposal\"",
"context":{ }
}
6 changes: 6 additions & 0 deletions cedar-example-use-cases/sales_orgs/static/ALLOW/bob_view.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"principal":"User::\"Bob\"",
"action":"Action::\"viewPresentation\"",
"resource":"Presentation::\"proposal\"",
"context":{ }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"principal":"User::\"Charlie\"",
"action":"Action::\"viewPresentation\"",
"resource":"Presentation::\"proposal\"",
"context":{ }
}
42 changes: 42 additions & 0 deletions cedar-example-use-cases/sales_orgs/static/entities.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[
{
"uid": { "type": "User", "id": "Alice"},
"attrs": {
"job": { "type": "Job", "id": "internal" },
"customerId": "1AYZ"
},
"parents": [{"type": "Market", "id": "YoungAdult"}]
},
{
"uid": { "type": "Market", "id": "YoungAdult" },
"attrs": {},
"parents": []
},
{
"uid": { "type": "User", "id": "Bob"} ,
"attrs": {
"job": { "type": "Job", "id": "customer" },
"customerId": "1AYZ"
},
"parents": [{"type": "Market", "id": "YoungAdult"}]
},
{
"uid": { "type": "User", "id": "Charlie"} ,
"attrs": {
"job": { "type": "Job", "id": "distributor" },
"customerId": "1AYZ"
},
"parents": []
},
{
"uid": { "type": "Presentation", "id": "proposal"},
"attrs": {
"owner": { "type": "User", "id": "Alice" },
"viewers": [
{ "type": "User", "id": "Bob" }
],
"editors": []
},
"parents": []
}
]
106 changes: 106 additions & 0 deletions cedar-example-use-cases/sales_orgs/static/policies.cedar
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// PRESENTATION POLICIES
@id("external-prez-view")
permit(
principal,
action in Action::"ExternalPrezViewActions",
resource)
when {
principal in resource.viewers
};

@id("internal-prez-view")
permit(
principal,
action in Action::"InternalPrezViewActions",
resource)
when {
principal.job == Job::"internal" &&
principal in resource.viewers
};

// Authorizes edit actions generally, but these limited with forbid policies
@id("prez-edit")
permit(
principal,
action in Action::"PrezEditActions",
resource)
when {
resource.owner == principal ||
principal in resource.editors
};

// only permit sharing to non-customers
@id("limit-prez-view-customer")
forbid(
principal,
action == Action::"grantViewAccessToPresentation",
resource)
unless {
context.target.job != Job::"customer" ||
(principal.job == Job::"distributor" &&
principal.customerId == context.target.customerId)
};

// forbid sharing editor access to non-internal users
@id("limit-prez-edit-to-internal")
forbid(
principal,
action == Action::"grantEditAccessToPresentation",
resource)
when {
context.target.job != Job::"internal"
};

// TEMPLATE POLICIES
@id("market-template-view")
permit(
principal,
action in Action::"MarketTemplateViewActions",
resource)
when {
principal in resource.viewerMarkets
};

@id("internal-template-view")
permit(
principal,
action in Action::"InternalTemplateViewActions",
resource)
when {
principal.job == Job::"internal" && principal in resource.viewers
};

// Authorizes edit actions generally, but these limited with forbid policies
@id("template-edit")
permit(
principal,
action in Action::"TemplateEditActions",
resource)
when {
resource.owner == principal ||
principal in resource.editors ||
principal in resource.editorMarkets
};

// only permit sharing by internal users to non-customers
@id("limit-template-grant-view")
forbid(
principal,
action == Action::"grantViewAccessToTemplate",
resource)
when {
context has targetUser && context.targetUser.job == Job::"customer" &&
(principal.job != Job::"distributor" ||
principal.customerId != context.targetUser.customerId)
};

// forbid sharing editor access to non-internal users
@id("limit-template-grant-edit-internal")
forbid(
principal,
action == Action::"grantEditAccessToTemplate",
resource)
when {
context has targetUser && context.targetUser.job != Job::"internal"
// context.targetMarket always Ok, no matter the market
};
80 changes: 80 additions & 0 deletions cedar-example-use-cases/sales_orgs/static/policies.cedarschema
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Entities
// entity Job enum ["internal", "distributor", "customer", "other"];
entity Job;
entity User in [Market] {
job: Job,
customerId: String, // irrelevant for Job::"internal"
};
entity Market;
entity Presentation {
owner: User,
viewers: Set<User>,
editors: Set<User>,
};
entity Template {
owner: User,
viewers: Set<User>,
editors: Set<User>,
viewerMarkets: Set<Market>,
editorMarkets: Set<Market>,
};
// Actions -- Presentations
action InternalPrezViewActions;
action ExternalPrezViewActions;
action PrezEditActions;
action viewPresentation, removeSelfAccessFromPresentation
in [InternalPrezViewActions, ExternalPrezViewActions, PrezEditActions]
appliesTo {
principal: User,
resource: Presentation,
};
action duplicatePresentation in [InternalPrezViewActions, PrezEditActions]
appliesTo {
principal: User,
resource: Presentation,
};
action editPresentation in [PrezEditActions]
appliesTo {
principal: User,
resource: Presentation,
};
// granting access depends on who it is -- context has target
action grantViewAccessToPresentation, grantEditAccessToPresentation
in [PrezEditActions]
appliesTo {
principal: User,
resource: Presentation,
context: { target: User, },
};

// Actions -- Templates
action InternalTemplateViewActions;
action MarketTemplateViewActions;
action TemplateEditActions;
action viewTemplate, duplicateTemplate
in [InternalTemplateViewActions, TemplateEditActions,
MarketTemplateViewActions]
appliesTo {
principal: User,
resource: Template,
};
action removeSelfAccessFromTemplate
in [InternalTemplateViewActions, TemplateEditActions]
appliesTo {
principal: User,
resource: Template
};
action editTemplate, removeOthersAccessToTemplate in [TemplateEditActions]
appliesTo {
principal: User,
resource: Template
};
// granting access depends on who, or what, it is -- spec. in context
action grantViewAccessToTemplate, grantEditAccessToTemplate
in [TemplateEditActions]
appliesTo {
principal: User,
resource: Template,
context: { targetMarket?: Market, targetUser?: User },
};

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"principal":"User::\"Alice\"",
"action":"Action::\"viewPresentation\"",
"resource":"Presentation::\"proposal\"",
"context":{ }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"principal":"User::\"Bob\"",
"action":"Action::\"viewPresentation\"",
"resource":"Presentation::\"proposal\"",
"context":{ }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"principal":"User::\"Charlie\"",
"action":"Action::\"viewPresentation\"",
"resource":"Presentation::\"proposal\"",
"context":{ }
}
4 changes: 4 additions & 0 deletions cedar-example-use-cases/sales_orgs/templated/NOTES
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
These policies were systematically converted from the `static/` policies
by replacing each policy that references `resource.viewers`, `resource.editors`, etc. with a template-linked policy instead. Then, each user that would have been a member of `resource.viewers` is linked against the relevant templates instead.

One interesting thing here is that you have to remember to link _all_ of the policies for each viewer, whereas in the `static/` policies just require updating the resource's `viewers` attribute a single time. Having to remember to do multiple links is a source of potential bugs, especially as new templates might get added over time, since the code needs to be updated to perform the links.
38 changes: 38 additions & 0 deletions cedar-example-use-cases/sales_orgs/templated/entities.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[
{
"uid": { "type": "User", "id": "Alice"},
"attrs": {
"job": { "type": "Job", "id": "internal" },
"customerId": "1AYZ"
},
"parents": [{"type": "Market", "id": "YoungAdult"}]
},
{
"uid": { "type": "Market", "id": "YoungAdult" },
"attrs": {},
"parents": []
},
{
"uid": { "type": "User", "id": "Bob"} ,
"attrs": {
"job": { "type": "Job", "id": "customer" },
"customerId": "1AYZ"
},
"parents": [{"type": "Market", "id": "YoungAdult"}]
},
{
"uid": { "type": "User", "id": "Charlie"} ,
"attrs": {
"job": { "type": "Job", "id": "distributor" },
"customerId": "1AYZ"
},
"parents": []
},
{
"uid": { "type": "Presentation", "id": "proposal"},
"attrs": {
"owner": { "type": "User", "id": "Alice" }
},
"parents": []
}
]
18 changes: 18 additions & 0 deletions cedar-example-use-cases/sales_orgs/templated/linked
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"template_id": "external-prez-view",
"link_id": "BobView",
"args": {
"?principal": "User::\"Bob\"",
"?resource": "Presentation::\"proposal\""
}
},
{
"template_id": "internal-prez-view",
"link_id": "BobViewInternal",
"args": {
"?principal": "User::\"Bob\"",
"?resource": "Presentation::\"proposal\""
}
}
]
Loading
Loading