Commit 96f7120

mo khan <mo@mokhan.ca>
2025-04-03 00:13:52
refactor: update gtwy cedar policy
1 parent d8bb191
Changed files (3)
pkg/gid/gid.go
@@ -30,3 +30,7 @@ func NewEntityUID(globalID string) cedar.EntityUID {
 func DefaultEntityUID(id string) cedar.EntityUID {
 	return cedar.NewEntityUID("User", cedar.String(id))
 }
+
+func ZeroEntityUID() cedar.EntityUID {
+	return cedar.NewEntityUID("", "")
+}
pkg/policies/gtwy.cedar
@@ -1,16 +1,12 @@
-permit (
-  principal == User::"1",
+permit(
+  principal is User,
   action in [
+    HttpMethod::"DELETE",
     HttpMethod::"GET",
-    HttpMethod::"POST",
-    HttpMethod::"PUT",
+    HttpMethod::"HEAD",
     HttpMethod::"PATCH",
-    HttpMethod::"DELETE",
-    HttpMethod::"HEAD"
+    HttpMethod::"POST",
+    HttpMethod::"PUT"
   ],
   resource
-) when {
-  context.host == "api.example.com" ||
-  context.host == "idp.example.com" ||
-  context.host == "ui.example.com"
-};
+);
pkg/policies/policies_test.go
@@ -11,80 +11,135 @@ import (
 
 func build(f func(*cedar.Request)) *cedar.Request {
 	request := &cedar.Request{
-		Principal: gid.NewEntityUID("gid://User/1"),
-		Action:    cedar.NewEntityUID("HttpMethod", cedar.String("GET")),
-		Resource:  cedar.NewEntityUID("HttpPath", cedar.String("/projects.json")),
-		Context:   cedar.NewRecord(cedar.RecordMap{"host": cedar.String("api.example.com")}),
-	}
-	if f != nil {
-		f(request)
+		Principal: gid.NewEntityUID("gid://example/User/1"),
+		Action:    cedar.NewEntityUID("HttpMethod", "GET"),
+		Resource:  cedar.NewEntityUID("HttpPath", "/"),
+		Context: cedar.NewRecord(cedar.RecordMap{
+			"host": cedar.String("idp.example.com"),
+		}),
 	}
+	f(request)
 	return request
 }
 
 func TestAllowed(t *testing.T) {
 	allowed := []*cedar.Request{
 		build(func(r *cedar.Request) {}),
-		build(func(r *cedar.Request) { r.Action = cedar.NewEntityUID("HttpMethod", cedar.String("POST")) }),
-		build(func(r *cedar.Request) { r.Action = cedar.NewEntityUID("HttpMethod", cedar.String("PUT")) }),
-		build(func(r *cedar.Request) { r.Action = cedar.NewEntityUID("HttpMethod", cedar.String("PATCH")) }),
-		build(func(r *cedar.Request) { r.Action = cedar.NewEntityUID("HttpMethod", cedar.String("DELETE")) }),
-		build(func(r *cedar.Request) { r.Action = cedar.NewEntityUID("HttpMethod", cedar.String("HEAD")) }),
 		build(func(r *cedar.Request) {
-			r.Resource = cedar.NewEntityUID("HttpPath", cedar.String("/organizations.json"))
+			r.Principal = gid.NewEntityUID("gid://example/User/1")
+			r.Action = cedar.NewEntityUID("HttpMethod", "POST")
 		}),
-		build(func(r *cedar.Request) { r.Resource = cedar.NewEntityUID("HttpPath", cedar.String("/groups.json")) }),
 		build(func(r *cedar.Request) {
-			r.Resource = cedar.NewEntityUID("HttpPath", cedar.String("/.well-known/openid-configuration"))
-			r.Context = cedar.NewRecord(cedar.RecordMap{"host": cedar.String("idp.example.com")})
+			r.Principal = gid.NewEntityUID("gid://example/User/1")
+			r.Action = cedar.NewEntityUID("HttpMethod", "PUT")
 		}),
 		build(func(r *cedar.Request) {
-			r.Resource = cedar.NewEntityUID("HttpPath", cedar.String("/.well-known/oauth-authorization-server"))
-			r.Context = cedar.NewRecord(cedar.RecordMap{"host": cedar.String("idp.example.com")})
+			r.Principal = gid.NewEntityUID("gid://example/User/1")
+			r.Action = cedar.NewEntityUID("HttpMethod", "PATCH")
 		}),
-		// build(func(r *cedar.Request) {
-		// 	r.Principal = gid.NewEntityUID("gid://User/*")
-		// 	r.Resource = cedar.NewEntityUID("HttpPath", cedar.String("/.well-known/openid-configuration"))
-		// 	r.Context = cedar.NewRecord(cedar.RecordMap{"host": cedar.String("idp.example.com")})
-		// }),
-		// build(func(r *cedar.Request) {
-		// 	r.Principal = gid.NewEntityUID("gid://User/*")
-		// 	r.Resource = cedar.NewEntityUID("HttpPath", cedar.String("/.well-known/oauth-authorization-server"))
-		// 	r.Context = cedar.NewRecord(cedar.RecordMap{"host": cedar.String("idp.example.com")})
-		// }),
 		build(func(r *cedar.Request) {
-			r.Action = cedar.NewEntityUID("HttpMethod", cedar.String("POST"))
-			r.Resource = cedar.NewEntityUID("HttpPath", cedar.String("/twirp/authx.rpc.Ability/Allowed"))
-			r.Context = cedar.NewRecord(cedar.RecordMap{"host": cedar.String("idp.example.com")})
+			r.Principal = gid.NewEntityUID("gid://example/User/1")
+			r.Action = cedar.NewEntityUID("HttpMethod", "DELETE")
+		}),
+		build(func(r *cedar.Request) {
+			r.Principal = gid.NewEntityUID("gid://example/User/1")
+			r.Action = cedar.NewEntityUID("HttpMethod", "HEAD")
+		}),
+		build(func(r *cedar.Request) {
+			r.Principal = gid.NewEntityUID("gid://example/User/1")
+			r.Resource = cedar.NewEntityUID("HttpPath", "/organizations.json")
+			r.Context = cedar.NewRecord(cedar.RecordMap{
+				"host": cedar.String("api.example.com"),
+			})
+		}),
+		build(func(r *cedar.Request) {
+			r.Principal = gid.NewEntityUID("gid://example/User/1")
+			r.Resource = cedar.NewEntityUID("HttpPath", "/groups.json")
+			r.Context = cedar.NewRecord(cedar.RecordMap{
+				"host": cedar.String("api.example.com"),
+			})
+		}),
+		build(func(r *cedar.Request) {
+			r.Principal = gid.NewEntityUID("gid://example/User/1")
+			r.Resource = cedar.NewEntityUID("HttpPath", "/.well-known/openid-configuration")
+			r.Context = cedar.NewRecord(cedar.RecordMap{
+				"host": cedar.String("idp.example.com"),
+			})
+		}),
+		build(func(r *cedar.Request) {
+			r.Principal = gid.NewEntityUID("gid://example/User/1")
+			r.Resource = cedar.NewEntityUID("HttpPath", "/.well-known/oauth-authorization-server")
+			r.Context = cedar.NewRecord(cedar.RecordMap{
+				"host": cedar.String("idp.example.com"),
+			})
 		}),
 		build(func(r *cedar.Request) {
-			r.Action = cedar.NewEntityUID("HttpMethod", cedar.String("GET"))
-			r.Resource = cedar.NewEntityUID("HttpPath", cedar.String("/index.html"))
-			r.Context = cedar.NewRecord(cedar.RecordMap{"host": cedar.String("ui.example.com")})
+			r.Principal = gid.NewEntityUID("gid://example/User/*")
+			r.Resource = cedar.NewEntityUID("HttpPath", "/.well-known/openid-configuration")
+			r.Context = cedar.NewRecord(cedar.RecordMap{
+				"host": cedar.String("idp.example.com"),
+			})
+		}),
+		build(func(r *cedar.Request) {
+			r.Principal = gid.NewEntityUID("gid://example/User/*")
+			r.Resource = cedar.NewEntityUID("HttpPath", "/.well-known/oauth-authorization-server")
+			r.Context = cedar.NewRecord(cedar.RecordMap{
+				"host": cedar.String("idp.example.com"),
+			})
+		}),
+		build(func(r *cedar.Request) {
+			r.Principal = gid.NewEntityUID("gid://example/User/1")
+			r.Action = cedar.NewEntityUID("HttpMethod", "POST")
+			r.Resource = cedar.NewEntityUID("HttpPath", "/twirp/authx.rpc.Ability/Allowed")
+			r.Context = cedar.NewRecord(cedar.RecordMap{
+				"host": cedar.String("idp.example.com"),
+			})
+		}),
+		build(func(r *cedar.Request) {
+			r.Principal = gid.NewEntityUID("gid://example/User/1")
+			r.Action = cedar.NewEntityUID("HttpMethod", "GET")
+			r.Resource = cedar.NewEntityUID("HttpPath", "/index.html")
+			r.Context = cedar.NewRecord(cedar.RecordMap{
+				"host": cedar.String("ui.example.com"),
+			})
 		}),
 	}
 
 	for _, tt := range allowed {
-		t.Run(fmt.Sprintf("allows: %v %v %v %v", tt.Principal, tt.Action, tt.Resource, tt.Context), func(t *testing.T) {
+		t.Run(fmt.Sprintf("allows: %v/%v %v %v%v", tt.Principal.Type, tt.Principal.ID, tt.Action.ID, tt.Context.Map()["host"], tt.Resource.ID), func(t *testing.T) {
 			assert.True(t, Allowed(*tt))
 		})
 	}
 
 	denied := []*cedar.Request{
 		build(func(r *cedar.Request) {
-			r.Principal = gid.NewEntityUID("gid://User/*")
+			r.Principal = gid.ZeroEntityUID()
 			r.Action = cedar.NewEntityUID("HttpMethod", cedar.String("POST"))
 		}),
 		build(func(r *cedar.Request) {
-			r.Context = cedar.NewRecord(cedar.RecordMap{"host": cedar.String("unknown.example.com")})
+			r.Principal = gid.ZeroEntityUID()
+			r.Action = cedar.NewEntityUID("HttpMethod", cedar.String("PUT"))
+		}),
+		build(func(r *cedar.Request) {
+			r.Principal = gid.ZeroEntityUID()
+			r.Action = cedar.NewEntityUID("HttpMethod", cedar.String("PATCH"))
+		}),
+		build(func(r *cedar.Request) {
+			r.Principal = gid.ZeroEntityUID()
+			r.Action = cedar.NewEntityUID("HttpMethod", cedar.String("DELETE"))
+		}),
+		build(func(r *cedar.Request) {
+			r.Principal = gid.ZeroEntityUID()
+			r.Action = cedar.NewEntityUID("HttpMethod", cedar.String("HEAD"))
 		}),
 		build(func(r *cedar.Request) {
+			r.Principal = gid.ZeroEntityUID()
 			r.Action = cedar.NewEntityUID("HttpMethod", cedar.String("TRACE"))
 		}),
 	}
 
 	for _, tt := range denied {
-		t.Run(fmt.Sprintf("denies: %v %v %v %v", tt.Principal, tt.Action, tt.Resource, tt.Context), func(t *testing.T) {
+		t.Run(fmt.Sprintf("denies: %v/%v %v %v%v", tt.Principal.Type, tt.Principal.ID, tt.Action.ID, tt.Context.Map()["host"], tt.Resource.ID), func(t *testing.T) {
 			assert.False(t, Allowed(*tt))
 		})
 	}