@@ -2,6 +2,7 @@ package k8scel
22
33import (
44 "context"
5+ "encoding/json"
56 "errors"
67 "fmt"
78 "strings"
@@ -15,15 +16,18 @@ import (
1516 "github.com/open-policy-agent/frameworks/constraint/pkg/types"
1617 pSchema "github.com/open-policy-agent/gatekeeper/v3/pkg/drivers/k8scel/schema"
1718 "github.com/open-policy-agent/gatekeeper/v3/pkg/drivers/k8scel/transform"
19+ "github.com/open-policy-agent/gatekeeper/v3/pkg/logging"
1820 "github.com/open-policy-agent/opa/storage"
1921 admissionv1 "k8s.io/api/admission/v1"
22+ corev1 "k8s.io/api/core/v1"
2023 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2124 "k8s.io/apimachinery/pkg/runtime"
2225 "k8s.io/apiserver/pkg/admission/plugin/cel"
2326 "k8s.io/apiserver/pkg/admission/plugin/policy/validating"
2427 "k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
2528 celAPI "k8s.io/apiserver/pkg/apis/cel"
2629 "k8s.io/apiserver/pkg/cel/environment"
30+ logf "sigs.k8s.io/controller-runtime/pkg/log"
2731)
2832
2933// NOTE: This is a PROTOTYPE driver. Do not use this for any critical work
@@ -48,7 +52,10 @@ const (
4852 runTimeNSDescription = "the number of nanoseconds it took to evaluate the constraint"
4953)
5054
51- var _ drivers.Driver = & Driver {}
55+ var (
56+ _ drivers.Driver = & Driver {}
57+ log = logf .Log .WithName ("k8scel-driver" ).WithValues (logging .Process , "k8scel" )
58+ )
5259
5360type Driver struct {
5461 mux sync.RWMutex
@@ -198,8 +205,18 @@ func (d *Driver) Query(ctx context.Context, target string, constraints []*unstru
198205 return nil , fmt .Errorf ("nil validator for constraint template %v" , strings .ToLower (constraint .GetKind ()))
199206 }
200207
201- // TODO: should namespace be made available, if possible? Generally that context should be present
202- response := validator .Validate (ctx , versionedAttr .GetResource (), versionedAttr , constraint , nil , celAPI .PerCallLimit , nil )
208+ // Convert namespace from map[string]interface{} to *corev1.Namespace if provided.
209+ // This enables CEL expressions to access namespaceObject for namespace-based policies.
210+ var namespace * corev1.Namespace
211+ if cfg .Namespace != nil {
212+ namespace , err = mapToNamespace (cfg .Namespace )
213+ if err != nil {
214+ log .Error (err , "failed to convert namespace to corev1.Namespace, continuing without namespace" )
215+ namespace = nil
216+ }
217+ }
218+
219+ response := validator .Validate (ctx , versionedAttr .GetResource (), versionedAttr , constraint , namespace , celAPI .PerCallLimit , nil )
203220
204221 for _ , decision := range response .Decisions {
205222 if decision .Action == validating .ActionDeny {
@@ -252,3 +269,24 @@ type ARGetter interface {
252269type IsAdmissionGetter interface {
253270 IsAdmissionRequest () bool
254271}
272+
273+ // mapToNamespace converts a map[string]interface{} representation of a namespace
274+ // to a *corev1.Namespace. This is used to pass the namespace object to the CEL
275+ // validator for namespaceObject support.
276+ func mapToNamespace (ns map [string ]interface {}) (* corev1.Namespace , error ) {
277+ if ns == nil {
278+ return nil , nil
279+ }
280+
281+ data , err := json .Marshal (ns )
282+ if err != nil {
283+ return nil , fmt .Errorf ("failed to marshal namespace: %w" , err )
284+ }
285+
286+ namespace := & corev1.Namespace {}
287+ if err := json .Unmarshal (data , namespace ); err != nil {
288+ return nil , fmt .Errorf ("failed to unmarshal namespace: %w" , err )
289+ }
290+
291+ return namespace , nil
292+ }
0 commit comments