55 "context"
66 "errors"
77 "fmt"
8+ "io"
89 "io/fs"
910 "maps"
1011 "slices"
@@ -26,6 +27,7 @@ import (
2627 helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client"
2728
2829 ocv1 "github.com/operator-framework/operator-controller/api/v1"
30+ "github.com/operator-framework/operator-controller/internal/operator-controller/authorization"
2931 "github.com/operator-framework/operator-controller/internal/operator-controller/labels"
3032 "github.com/operator-framework/operator-controller/internal/shared/util/cache"
3133)
@@ -280,23 +282,14 @@ type Boxcutter struct {
280282 Scheme * runtime.Scheme
281283 RevisionGenerator ClusterExtensionRevisionGenerator
282284 Preflights []Preflight
285+ PreAuthorizer authorization.PreAuthorizer
283286 FieldOwner string
284287}
285288
286289func (bc * Boxcutter ) Apply (ctx context.Context , contentFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , revisionAnnotations map [string ]string ) (bool , string , error ) {
287290 return bc .apply (ctx , contentFS , ext , objectLabels , revisionAnnotations )
288291}
289292
290- func (bc * Boxcutter ) getObjects (rev * ocv1.ClusterExtensionRevision ) []client.Object {
291- var objs []client.Object
292- for _ , phase := range rev .Spec .Phases {
293- for _ , phaseObject := range phase .Objects {
294- objs = append (objs , & phaseObject .Object )
295- }
296- }
297- return objs
298- }
299-
300293func (bc * Boxcutter ) createOrUpdate (ctx context.Context , obj client.Object ) error {
301294 if obj .GetObjectKind ().GroupVersionKind ().Empty () {
302295 gvk , err := apiutil .GVKForObject (obj , bc .Scheme )
@@ -315,6 +308,11 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
315308 return false , "" , err
316309 }
317310
311+ // Run auth preflight checks
312+ if err := bc .runPreAuthorizationChecks (ctx , ext , desiredRevision ); err != nil {
313+ return false , "" , err
314+ }
315+
318316 if err := controllerutil .SetControllerReference (ext , desiredRevision , bc .Scheme ); err != nil {
319317 return false , "" , fmt .Errorf ("set ownerref: %w" , err )
320318 }
@@ -349,7 +347,7 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
349347 }
350348
351349 // Preflights
352- plainObjs := bc . getObjects (desiredRevision )
350+ plainObjs := getObjects (desiredRevision )
353351 for _ , preflight := range bc .Preflights {
354352 if shouldSkipPreflight (ctx , preflight , ext , state ) {
355353 continue
@@ -411,6 +409,20 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
411409 return true , "" , nil
412410}
413411
412+ // runPreAuthorizationChecks runs PreAuthorization checks if the PreAuthorizer is set. An error will be returned if
413+ // the ClusterExtension service account does not have the necessary permissions to manage to the revision's resources
414+ func (bc * Boxcutter ) runPreAuthorizationChecks (ctx context.Context , ext * ocv1.ClusterExtension , rev * ocv1.ClusterExtensionRevision ) error {
415+ if bc .PreAuthorizer == nil {
416+ return nil
417+ }
418+
419+ manifestReader , err := revisionManifestReader (rev )
420+ if err != nil {
421+ return err
422+ }
423+ return formatPreAuthorizerOutput (bc .PreAuthorizer .PreAuthorize (ctx , ext , manifestReader ))
424+ }
425+
414426// garbageCollectOldRevisions deletes archived revisions beyond ClusterExtensionRevisionRetentionLimit.
415427// Active revisions are never deleted. revisionList must be sorted oldest to newest.
416428func (bc * Boxcutter ) garbageCollectOldRevisions (ctx context.Context , revisionList []ocv1.ClusterExtensionRevision ) error {
@@ -469,3 +481,29 @@ func splitManifestDocuments(file string) []string {
469481 }
470482 return docs
471483}
484+
485+ // getObjects returns a slice of all objects in the revision
486+ func getObjects (rev * ocv1.ClusterExtensionRevision ) []client.Object {
487+ var objs []client.Object
488+ for _ , phase := range rev .Spec .Phases {
489+ for _ , phaseObject := range phase .Objects {
490+ objs = append (objs , & phaseObject .Object )
491+ }
492+ }
493+ return objs
494+ }
495+
496+ // revisionMenifestReader returns an io.Reader containing all manifests in the revision
497+ func revisionManifestReader (rev * ocv1.ClusterExtensionRevision ) (io.Reader , error ) {
498+ var manifestBuilder strings.Builder
499+ for _ , obj := range getObjects (rev ) {
500+ objBytes , err := yaml .Marshal (obj )
501+ if err != nil {
502+ return nil , fmt .Errorf ("error generating revision manifest: %w" , err )
503+ }
504+ manifestBuilder .WriteString ("---\n " )
505+ manifestBuilder .WriteString (string (objBytes ))
506+ manifestBuilder .WriteString ("\n " )
507+ }
508+ return strings .NewReader (manifestBuilder .String ()), nil
509+ }
0 commit comments