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)
@@ -274,23 +276,14 @@ type Boxcutter struct {
274276 Scheme * runtime.Scheme
275277 RevisionGenerator ClusterExtensionRevisionGenerator
276278 Preflights []Preflight
279+ PreAuthorizer authorization.PreAuthorizer
277280 FieldOwner string
278281}
279282
280283func (bc * Boxcutter ) Apply (ctx context.Context , contentFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , revisionAnnotations map [string ]string ) (bool , string , error ) {
281284 return bc .apply (ctx , contentFS , ext , objectLabels , revisionAnnotations )
282285}
283286
284- func (bc * Boxcutter ) getObjects (rev * ocv1.ClusterExtensionRevision ) []client.Object {
285- var objs []client.Object
286- for _ , phase := range rev .Spec .Phases {
287- for _ , phaseObject := range phase .Objects {
288- objs = append (objs , & phaseObject .Object )
289- }
290- }
291- return objs
292- }
293-
294287func (bc * Boxcutter ) createOrUpdate (ctx context.Context , obj client.Object ) error {
295288 if obj .GetObjectKind ().GroupVersionKind ().Empty () {
296289 gvk , err := apiutil .GVKForObject (obj , bc .Scheme )
@@ -309,6 +302,11 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
309302 return false , "" , err
310303 }
311304
305+ // Run auth preflight checks
306+ if err := bc .runPreAuthorizationChecks (ctx , ext , desiredRevision ); err != nil {
307+ return false , "" , err
308+ }
309+
312310 if err := controllerutil .SetControllerReference (ext , desiredRevision , bc .Scheme ); err != nil {
313311 return false , "" , fmt .Errorf ("set ownerref: %w" , err )
314312 }
@@ -343,7 +341,7 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
343341 }
344342
345343 // Preflights
346- plainObjs := bc . getObjects (desiredRevision )
344+ plainObjs := getObjects (desiredRevision )
347345 for _ , preflight := range bc .Preflights {
348346 if shouldSkipPreflight (ctx , preflight , ext , state ) {
349347 continue
@@ -405,6 +403,20 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
405403 return true , "" , nil
406404}
407405
406+ // runPreAuthorizationChecks runs PreAuthorization checks if the PreAuthorizer is set. An error will be returned if
407+ // the ClusterExtension service account does not have the necessary permissions to manage to the revision's resources
408+ func (bc * Boxcutter ) runPreAuthorizationChecks (ctx context.Context , ext * ocv1.ClusterExtension , rev * ocv1.ClusterExtensionRevision ) error {
409+ if bc .PreAuthorizer == nil {
410+ return nil
411+ }
412+
413+ manifestReader , err := revisionManifestReader (rev )
414+ if err != nil {
415+ return err
416+ }
417+ return formatPreAuthorizerOutput (bc .PreAuthorizer .PreAuthorize (ctx , ext , manifestReader ))
418+ }
419+
408420// garbageCollectOldRevisions deletes archived revisions beyond ClusterExtensionRevisionRetentionLimit.
409421// Active revisions are never deleted. revisionList must be sorted oldest to newest.
410422func (bc * Boxcutter ) garbageCollectOldRevisions (ctx context.Context , revisionList []ocv1.ClusterExtensionRevision ) error {
@@ -463,3 +475,29 @@ func splitManifestDocuments(file string) []string {
463475 }
464476 return docs
465477}
478+
479+ // getObjects returns a slice of all objects in the revision
480+ func getObjects (rev * ocv1.ClusterExtensionRevision ) []client.Object {
481+ var objs []client.Object
482+ for _ , phase := range rev .Spec .Phases {
483+ for _ , phaseObject := range phase .Objects {
484+ objs = append (objs , & phaseObject .Object )
485+ }
486+ }
487+ return objs
488+ }
489+
490+ // revisionMenifestReader returns an io.Reader containing all manifests in the revision
491+ func revisionManifestReader (rev * ocv1.ClusterExtensionRevision ) (io.Reader , error ) {
492+ var manifestBuilder strings.Builder
493+ for _ , obj := range getObjects (rev ) {
494+ objBytes , err := yaml .Marshal (obj )
495+ if err != nil {
496+ return nil , fmt .Errorf ("error generating revision manifest: %w" , err )
497+ }
498+ manifestBuilder .WriteString ("---\n " )
499+ manifestBuilder .WriteString (string (objBytes ))
500+ manifestBuilder .WriteString ("\n " )
501+ }
502+ return strings .NewReader (manifestBuilder .String ()), nil
503+ }
0 commit comments