Commit 3f54e2f

mo khan <mo@mokhan.ca>
2025-04-02 17:23:47
refactor: move policies and entities in policies package
1 parent e8708d5
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
+		})
 	})
 }
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"
+      }
+    ]
+  }
+]
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)
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"
+);
pkg/rpc/ability_handler.go → pkg/rpc/ability_service.go
File renamed without changes
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)
+	})
 }
cedar.conf
@@ -1,5 +0,0 @@
-permit (
-    principal == Subject::"*",
-    action == Action::"GET",
-    resource in Path::"/projects.json"
-);
cedar.json
@@ -1,24 +0,0 @@
-[
-  {
-    "uid": {
-      "type": "User",
-      "id": "*"
-    }
-  },
-  {
-    "uid": {
-      "type": "Project",
-      "id": "3"
-    },
-    "parents": [
-      {
-        "type": "Group",
-        "id": "3"
-      },
-      {
-        "type": "Path",
-        "id": "/projects.json"
-      }
-    ]
-  }
-]