diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-30 11:03:45 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-30 11:03:45 -0600 |
| commit | c1698f896ff343b1b65e57d3961a78d3bb263b7c (patch) | |
| tree | 3eecd594af67f4849ccea1d8ad758004ccfdec9c /app/db/authorization.go | |
| parent | 0626bc0cfffa89b73adc2f9576354e8462270eae (diff) | |
refactor: rename repository types
Diffstat (limited to 'app/db/authorization.go')
| -rw-r--r-- | app/db/authorization.go | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/app/db/authorization.go b/app/db/authorization.go new file mode 100644 index 0000000..3041f51 --- /dev/null +++ b/app/db/authorization.go @@ -0,0 +1,121 @@ +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) + if currentUser == nil { + return &v1.SubjectReference{ + Object: &v1.ObjectReference{ + ObjectType: "user", + ObjectId: "*", + }, + } + } + + return currentUser.ToSubjectReference() +} |
