summaryrefslogtreecommitdiff
path: root/app/controllers
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-24 17:58:01 -0600
committermo khan <mo@mokhan.ca>2025-07-24 17:58:01 -0600
commit72296119fc9755774719f8f625ad03e0e0ec457a (patch)
treeed236ddee12a20fb55b7cfecf13f62d3a000dcb5 /app/controllers
parenta920a8cfe415858bb2777371a77018599ffed23f (diff)
parenteaa1bd3b8e12934aed06413d75e7482ac58d805a (diff)
Merge branch 'the-spice-must-flow' into 'main'
Add SpiceDB Authorization See merge request gitlab-org/software-supply-chain-security/authorization/sparkled!19
Diffstat (limited to 'app/controllers')
-rw-r--r--app/controllers/sparkles/controller.go13
-rw-r--r--app/controllers/sparkles/controller_test.go118
2 files changed, 80 insertions, 51 deletions
diff --git a/app/controllers/sparkles/controller.go b/app/controllers/sparkles/controller.go
index 0b86c7e..90767b2 100644
--- a/app/controllers/sparkles/controller.go
+++ b/app/controllers/sparkles/controller.go
@@ -9,15 +9,20 @@ 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"
)
type Controller struct {
- db domain.Repository[*domain.Sparkle]
+ db domain.Repository[*domain.Sparkle]
+ check authz.CheckPermissionService
}
-func New(db domain.Repository[*domain.Sparkle]) *Controller {
- return &Controller{db: db}
+func New(db domain.Repository[*domain.Sparkle], check authz.CheckPermissionService) *Controller {
+ return &Controller{
+ check: check,
+ db: db,
+ }
}
func (c *Controller) MountTo(mux *http.ServeMux) {
@@ -25,7 +30,7 @@ func (c *Controller) MountTo(mux *http.ServeMux) {
mux.Handle("POST /sparkles", x.Middleware[http.Handler](
http.HandlerFunc(c.Create),
middleware.RequireUser(),
- // middleware.RequirePermission("create_sparkle", ioc.MustResolve[rpc.Ability](ioc.Default)),
+ // middleware.RequirePermission("create", c.check),
))
// This is a temporary endpoint to restore a backup
diff --git a/app/controllers/sparkles/controller_test.go b/app/controllers/sparkles/controller_test.go
index 37e1a82..5c37a11 100644
--- a/app/controllers/sparkles/controller_test.go
+++ b/app/controllers/sparkles/controller_test.go
@@ -8,11 +8,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/xlgmokha/x/pkg/event"
"github.com/xlgmokha/x/pkg/serde"
"github.com/xlgmokha/x/pkg/test"
+ "github.com/xlgmokha/x/pkg/x"
"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 {
@@ -33,12 +36,12 @@ func (f *FailingResponseWriter) Write([]byte) (int, error) {
func TestSparkles(t *testing.T) {
t.Run("GET /sparkles", func(t *testing.T) {
- sparkle, _ := domain.NewSparkle("@tanuki for helping me")
- store := db.NewRepository[*domain.Sparkle]()
+ sparkle := x.New[*domain.Sparkle](domain.WithText("@tanuki for helping me"))
+ store := db.NewRepository[*domain.Sparkle](event.New[*domain.Sparkle]())
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) {
@@ -61,63 +64,84 @@ func TestSparkles(t *testing.T) {
})
t.Run("POST /sparkles", func(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("when a user is authenticated", func(t *testing.T) {
+ currentUser := x.New[*domain.User](domain.WithID[*domain.User](domain.ID("1")))
+ repository := db.NewRepository[*domain.Sparkle](event.New[*domain.Sparkle]())
- t.Run("saves a new sparkle", func(t *testing.T) {
- repository := db.NewRepository[*domain.Sparkle]()
+ t.Run("when the user is authorized", func(t *testing.T) {
mux := http.NewServeMux()
- controller := New(repository)
+ 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 a new sparkle", func(t *testing.T) {
+ sparkle := x.New[*domain.Sparkle](domain.WithText("@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("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]
+ t.Run("prevents double WriteHeader when serialization fails", func(t *testing.T) {
+ currentUser := x.New[*domain.User](domain.WithID[*domain.User](domain.ID("1")))
+ sparkle := x.New[*domain.Sparkle](domain.WithText("@user for testing"))
- assert.Equal(t, "@tanuki", item.Sparklee)
- assert.Equal(t, "for reviewing my code!", item.Reason)
- assert.Equal(t, currentUser, item.Author)
+ request, response := test.RequestResponse(
+ "POST",
+ "/sparkles",
+ test.WithAcceptHeader(serde.JSON),
+ test.WithContentType(sparkle, serde.JSON),
+ test.WithContextKeyValue(t.Context(), cfg.CurrentUser, currentUser),
+ )
+
+ mux.ServeHTTP(&FailingResponseWriter{T: t, ResponseRecorder: response}, request)
})
})
- t.Run("prevents double WriteHeader when serialization fails", func(t *testing.T) {
- repository := db.NewRepository[*domain.Sparkle]()
- controller := New(repository)
+ t.Run("when the user is not authorized", func(t *testing.T) {
+ t.Skip()
+ mux := http.NewServeMux()
+ controller := New(repository, stub.Deny())
+ controller.MountTo(mux)
- currentUser := domain.NewUser(domain.WithID[*domain.User](domain.ID("1")))
- sparkle, _ := domain.NewSparkle("@user for testing")
+ t.Run("returns an error", func(t *testing.T) {
+ sparkle := x.New[*domain.Sparkle](domain.WithText("@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),
+ )
- 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)
- controller.Create(&FailingResponseWriter{T: t, ResponseRecorder: response}, request)
+ require.Equal(t, http.StatusForbidden, response.Code)
+ })
})
})
})