summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/controllers/sparkles/controller.go3
-rw-r--r--app/controllers/sparkles/controller_test.go77
-rw-r--r--app/domain/user.go3
-rw-r--r--app/init.go25
-rw-r--r--app/middleware/require_permission.go2
-rw-r--r--app/middleware/require_permission_test.go32
6 files changed, 62 insertions, 80 deletions
diff --git a/app/controllers/sparkles/controller.go b/app/controllers/sparkles/controller.go
index 7e6975c..80e95cb 100644
--- a/app/controllers/sparkles/controller.go
+++ b/app/controllers/sparkles/controller.go
@@ -9,6 +9,7 @@ import (
"github.com/xlgmokha/x/pkg/x"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/domain"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/middleware"
+ "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/authz"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/pls"
)
@@ -16,7 +17,7 @@ type Controller struct {
db domain.Repository[*domain.Sparkle]
}
-func New(db domain.Repository[*domain.Sparkle]) *Controller {
+func New(db domain.Repository[*domain.Sparkle], check authz.PermissionService) *Controller {
return &Controller{db: db}
}
diff --git a/app/controllers/sparkles/controller_test.go b/app/controllers/sparkles/controller_test.go
index 37e1a82..006c3fa 100644
--- a/app/controllers/sparkles/controller_test.go
+++ b/app/controllers/sparkles/controller_test.go
@@ -13,6 +13,7 @@ import (
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/cfg"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/db"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/domain"
+ "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/internal/stub"
)
type FailingResponseWriter struct {
@@ -38,7 +39,7 @@ func TestSparkles(t *testing.T) {
store.Save(t.Context(), sparkle)
mux := http.NewServeMux()
- controller := New(store)
+ controller := New(store, stub.AllowWith(t, "user:*", "read", "sparkle:*"))
controller.MountTo(mux)
t.Run("returns JSON", func(t *testing.T) {
@@ -64,47 +65,49 @@ func TestSparkles(t *testing.T) {
t.Run("when a user is logged in", func(t *testing.T) {
currentUser := domain.NewUser(domain.WithID[*domain.User](domain.ID("1")))
- t.Run("saves a new sparkle", func(t *testing.T) {
- repository := db.NewRepository[*domain.Sparkle]()
- mux := http.NewServeMux()
- controller := New(repository)
- controller.MountTo(mux)
-
- sparkle, _ := domain.NewSparkle("@tanuki for reviewing my code!")
- request, response := test.RequestResponse(
- "POST",
- "/sparkles",
- test.WithAcceptHeader(serde.JSON),
- test.WithContentType(sparkle, serde.JSON),
- test.WithContextKeyValue(t.Context(), cfg.CurrentUser, currentUser),
- )
-
- mux.ServeHTTP(response, request)
-
- require.Equal(t, http.StatusCreated, response.Code)
-
- t.Run("returns a JSON representation of the sparkle", func(t *testing.T) {
- item, err := serde.FromJSON[*domain.Sparkle](response.Body)
- require.NoError(t, err)
-
- assert.NotEmpty(t, item.ID)
- assert.Equal(t, "@tanuki", item.Sparklee)
- assert.Equal(t, "for reviewing my code!", item.Reason)
- })
-
- t.Run("saves the sparkle to the db", func(t *testing.T) {
- assert.Equal(t, 1, len(repository.All(t.Context())))
- item := repository.All(t.Context())[0]
-
- assert.Equal(t, "@tanuki", item.Sparklee)
- assert.Equal(t, "for reviewing my code!", item.Reason)
- assert.Equal(t, currentUser, item.Author)
+ t.Run("when the user is authorized to create sparkles", func(t *testing.T) {
+ t.Run("saves a new sparkle", func(t *testing.T) {
+ repository := db.NewRepository[*domain.Sparkle]()
+ mux := http.NewServeMux()
+ controller := New(repository, stub.AllowWith(t, "user:1", "create", "sparkle:*"))
+ controller.MountTo(mux)
+
+ sparkle, _ := domain.NewSparkle("@tanuki for reviewing my code!")
+ request, response := test.RequestResponse(
+ "POST",
+ "/sparkles",
+ test.WithAcceptHeader(serde.JSON),
+ test.WithContentType(sparkle, serde.JSON),
+ test.WithContextKeyValue(t.Context(), cfg.CurrentUser, currentUser),
+ )
+
+ mux.ServeHTTP(response, request)
+
+ require.Equal(t, http.StatusCreated, response.Code)
+
+ t.Run("returns a JSON representation of the sparkle", func(t *testing.T) {
+ item, err := serde.FromJSON[*domain.Sparkle](response.Body)
+ require.NoError(t, err)
+
+ assert.NotEmpty(t, item.ID)
+ assert.Equal(t, "@tanuki", item.Sparklee)
+ assert.Equal(t, "for reviewing my code!", item.Reason)
+ })
+
+ t.Run("saves the sparkle to the db", func(t *testing.T) {
+ assert.Equal(t, 1, len(repository.All(t.Context())))
+ item := repository.All(t.Context())[0]
+
+ assert.Equal(t, "@tanuki", item.Sparklee)
+ assert.Equal(t, "for reviewing my code!", item.Reason)
+ assert.Equal(t, currentUser, item.Author)
+ })
})
})
t.Run("prevents double WriteHeader when serialization fails", func(t *testing.T) {
repository := db.NewRepository[*domain.Sparkle]()
- controller := New(repository)
+ controller := New(repository, stub.Allow())
currentUser := domain.NewUser(domain.WithID[*domain.User](domain.ID("1")))
sparkle, _ := domain.NewSparkle("@user for testing")
diff --git a/app/domain/user.go b/app/domain/user.go
index 88930c5..d59baac 100644
--- a/app/domain/user.go
+++ b/app/domain/user.go
@@ -42,9 +42,8 @@ func (self *User) ToGID() string {
}
func (self *User) ToObjectReference() *v1.ObjectReference {
- // TODO:: Username is easy for demos but will need to change to ID
return &v1.ObjectReference{
ObjectType: "user",
- ObjectId: self.Username,
+ ObjectId: self.ID.String(),
}
}
diff --git a/app/init.go b/app/init.go
index 53f9e66..2986809 100644
--- a/app/init.go
+++ b/app/init.go
@@ -23,6 +23,16 @@ func init() {
ioc.RegisterSingleton[*zerolog.Logger](ioc.Default, func() *zerolog.Logger {
return log.New(os.Stdout, log.Fields{"app": "sparkled"})
})
+ ioc.RegisterSingleton[*authzed.Client](ioc.Default, func() *authzed.Client {
+ return authz.NewSpiceDBClient(
+ context.Background(),
+ env.Fetch("ZED_ENDPOINT", ":50051"),
+ env.Fetch("ZED_TOKEN", "secret"),
+ )
+ })
+ ioc.Register[authz.PermissionService](ioc.Default, func() authz.PermissionService {
+ return ioc.MustResolve[*authzed.Client](ioc.Default)
+ })
ioc.RegisterSingleton[domain.Repository[*domain.Sparkle]](ioc.Default, func() domain.Repository[*domain.Sparkle] {
return db.NewRepository[*domain.Sparkle]()
})
@@ -33,7 +43,10 @@ func init() {
return dashboard.New()
})
ioc.Register[*sparkles.Controller](ioc.Default, func() *sparkles.Controller {
- return sparkles.New(ioc.MustResolve[domain.Repository[*domain.Sparkle]](ioc.Default))
+ return sparkles.New(
+ ioc.MustResolve[domain.Repository[*domain.Sparkle]](ioc.Default),
+ ioc.MustResolve[authz.PermissionService](ioc.Default),
+ )
})
ioc.RegisterSingleton[*http.Client](ioc.Default, func() *http.Client {
return &http.Client{
@@ -42,16 +55,6 @@ func init() {
},
}
})
- ioc.RegisterSingleton[*authzed.Client](ioc.Default, func() *authzed.Client {
- return authz.NewSpiceDBClient(
- context.Background(),
- env.Fetch("ZED_ENDPOINT", ":50051"),
- env.Fetch("ZED_TOKEN", "secret"),
- )
- })
- ioc.Register[authz.CheckPermission](ioc.Default, func() authz.CheckPermission {
- return ioc.MustResolve[*authzed.Client](ioc.Default)
- })
http.DefaultClient = ioc.MustResolve[*http.Client](ioc.Default)
diff --git a/app/middleware/require_permission.go b/app/middleware/require_permission.go
index a10a9b6..cfcae0c 100644
--- a/app/middleware/require_permission.go
+++ b/app/middleware/require_permission.go
@@ -10,7 +10,7 @@ import (
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/pls"
)
-func RequirePermission(permission Permission, client authz.CheckPermission) func(http.Handler) http.Handler {
+func RequirePermission(permission Permission, client authz.PermissionService) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := cfg.CurrentUser.From(r.Context())
diff --git a/app/middleware/require_permission_test.go b/app/middleware/require_permission_test.go
index df21b5a..b11a33c 100644
--- a/app/middleware/require_permission_test.go
+++ b/app/middleware/require_permission_test.go
@@ -1,45 +1,25 @@
package middleware
import (
- "context"
"net/http"
"testing"
- v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
"github.com/stretchr/testify/require"
"github.com/xlgmokha/x/pkg/test"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/cfg"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/domain"
- "google.golang.org/grpc"
+ "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/internal/stub"
)
-type MockAbility func(context.Context, *v1.CheckPermissionRequest) (*v1.CheckPermissionResponse, error)
-
-func (m MockAbility) CheckPermission(ctx context.Context, r *v1.CheckPermissionRequest, opts ...grpc.CallOption) (*v1.CheckPermissionResponse, error) {
- return m(ctx, r)
-}
-
func TestRequirePermission(t *testing.T) {
user := &domain.User{ID: domain.ID("1")}
ctx := cfg.CurrentUser.With(t.Context(), user)
- permission := Permission("read_sparkles")
+ permission := Permission("read")
t.Run("when the permission is granted", func(t *testing.T) {
r, w := test.RequestResponse("GET", "/sparkles", test.WithContext(ctx))
- middleware := RequirePermission(permission, MockAbility(func(ctx context.Context, r *v1.CheckPermissionRequest) (*v1.CheckPermissionResponse, error) {
- require.Equal(t, "user", r.Subject.Object.ObjectType)
- require.Equal(t, user.Username, r.Subject.Object.ObjectId)
-
- require.Equal(t, permission.String(), r.Permission)
-
- require.Equal(t, "sparkle", r.Resource.ObjectType)
- require.Equal(t, "*", r.Resource.ObjectId)
-
- return &v1.CheckPermissionResponse{
- Permissionship: v1.CheckPermissionResponse_PERMISSIONSHIP_HAS_PERMISSION,
- }, nil
- }))
+ middleware := RequirePermission(permission, stub.AllowWith(t, "user:1", permission.String(), "sparkle:*"))
server := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusTeapot)
}))
@@ -51,11 +31,7 @@ func TestRequirePermission(t *testing.T) {
t.Run("when the permission is denied", func(t *testing.T) {
r, w := test.RequestResponse("GET", "/sparkles", test.WithContext(ctx))
- middleware := RequirePermission(permission, MockAbility(func(ctx context.Context, r *v1.CheckPermissionRequest) (*v1.CheckPermissionResponse, error) {
- return &v1.CheckPermissionResponse{
- Permissionship: v1.CheckPermissionResponse_PERMISSIONSHIP_NO_PERMISSION,
- }, nil
- }))
+ middleware := RequirePermission(permission, stub.Deny())
server := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Fail(t, "unexpected call to handler")
}))