summaryrefslogtreecommitdiff
path: root/app/db/authorization.go
diff options
context:
space:
mode:
Diffstat (limited to 'app/db/authorization.go')
-rw-r--r--app/db/authorization.go112
1 files changed, 112 insertions, 0 deletions
diff --git a/app/db/authorization.go b/app/db/authorization.go
new file mode 100644
index 0000000..ec8e47b
--- /dev/null
+++ b/app/db/authorization.go
@@ -0,0 +1,112 @@
+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 authorization[T domain.Entity] struct {
+ client *authzed.Client
+ repository domain.Repository[T]
+}
+
+func WithAuthorization[T domain.Entity](client *authzed.Client) x.Option[domain.Repository[T]] {
+ return func(repository domain.Repository[T]) domain.Repository[T] {
+ return &authorization[T]{
+ client: client,
+ repository: repository,
+ }
+ }
+}
+
+func (r *authorization[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 *authorization[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 *authorization[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 *authorization[T]) subjectFrom(ctx context.Context) *v1.SubjectReference {
+ currentUser := cfg.CurrentUser.From(ctx)
+ return currentUser.ToSubjectReference()
+}