Skip to content
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This repository contains examples demonstrating the use of [Cedar](https://githu
|---------------------------------------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`tinytodo`][] | Rust, Python | A simple application for managing task lists that uses Cedar for authorization demonstrating the usage of the [Cedar Rust APIs][] |
| [`tinytodo-go`][] | Go, Python | A simple application for managing task lists that uses Cedar for authorization demonstrating the usage of the [Cedar Go APIs][] | |
| [`cedar-go-hello-world`][] | Go | A simple application demonstrating the usage of the [Cedar Go APIs][] |
| [`cedar-java-hello-world`][] | Java | A simple application demonstrating the usage of the [Cedar Java APIs][] |
| [`cedar-rust-hello-world`][] | Rust | A simple application demonstrating the usage of the [Cedar Rust APIs][] |
| [`cedar-wasm-example`][] | TypeScript | A simple application demonstrating the usage of the [Cedar Wasm APIs][] |
Expand All @@ -26,6 +27,7 @@ This project is licensed under the Apache-2.0 License.
[Cedar Java APIs]: https://github.com/cedar-policy/cedar-java
[Cedar Wasm APIs]: https://github.com/cedar-policy/cedar/tree/main/cedar-wasm
[`cedar-example-use-cases`]: ./cedar-example-use-cases
[`cedar-go-hello-world`]: ./cedar-go-hello-world
[`cedar-java-hello-world`]: ./cedar-java-hello-world
[`cedar-rust-hello-world`]: ./cedar-rust-hello-world
[`cedar-wasm-example`]: ./cedar-wasm-example
Expand Down
15 changes: 15 additions & 0 deletions cedar-go-hello-world/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Cedar Go Hello World
This repository contains a simple hello world program demonstrating typical usage of the [Cedar Go APIs](https://github.com/cedar-policy/cedar-go).
It is designed for demonstration purposes and does not reflect a production-ready application.

The file `main.go` provides example code, showcasing the primary steps an application takes to use Cedar.
- Parsing policies from JSON and Cedar
- Creating policies programmatically with the AST package
- Creating entity sets
- Creating authorization requests
- Outputting and interpreting the decision from the request

## Setup and run
The example is built on Go 1.25.6 and uses cedar-go 1.6.0 - you will need to have a compatible version of Go installed.
To run the program, navigate to the `cedar-go-hello-world` directory and run the following command:
`go run .`
7 changes: 7 additions & 0 deletions cedar-go-hello-world/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module cedar-go-hello-world

go 1.25.6

require github.com/cedar-policy/cedar-go v1.6.0

require golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e // indirect
6 changes: 6 additions & 0 deletions cedar-go-hello-world/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
github.com/cedar-policy/cedar-go v1.6.0 h1:5dYWkrQjza+GzdJxnzmus7Ag/2pHv4bYWe460/kDlAM=
github.com/cedar-policy/cedar-go v1.6.0/go.mod h1:h5+3CVW1oI5LXVskJG+my9TFCYI5yjh/+Ul3EJie6MI=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e h1:Ctm9yurWsg7aWwIpH9Bnap/IdSVxixymIb3MhiMEQQA=
golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
214 changes: 214 additions & 0 deletions cedar-go-hello-world/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
package main

import (
"fmt"

"github.com/cedar-policy/cedar-go"
"github.com/cedar-policy/cedar-go/ast"
)

func main() {
// For an authorization evaluation, we need three inputs:
// 1. A cedar.PolicySet to evaluate against the request.
// 2. A cedar.EntityMap that contains the entities referenced in the policies and request.
// 3. A cedar.Request to evaluate.
// In this example, we define the policies, entities, and requests inline,
// but they could also be loaded from external sources.
policySet := buildPolicySet()
entityMap := buildEntityMap()
// We build a slice of requests to showcase the different outcomes of the cedar.Authorize method.
evalRequests := buildRequests()

// The first request is for Alice to view JanesVacation.jpg, which should be allowed by policy0.
fmt.Println("Evaluating if Alice can view JanesVacation.jpg...")
ok, diag := cedar.Authorize(policySet, entityMap, evalRequests[0])
outputDecision(ok, diag)

// The second request is for Alice to view JanesVacationPrivate.jpg, which should be denied by policy2.
fmt.Println("Evaluating if Alice can view JanesVacationPrivate.jpg...")
ok, diag = cedar.Authorize(policySet, entityMap, evalRequests[1])
outputDecision(ok, diag)

// The third request is for Alice to view John's vacation album, which should be denied and error
// as the resource Album::"john_vacation" does not exist in the entity map.
fmt.Println("Evaluating if Alice can view John's vacation album...")
ok, diag = cedar.Authorize(policySet, entityMap, evalRequests[2])
outputDecision(ok, diag)

// The fourth request is for Bob to view JanesVacation.jpg, which should be denied as no policy gives permission.
fmt.Println("Evaluating if Bob can view JanesVacation.jpg...")
ok, diag = cedar.Authorize(policySet, entityMap, evalRequests[3])
outputDecision(ok, diag)
}

// buildPolicySet constructs a PolicySet with three policies:
// 1. A standard permit policy defined via JSON.
// 2. A permit policy with context defined via Cedar syntax.
// 3. A forbid policy constructed via AST.
// It returns the constructed PolicySet for use in authorization evaluations.
func buildPolicySet() *cedar.PolicySet {
// policy0 allows Alice to view the photo JanesVacation.jpg
policy0 := buildPolicyFromJson()
// policy1 allows Alice to view a photo if it's tagged with work
policy1 := buildPolicyFromCedar()
// policy2 forbids access to resources tagged as private unless the principal is the owner of the resource.
policy2 := buildPolicyFromAST()

policySet := cedar.NewPolicySet()
policySet.Add("policy0", &policy0)
policySet.Add("policy1", &policy1)
policySet.Add("policy2", policy2)

return policySet
}

// buildPolicyFromJson constructs a simple permit policy from a JSON representation.
// The policy allows the user "alice" to perform the "view" action on the Photo resource "JanesVacation.jpg".
func buildPolicyFromJson() cedar.Policy {
var jsonPolicy = []byte(`{
"effect": "permit",
"principal": {"op": "==", "entity": {"type": "User", "id": "alice"}},
"action": {"op": "==", "entity": {"type": "Action", "id": "view"}},
"resource": {"op": "==", "entity": {"type": "Photo", "id": "JanesVacation.jpg"}}
}`)

var policy0 cedar.Policy
if err := policy0.UnmarshalJSON(jsonPolicy); err != nil {
fmt.Println("Unmarshal error for JSON policy:", err)
return cedar.Policy{}
}

return policy0
}

// buildPolicyFromCedar constructs a permit policy from a Cedar syntax representation.
// The policy allows the user "john" to perform the "view" action on the "jane_vacation" album resource
// when the resource is tagged with "work".
func buildPolicyFromCedar() cedar.Policy {
var cedarPolicy = []byte(`permit (
principal == User::"john",
action == Action::"view",
resource in Album::"jane_vacation"
)
when{ resource.tags.contains("work") };`)

var policy1 cedar.Policy
if err := policy1.UnmarshalCedar(cedarPolicy); err != nil {
fmt.Println("Unmarshal error for Cedar policy:", err)
return cedar.Policy{}
}
return policy1
}

// buildPolicyFromAST constructs a forbid policy programmatically using the AST package.
// The policy forbids actions when the resource is tagged as private,
// unless the principal is the owner of the resource.
func buildPolicyFromAST() *cedar.Policy {

astPolicy := ast.Forbid().
When(ast.Resource().Access("tags").Contains(ast.String("private"))).
Unless(ast.Resource().Access("owner").Equal(ast.Principal()))

policy2 := cedar.NewPolicyFromAST(astPolicy)
return policy2
}

// buildEntityMap constructs an EntityMap containing the entities referenced in the policies and requests.
func buildEntityMap() cedar.EntityMap {
var jsonEntities = []byte(`[
{
"uid": { "type": "User", "id": "alice" },
"attrs": {},
"parents": []
},
{
"uid": { "type": "User", "id": "john" },
"attrs": {},
"parents": []
},
{
"uid": { "type": "User", "id": "jane" },
"attrs": {},
"parents": []
},
{
"uid": { "type": "Photo", "id": "JanesVacation.jpg" },
"attrs": { "tags": [], "owner": "User::jane" },
"parents": [{ "type": "Album", "id": "jane_vacation" }]
},
{
"uid": { "type": "Photo", "id": "JanesVacationPrivate.jpg" },
"attrs": { "tags": ["private"], "owner": "User::jane" },
"parents": [{ "type": "Album", "id": "jane_vacation" }]
},
{
"uid": { "type": "Album", "id": "jane_vacation" },
"attrs": { "tags": [], "owner": "User::jane" },
"parents": []
}
]`)

var entityMap cedar.EntityMap
if err := entityMap.UnmarshalJSON(jsonEntities); err != nil {
fmt.Println("Entity map unmarshal error:", err)
return cedar.EntityMap{}
}
return entityMap
}

// buildRequests constructs a slice of Requests to pass to the Authorize method for evaluation. Each request is designed to test different policies and outcomes:
// 1. Alice viewing JanesVacation.jpg (should be allowed by policy0).
// 2. Alice viewing JanesVacationPrivate.jpg (should be denied by policy2).
// 3. Alice viewing John's vacation album (should be denied due to an error of the resource missing from the entity map and therefore unable to fulfil policy2)
// 4. John viewing JanesVacation.jpg (should be denied because no policy applies).
func buildRequests() []cedar.Request {

makeRequest := func(principalType, actionType, resourceType cedar.EntityType, principalID, actionID, resourceID cedar.String) cedar.Request {
return cedar.Request{
Principal: cedar.NewEntityUID(principalType, principalID),
Action: cedar.NewEntityUID(actionType, actionID),
Resource: cedar.NewEntityUID(resourceType, resourceID),
}
}

return []cedar.Request{
makeRequest("User", "Action", "Photo", "alice", "view", "JanesVacation.jpg"),
makeRequest("User", "Action", "Photo", "alice", "view", "JanesVacationPrivate.jpg"),
makeRequest("User", "Action", "Album", "alice", "view", "john_vacation"),
makeRequest("User", "Action", "Photo", "john", "view", "JanesVacation.jpg"),
}
}

// outputDecision is a helper function that takes an authorization decision and
// its corresponding diagnostic information, and prints the decision along with
// the reasons and any errors encountered during evaluation.
func outputDecision(decision cedar.Decision, diag cedar.Diagnostic) {
fmt.Println("Decision:", decision.String())

// If there are errors in the diagnostic, print them and return.
if len(diag.Errors) > 0 {
fmt.Println("Errors encountered during evaluation:")
for _, err := range diag.Errors {
fmt.Println(" *", err)
fmt.Println()
}
}

// If there are no reasons, then no policies applied to the request.
if len(diag.Reasons) == 0 {
fmt.Println("No applicable policies found.")
fmt.Println()
return
}

if decision == cedar.Allow {
fmt.Println("Permitted by:")
} else {
fmt.Println("Denied by:")
}
// The reasons will contain the policies that contributed to the final decision.
for _, d := range diag.Reasons {
fmt.Println(" *", d.PolicyID)
}
fmt.Println()
}
Loading