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() }