From 3f54e2fc59f21029813863491b37e39bb6015115 Mon Sep 17 00:00:00 2001 From: mo khan Date: Wed, 2 Apr 2025 11:23:47 -0600 Subject: refactor: move policies and entities in policies package --- cedar.conf | 5 ----- cedar.json | 24 --------------------- pkg/authz/cedar.go | 35 ++++-------------------------- pkg/policies/entities.json | 47 ++++++++++++++++++++++++++++++++++++++++ pkg/policies/init.go | 54 +++++++++++++++++++++++----------------------- pkg/policies/rest.cedar | 41 +++++++++++++++++++++++++++++++++++ pkg/rpc/ability_handler.go | 26 ---------------------- pkg/rpc/ability_service.go | 26 ++++++++++++++++++++++ pkg/rpc/server_test.go | 12 ++++++++++- 9 files changed, 156 insertions(+), 114 deletions(-) delete mode 100644 cedar.conf delete mode 100644 cedar.json create mode 100644 pkg/policies/entities.json create mode 100644 pkg/policies/rest.cedar delete mode 100644 pkg/rpc/ability_handler.go create mode 100644 pkg/rpc/ability_service.go diff --git a/cedar.conf b/cedar.conf deleted file mode 100644 index 0d032bc..0000000 --- a/cedar.conf +++ /dev/null @@ -1,5 +0,0 @@ -permit ( - principal == Subject::"*", - action == Action::"GET", - resource in Path::"/projects.json" -); diff --git a/cedar.json b/cedar.json deleted file mode 100644 index c170a3b..0000000 --- a/cedar.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "uid": { - "type": "User", - "id": "*" - } - }, - { - "uid": { - "type": "Project", - "id": "3" - }, - "parents": [ - { - "type": "Group", - "id": "3" - }, - { - "type": "Path", - "id": "/projects.json" - } - ] - } -] diff --git a/pkg/authz/cedar.go b/pkg/authz/cedar.go index 1d95265..7a92f8e 100644 --- a/pkg/authz/cedar.go +++ b/pkg/authz/cedar.go @@ -1,53 +1,26 @@ package authz import ( - "encoding/json" - "fmt" - "net" "net/http" - "os" cedar "github.com/cedar-policy/cedar-go" - "github.com/cedar-policy/cedar-go/types" - "github.com/xlgmokha/x/pkg/x" - xlog "gitlab.com/mokhax/spike/pkg/log" + "gitlab.com/mokhax/spike/pkg/policies" ) func WithCedar() Authorizer { - var policy cedar.Policy - x.Check(policy.UnmarshalCedar(x.Must(os.ReadFile("cedar.conf")))) - - policies := cedar.NewPolicySet() - policies.Add("cedar.conf", &policy) - - var entities cedar.EntityMap - if err := json.Unmarshal(x.Must(os.ReadFile("cedar.json")), &entities); err != nil { - xlog.Logger.Error("Error", "error", err) - return nil - } - return AuthorizerFunc(func(r *http.Request) bool { - host, _, err := net.SplitHostPort(r.Host) - if err != nil { - return false - } - subject, found := TokenFrom(r).Subject() if !found { subject = "*" } - req := cedar.Request{ + return policies.Allowed(cedar.Request{ Principal: cedar.NewEntityUID("Subject", cedar.String(subject)), Action: cedar.NewEntityUID("Action", cedar.String(r.Method)), Resource: cedar.NewEntityUID("Path", cedar.String(r.URL.Path)), Context: cedar.NewRecord(cedar.RecordMap{ - "Host": cedar.String(host), + "Host": cedar.String(r.Host), }), - } - - ok, diagnostic := policies.IsAuthorized(entities, req) - fmt.Printf("%v: %v -> %v %v%v %v\n", ok, subject, r.Method, host, r.URL.Path, diagnostic.Reasons) - return ok == types.Allow + }) }) } diff --git a/pkg/policies/entities.json b/pkg/policies/entities.json new file mode 100644 index 0000000..e6d4145 --- /dev/null +++ b/pkg/policies/entities.json @@ -0,0 +1,47 @@ +[ + { + "uid": { + "type": "User", + "id": "alice" + }, + "attrs": { + "age": 18 + }, + "parents": [] + }, + { + "uid": { + "type": "Photo", + "id": "VacationPhoto94.jpg" + }, + "attrs": {}, + "parents": [ + { + "type": "Album", + "id": "jane_vacation" + } + ] + }, + { + "uid": { + "type": "User", + "id": "1" + } + }, + { + "uid": { + "type": "Project", + "id": "3" + }, + "parents": [ + { + "type": "Group", + "id": "3" + }, + { + "type": "Path", + "id": "/projects.json" + } + ] + } +] diff --git a/pkg/policies/init.go b/pkg/policies/init.go index d455cb8..cabfbec 100644 --- a/pkg/policies/init.go +++ b/pkg/policies/init.go @@ -3,33 +3,21 @@ package policies import ( "embed" _ "embed" - "encoding/json" + "fmt" "io/fs" "log" + "strings" "github.com/cedar-policy/cedar-go" "github.com/cedar-policy/cedar-go/types" - "github.com/xlgmokha/x/pkg/x" xlog "gitlab.com/mokhax/spike/pkg/log" ) -//go:embed *.cedar +//go:embed *.cedar *.json 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" }] - } -]` +var Entities cedar.EntityMap = cedar.EntityMap{} func init() { err := fs.WalkDir(files, ".", func(path string, d fs.DirEntry, err error) error { @@ -41,17 +29,30 @@ func init() { return nil } - content, err := fs.ReadFile(files, path) - if err != nil { - return err - } + if strings.HasSuffix(path, ".cedar") { + content, err := fs.ReadFile(files, path) + if err != nil { + return err + } - var policy cedar.Policy - if err := policy.UnmarshalCedar(content); err != nil { - return err + policy := cedar.Policy{} + if err := policy.UnmarshalCedar(content); err != nil { + return err + } + policy.SetFilename(path) + + All.Add(cedar.PolicyID(path), &policy) + } else if strings.HasSuffix(path, ".json") { + content, err := fs.ReadFile(files, path) + if err != nil { + return err + } + + if err := Entities.UnmarshalJSON(content); err != nil { + return err + } } - All.Add(cedar.PolicyID(path), &policy) return nil }) @@ -61,10 +62,9 @@ func init() { } func Allowed(request cedar.Request) bool { - var entities cedar.EntityMap - x.Check(json.Unmarshal([]byte(entitiesJSON), &entities)) + ok, diagnostic := All.IsAuthorized(Entities, request) + fmt.Printf("%v: %v -> %v %v%v\n", ok, request.Principal, request.Action, request.Context.Map(), request.Resource) - ok, diagnostic := All.IsAuthorized(entities, request) if len(diagnostic.Errors) > 0 { for err := range diagnostic.Errors { xlog.Default.Printf("%v\n", err) diff --git a/pkg/policies/rest.cedar b/pkg/policies/rest.cedar new file mode 100644 index 0000000..a889684 --- /dev/null +++ b/pkg/policies/rest.cedar @@ -0,0 +1,41 @@ +permit ( + principal == Subject::"*", + action == Action::"GET", + resource in Path::"/projects.json" +); + +permit ( + principal == Subject::"gid://User/1", + action == Action::"GET", + resource in Path::"/*.json" +); + +permit ( + principal == Subject::"gid://User/1", + action == Action::"POST", + resource in Path::"/*.json" +); + +permit ( + principal == Subject::"gid://User/1", + action == Action::"PUT", + resource in Path::"/*.json" +); + +permit ( + principal == Subject::"gid://User/1", + action == Action::"PATCH", + resource in Path::"/*.json" +); + +permit ( + principal == Subject::"gid://User/1", + action == Action::"DELETE", + resource in Path::"/*.json" +); + +permit ( + principal == Subject::"gid://User/1", + action == Action::"HEAD", + resource in Path::"/*.json" +); diff --git a/pkg/rpc/ability_handler.go b/pkg/rpc/ability_handler.go deleted file mode 100644 index bf299da..0000000 --- a/pkg/rpc/ability_handler.go +++ /dev/null @@ -1,26 +0,0 @@ -package rpc - -import ( - context "context" - - "github.com/cedar-policy/cedar-go" - "gitlab.com/mokhax/spike/pkg/policies" -) - -type AbilityService struct { - UnimplementedAbilityServer -} - -func NewAbilityService() *AbilityService { - return &AbilityService{} -} - -func (h *AbilityService) Allowed(ctx context.Context, req *AllowRequest) (*AllowReply, error) { - 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/ability_service.go b/pkg/rpc/ability_service.go new file mode 100644 index 0000000..bf299da --- /dev/null +++ b/pkg/rpc/ability_service.go @@ -0,0 +1,26 @@ +package rpc + +import ( + context "context" + + "github.com/cedar-policy/cedar-go" + "gitlab.com/mokhax/spike/pkg/policies" +) + +type AbilityService struct { + UnimplementedAbilityServer +} + +func NewAbilityService() *AbilityService { + return &AbilityService{} +} + +func (h *AbilityService) Allowed(ctx context.Context, req *AllowRequest) (*AllowReply, error) { + 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_test.go b/pkg/rpc/server_test.go index 266f143..8f333a5 100644 --- a/pkg/rpc/server_test.go +++ b/pkg/rpc/server_test.go @@ -41,7 +41,7 @@ func TestServer(t *testing.T) { assert.False(t, reply.Result) }) - t.Run("returns true", func(t *testing.T) { + t.Run("returns true for alice:view:jane_vacation", func(t *testing.T) { reply, err := client.Allowed(t.Context(), &AllowRequest{ Subject: "alice", Permission: "view", @@ -50,4 +50,14 @@ func TestServer(t *testing.T) { require.NoError(t, err) assert.True(t, reply.Result) }) + + t.Run("returns gid://User/1:read_projects:gid://Organization/1", func(t *testing.T) { + reply, err := client.Allowed(t.Context(), &AllowRequest{ + Subject: "gid://User/1", + Permission: "read_projects", + Resource: "gid://Organization/1", + }) + require.NoError(t, err) + assert.True(t, reply.Result) + }) } -- cgit v1.2.3