summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/db/secure_repository.go119
1 files changed, 119 insertions, 0 deletions
diff --git a/app/db/secure_repository.go b/app/db/secure_repository.go
new file mode 100644
index 0000000..26b85c9
--- /dev/null
+++ b/app/db/secure_repository.go
@@ -0,0 +1,119 @@
+package db
+
+import (
+ "context"
+ "errors"
+
+ v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
+ "github.com/authzed/authzed-go/v1"
+ "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/domain"
+ "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/pls"
+)
+
+type SecureRepository[T domain.Entity] struct {
+ client *authzed.Client
+ repository domain.Repository[T]
+}
+
+func NewSecureRepository[T domain.Entity](client *authzed.Client, repository domain.Repository[T]) domain.Repository[T] {
+ return &SecureRepository[T]{
+ client: client,
+ repository: repository,
+ }
+}
+
+func (r *SecureRepository[T]) All(ctx context.Context) []T {
+ allItems := r.repository.All(ctx)
+ if len(allItems) == 0 {
+ return allItems
+ }
+
+ response, err := r.client.CheckBulkPermissions(ctx, &v1.CheckBulkPermissionsRequest{
+ Items: x.Map(allItems, func(item T) *v1.CheckBulkPermissionsRequestItem {
+ return &v1.CheckBulkPermissionsRequestItem{
+ Resource: item.ToGID().ToObjectReference(),
+ Permission: "read",
+ Subject: r.subjectFrom(ctx),
+ }
+ }),
+ })
+ if err != nil {
+ pls.LogError(ctx, err)
+ return x.Default[[]T]()
+ }
+
+ filteredItems := make([]T, 0)
+ for i, pair := range response.Pairs {
+ if item := pair.GetItem(); item != nil {
+ if item.Permissionship == v1.CheckPermissionResponse_PERMISSIONSHIP_HAS_PERMISSION {
+ filteredItems = append(filteredItems, allItems[i])
+ }
+ }
+ }
+
+ return filteredItems
+}
+
+func (r *SecureRepository[T]) Find(ctx context.Context, id domain.ID) T {
+ item := r.repository.Find(ctx, id)
+
+ response, err := r.client.CheckPermission(ctx, &v1.CheckPermissionRequest{
+ Resource: item.ToGID().ToObjectReference(),
+ Permission: "read",
+ Subject: r.subjectFrom(ctx),
+ })
+ if err != nil {
+ pls.LogError(ctx, err)
+ return x.Default[T]()
+ }
+
+ if response.Permissionship != v1.CheckPermissionResponse_PERMISSIONSHIP_HAS_PERMISSION {
+ return x.Default[T]()
+ }
+
+ return item
+}
+
+func (r *SecureRepository[T]) Save(ctx context.Context, item T) error {
+ currentUser := cfg.CurrentUser.From(ctx)
+ if currentUser == nil {
+ return errors.New("authentication required for creating or updating entities")
+ }
+
+ if item.GetID() != "" {
+ response, err := r.client.CheckPermission(ctx, &v1.CheckPermissionRequest{
+ Resource: item.ToGID().ToObjectReference(),
+ Permission: "update",
+ Subject: currentUser.ToSubjectReference(),
+ })
+ if err != nil {
+ return err
+ }
+
+ if response.Permissionship != v1.CheckPermissionResponse_PERMISSIONSHIP_HAS_PERMISSION {
+ return errors.New("user does not have permission to update this entity")
+ }
+ }
+
+ if sparkle, ok := any(item).(*domain.Sparkle); ok && item.GetID() == "" {
+ sparkle.Author = currentUser
+ }
+
+ return r.repository.Save(ctx, item)
+}
+
+func (r *SecureRepository[T]) subjectFrom(ctx context.Context) *v1.SubjectReference {
+ currentUser := cfg.CurrentUser.From(ctx)
+ if currentUser == nil {
+ return &v1.SubjectReference{
+ Object: &v1.ObjectReference{
+ ObjectType: "user",
+ ObjectId: "*",
+ },
+ }
+ }
+
+ return currentUser.ToSubjectReference()
+}