Commit fc07382

mo khan <mo@mokhan.ca>
2025-03-20 16:49:23
feat: add a cedar policy
1 parent 761d493
pkg/app/app.go
@@ -1,11 +1,16 @@
 package app
 
 import (
+	"encoding/json"
 	"fmt"
+	"log"
 	"net"
 	"net/http"
+	"os"
 
 	"github.com/casbin/casbin/v3"
+	cedar "github.com/cedar-policy/cedar-go"
+	"github.com/cedar-policy/cedar-go/types"
 	"github.com/xlgmokha/x/pkg/x"
 	"gitlab.com/mokhax/spike/pkg/authz"
 	"gitlab.com/mokhax/spike/pkg/cfg"
@@ -36,9 +41,47 @@ func WithCasbin() authz.Authorizer {
 	})
 }
 
+func WithCedar() authz.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 {
+		log.Fatal(err)
+	}
+	return authz.AuthorizerFunc(func(r *http.Request) bool {
+		host, _, err := net.SplitHostPort(r.Host)
+		if err != nil {
+			return false
+		}
+
+		subject, found := authz.TokenFrom(r).Subject()
+		if !found {
+			subject = "*"
+		}
+
+		req := 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),
+			}),
+		}
+
+		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
+	})
+}
+
 func Start(bindAddr string) error {
 	return srv.Run(cfg.New(
 		bindAddr,
+		// cfg.WithMux(authz.HTTP(WithCedar(), Routes())),
 		cfg.WithMux(authz.HTTP(WithCasbin(), Routes())),
 	))
 }
cedar.conf
@@ -0,0 +1,5 @@
+permit (
+    principal == Subject::"*",
+    action == Action::"GET",
+    resource in Path::"/projects.json"
+);
cedar.json
@@ -0,0 +1,24 @@
+[
+  {
+    "uid": {
+      "type": "User",
+      "id": "*"
+    }
+  },
+  {
+    "uid": {
+      "type": "Project",
+      "id": "3"
+    },
+    "parents": [
+      {
+        "type": "Group",
+        "id": "3"
+      },
+      {
+        "type": "Path",
+        "id": "/projects.json"
+      }
+    ]
+  }
+]
go.mod
@@ -4,6 +4,7 @@ go 1.24.0
 
 require (
 	github.com/casbin/casbin/v3 v3.0.0-beta.7
+	github.com/cedar-policy/cedar-go v1.1.1
 	github.com/lestrrat-go/jwx/v3 v3.0.0-alpha3
 	github.com/magefile/mage v1.15.0
 	github.com/playwright-community/playwright-go v0.5001.0
@@ -32,6 +33,7 @@ require (
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/segmentio/asm v1.2.0 // indirect
 	golang.org/x/crypto v0.36.0 // indirect
+	golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
 	golang.org/x/sys v0.31.0 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
go.sum
@@ -4,6 +4,8 @@ github.com/arthurnn/twirp-ruby v1.13.0 h1:j0T7I5oxe2niKFdfjiiCmkiydwYeegrbwVMs+G
 github.com/arthurnn/twirp-ruby v1.13.0/go.mod h1:1fVOQuSLzwXoPi9/ejlDYG3roilJIPAZN2sw+A3o48o=
 github.com/casbin/casbin/v3 v3.0.0-beta.7 h1:siS3e6cRtuyFlshUgJfw0wnWuK3z3U/ald0C8Jtof24=
 github.com/casbin/casbin/v3 v3.0.0-beta.7/go.mod h1:69HoI+h4yMUTydUMxT7VQh7FgGpoJsB/ZskkVGcvasQ=
+github.com/cedar-policy/cedar-go v1.1.1 h1:yXCEaYQCXC+BxbOx/8TDHQu4dfuWVLs8irGrGjqClu0=
+github.com/cedar-policy/cedar-go v1.1.1/go.mod h1:pEgiK479O5dJfzXnTguOMm+bCplzy5rEEFPGdZKPWz4=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -68,6 +70,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
 golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
 golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
+golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
+golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=