summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/authz/cedar.go18
-rw-r--r--pkg/gid/gid.go20
-rw-r--r--pkg/policies/album.cedar6
-rw-r--r--pkg/policies/entities.json9
-rw-r--r--pkg/policies/init.go3
-rw-r--r--pkg/policies/organization.cedar6
-rw-r--r--pkg/policies/policies_test.go59
-rw-r--r--pkg/policies/rest.cedar51
-rw-r--r--pkg/rpc/ability_service.go7
-rw-r--r--pkg/rpc/server_test.go10
10 files changed, 127 insertions, 62 deletions
diff --git a/pkg/authz/cedar.go b/pkg/authz/cedar.go
index 7a92f8e4..80bb2a3a 100644
--- a/pkg/authz/cedar.go
+++ b/pkg/authz/cedar.go
@@ -1,25 +1,33 @@
package authz
import (
+ "net"
"net/http"
cedar "github.com/cedar-policy/cedar-go"
+ "gitlab.com/mokhax/spike/pkg/gid"
+ xlog "gitlab.com/mokhax/spike/pkg/log"
"gitlab.com/mokhax/spike/pkg/policies"
)
func WithCedar() Authorizer {
return AuthorizerFunc(func(r *http.Request) bool {
+ host, _, err := net.SplitHostPort(r.Host)
+ if err != nil {
+ xlog.WithFields(r, xlog.Fields{"error": err})
+ return false
+ }
subject, found := TokenFrom(r).Subject()
if !found {
- subject = "*"
+ subject = "gid://User/*"
}
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)),
+ Principal: gid.NewEntityUID(subject),
+ Action: cedar.NewEntityUID("HttpMethod", cedar.String(r.Method)),
+ Resource: cedar.NewEntityUID("HttpPath", cedar.String(r.URL.Path)),
Context: cedar.NewRecord(cedar.RecordMap{
- "Host": cedar.String(r.Host),
+ "host": cedar.String(host),
}),
})
})
diff --git a/pkg/gid/gid.go b/pkg/gid/gid.go
new file mode 100644
index 00000000..e82b073e
--- /dev/null
+++ b/pkg/gid/gid.go
@@ -0,0 +1,20 @@
+package gid
+
+import (
+ "net/url"
+ "strings"
+
+ "github.com/cedar-policy/cedar-go"
+)
+
+func NewEntityUID(globalID string) cedar.EntityUID {
+ url, err := url.Parse(globalID)
+ if err != nil {
+ return cedar.NewEntityUID("User", cedar.String(globalID))
+ }
+
+ return cedar.NewEntityUID(
+ cedar.EntityType(url.Hostname()),
+ cedar.String(strings.TrimPrefix(url.Path, "/")),
+ )
+}
diff --git a/pkg/policies/album.cedar b/pkg/policies/album.cedar
index 6ba3cbdf..aed5a53e 100644
--- a/pkg/policies/album.cedar
+++ b/pkg/policies/album.cedar
@@ -1,5 +1,5 @@
permit (
- principal == User::"alice",
- action == Action::"view",
- resource in Album::"jane_vacation"
+ principal == User::"alice",
+ action == Permission::"view",
+ resource in Album::"jane_vacation"
);
diff --git a/pkg/policies/entities.json b/pkg/policies/entities.json
index cfdc0996..3df6e43e 100644
--- a/pkg/policies/entities.json
+++ b/pkg/policies/entities.json
@@ -26,7 +26,8 @@
"uid": {
"type": "User",
"id": "1"
- }
+ },
+ "parents": []
},
{
"uid": {
@@ -301,5 +302,11 @@
"id": "4"
}
]
+ },
+ {
+ "uid": {
+ "type": "HttpPath",
+ "id": "/projects.json"
+ }
}
]
diff --git a/pkg/policies/init.go b/pkg/policies/init.go
index cabfbecc..a10526f7 100644
--- a/pkg/policies/init.go
+++ b/pkg/policies/init.go
@@ -5,7 +5,6 @@ import (
_ "embed"
"fmt"
"io/fs"
- "log"
"strings"
"github.com/cedar-policy/cedar-go"
@@ -57,7 +56,7 @@ func init() {
})
if err != nil {
- log.Fatal(err)
+ xlog.Default.Printf("error: %v\n", err)
}
}
diff --git a/pkg/policies/organization.cedar b/pkg/policies/organization.cedar
index 22e4b6aa..8ac143c1 100644
--- a/pkg/policies/organization.cedar
+++ b/pkg/policies/organization.cedar
@@ -1,5 +1,5 @@
permit (
- principal == User::"1",
- action == Action::"read",
- resource in Organization::"1"
+ principal == User::"1",
+ action == Permission::"read",
+ resource in Organization::"2"
);
diff --git a/pkg/policies/policies_test.go b/pkg/policies/policies_test.go
new file mode 100644
index 00000000..e038edbe
--- /dev/null
+++ b/pkg/policies/policies_test.go
@@ -0,0 +1,59 @@
+package policies
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/cedar-policy/cedar-go"
+ "github.com/stretchr/testify/assert"
+ "gitlab.com/mokhax/spike/pkg/gid"
+)
+
+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)
+ }
+ 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")) }),
+ }
+
+ 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) {
+ assert.True(t, Allowed(*tt))
+ })
+ }
+
+ denied := []*cedar.Request{
+ build(func(r *cedar.Request) {
+ r.Principal = gid.NewEntityUID("gid://User/*")
+ 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")})
+ }),
+ build(func(r *cedar.Request) {
+ 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) {
+ assert.False(t, Allowed(*tt))
+ })
+ }
+}
diff --git a/pkg/policies/rest.cedar b/pkg/policies/rest.cedar
index a8896849..c6c4f745 100644
--- a/pkg/policies/rest.cedar
+++ b/pkg/policies/rest.cedar
@@ -1,41 +1,12 @@
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"
-);
+ principal == User::"1",
+ action in [
+ HttpMethod::"GET",
+ HttpMethod::"POST",
+ HttpMethod::"PUT",
+ HttpMethod::"PATCH",
+ HttpMethod::"DELETE",
+ HttpMethod::"HEAD"
+ ],
+ resource
+) when { context.host == "api.example.com" };
diff --git a/pkg/rpc/ability_service.go b/pkg/rpc/ability_service.go
index bf299da9..18327d52 100644
--- a/pkg/rpc/ability_service.go
+++ b/pkg/rpc/ability_service.go
@@ -4,6 +4,7 @@ import (
context "context"
"github.com/cedar-policy/cedar-go"
+ "gitlab.com/mokhax/spike/pkg/gid"
"gitlab.com/mokhax/spike/pkg/policies"
)
@@ -17,9 +18,9 @@ func NewAbilityService() *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)),
+ Principal: gid.NewEntityUID(req.Subject),
+ Action: cedar.NewEntityUID("Permission", cedar.String(req.Permission)),
+ Resource: gid.NewEntityUID(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 0871173b..66a177d5 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 false", func(t *testing.T) {
+ t.Run("forbids", func(t *testing.T) {
reply, err := client.Allowed(t.Context(), &AllowRequest{
Subject: "",
Permission: "",
@@ -41,17 +41,17 @@ func TestServer(t *testing.T) {
assert.False(t, reply.Result)
})
- t.Run("returns true for alice:view:jane_vacation", func(t *testing.T) {
+ t.Run("allows alice:view:jane_vacation", func(t *testing.T) {
reply, err := client.Allowed(t.Context(), &AllowRequest{
- Subject: "alice",
+ Subject: "gid://User/alice",
Permission: "view",
- Resource: "jane_vacation",
+ Resource: "gid://Album/jane_vacation",
})
require.NoError(t, err)
assert.True(t, reply.Result)
})
- t.Run("returns gid://User/1:read:gid://Organization/2", func(t *testing.T) {
+ t.Run("allows gid://User/1 read gid://Organization/2", func(t *testing.T) {
reply, err := client.Allowed(t.Context(), &AllowRequest{
Subject: "gid://User/1",
Permission: "read",