From c75ceda92ce98c654747457c4fdfd32766487653 Mon Sep 17 00:00:00 2001 From: mo khan Date: Wed, 2 Apr 2025 09:45:37 -0600 Subject: feat: embed cedar policies in policies package --- pkg/policies/init.go | 72 ++++++++++++++++++++++++++++++++++++++++++++++ pkg/policies/project.cedar | 5 ++++ pkg/rpc/ability_handler.go | 17 +++++++++-- pkg/rpc/server.go | 2 +- pkg/rpc/server_test.go | 12 +++++++- test/e2e_test.go | 4 +++ 6 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 pkg/policies/init.go create mode 100644 pkg/policies/project.cedar diff --git a/pkg/policies/init.go b/pkg/policies/init.go new file mode 100644 index 00000000..9916b119 --- /dev/null +++ b/pkg/policies/init.go @@ -0,0 +1,72 @@ +package policies + +import ( + "embed" + _ "embed" + "encoding/json" + "fmt" + "io/fs" + "log" + + "github.com/cedar-policy/cedar-go" + "github.com/cedar-policy/cedar-go/types" + "github.com/xlgmokha/x/pkg/x" +) + +//go:embed *.cedar +var files embed.FS + +var All *cedar.PolicySet = cedar.NewPolicySet() + +const entitiesJSON = `[ + { + "uid": { "type": "User", "id": "alice" }, + "attrs": { "age": 18 }, + "parents": [] + }, + { + "uid": { "type": "Photo", "id": "VacationPhoto94.jpg" }, + "attrs": {}, + "parents": [{ "type": "Album", "id": "jane_vacation" }] + } +]` + +func init() { + err := fs.WalkDir(files, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + return nil + } + + content, err := fs.ReadFile(files, path) + if err != nil { + return err + } + + var policy cedar.Policy + if err := policy.UnmarshalCedar(content); err != nil { + return err + } + + All.Add(cedar.PolicyID(path), &policy) + return nil + }) + + if err != nil { + log.Fatal(err) + } +} + +func Allowed(request cedar.Request) bool { + var entities cedar.EntityMap + x.Check(json.Unmarshal([]byte(entitiesJSON), &entities)) + + ok, diagnostic := All.IsAuthorized(entities, request) + if len(diagnostic.Errors) > 0 || len(diagnostic.Reasons) > 0 { + fmt.Printf("%v %v\n", diagnostic.Errors, diagnostic.Reasons) + } + return ok == types.Allow +} diff --git a/pkg/policies/project.cedar b/pkg/policies/project.cedar new file mode 100644 index 00000000..6ba3cbdf --- /dev/null +++ b/pkg/policies/project.cedar @@ -0,0 +1,5 @@ +permit ( + principal == User::"alice", + action == Action::"view", + resource in Album::"jane_vacation" +); diff --git a/pkg/rpc/ability_handler.go b/pkg/rpc/ability_handler.go index b36ce14e..973e1db8 100644 --- a/pkg/rpc/ability_handler.go +++ b/pkg/rpc/ability_handler.go @@ -2,14 +2,25 @@ package rpc import ( context "context" + + "github.com/cedar-policy/cedar-go" + "gitlab.com/mokhax/spike/pkg/policies" ) type AbilityHandler struct { UnimplementedAbilityServer } +func NewAbilityHandler() *AbilityHandler { + return &AbilityHandler{} +} + func (h *AbilityHandler) Allowed(ctx context.Context, req *AllowRequest) (*AllowReply, error) { - return &AllowReply{ - Result: false, - }, nil + ok := policies.Allowed(cedar.Request{ + Principal: cedar.NewEntityUID("User", cedar.String(req.Subject)), + Action: cedar.NewEntityUID("Action", cedar.String(req.Permission)), + Resource: cedar.NewEntityUID("Album", cedar.String(req.Resource)), + Context: cedar.NewRecord(cedar.RecordMap{}), + }) + return &AllowReply{Result: ok}, nil } diff --git a/pkg/rpc/server.go b/pkg/rpc/server.go index c78b5d42..90bfdaf9 100644 --- a/pkg/rpc/server.go +++ b/pkg/rpc/server.go @@ -6,6 +6,6 @@ import ( func New(options ...grpc.ServerOption) *grpc.Server { server := grpc.NewServer(options...) - RegisterAbilityServer(server, &AbilityHandler{}) + RegisterAbilityServer(server, NewAbilityHandler()) return server } diff --git a/pkg/rpc/server_test.go b/pkg/rpc/server_test.go index 0ae0f013..266f1434 100644 --- a/pkg/rpc/server_test.go +++ b/pkg/rpc/server_test.go @@ -31,7 +31,7 @@ func TestServer(t *testing.T) { defer connection.Close() client := NewAbilityClient(connection) - t.Run("returns a result", func(t *testing.T) { + t.Run("returns false", func(t *testing.T) { reply, err := client.Allowed(t.Context(), &AllowRequest{ Subject: "", Permission: "", @@ -40,4 +40,14 @@ func TestServer(t *testing.T) { require.NoError(t, err) assert.False(t, reply.Result) }) + + t.Run("returns true", func(t *testing.T) { + reply, err := client.Allowed(t.Context(), &AllowRequest{ + Subject: "alice", + Permission: "view", + Resource: "jane_vacation", + }) + require.NoError(t, err) + assert.True(t, reply.Result) + }) } diff --git a/test/e2e_test.go b/test/e2e_test.go index 3a02bd2f..d51dece8 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -20,6 +20,10 @@ import ( ) func TestAuthx(t *testing.T) { + if env.Fetch("SKIP_E2E", "") != "" { + t.Skip() + } + _ = playwright.Install() pw := x.Must(playwright.Run()) -- cgit v1.2.3