pkg/authority/k8s/controller.go (276 lines of code) (raw):

// Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package k8s import ( apiV1beta1 "github.com/apache/dubbo-admin/pkg/authority/apis/dubbo.apache.org/v1beta1" clientSet "github.com/apache/dubbo-admin/pkg/authority/generated/clientset/versioned" informerV1beta1 "github.com/apache/dubbo-admin/pkg/authority/generated/informers/externalversions/dubbo.apache.org/v1beta1" "github.com/apache/dubbo-admin/pkg/authority/rule/authentication" "github.com/apache/dubbo-admin/pkg/authority/rule/authorization" "github.com/apache/dubbo-admin/pkg/logger" "k8s.io/client-go/tools/cache" "k8s.io/utils/strings/slices" ) type NotificationType int const ( // AddNotification is a notification type for add events. AddNotification NotificationType = iota // UpdateNotification is a notification type for update events. UpdateNotification // DeleteNotification is a notification type for delete events. DeleteNotification ) // Controller is the controller implementation for Foo resources type Controller struct { dubboClientSet clientSet.Interface rootNamespace string authenticationSynced cache.InformerSynced authorizationSynced cache.InformerSynced authenticationHandler authentication.Handler authorizationHandler authorization.Handler } // NewController returns a new sample controller func NewController( clientSet clientSet.Interface, rootNamespace string, authenticationHandler authentication.Handler, authorizationHandler authorization.Handler, acInformer informerV1beta1.AuthenticationPolicyInformer, apInformer informerV1beta1.AuthorizationPolicyInformer, ) *Controller { controller := &Controller{ dubboClientSet: clientSet, rootNamespace: rootNamespace, authenticationSynced: acInformer.Informer().HasSynced, authorizationSynced: apInformer.Informer().HasSynced, authenticationHandler: authenticationHandler, authorizationHandler: authorizationHandler, } logger.Sugar().Info("Setting up event handlers") // Set up an event handler for when Foo resources change _, err := acInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { controller.handleEvent(obj, AddNotification) }, UpdateFunc: func(oldObj, newObj interface{}) { controller.handleEvent(newObj, UpdateNotification) }, DeleteFunc: func(obj interface{}) { controller.handleEvent(obj, DeleteNotification) }, }) if err != nil { return nil } _, err = apInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { controller.handleEvent(obj, AddNotification) }, UpdateFunc: func(oldObj, newObj interface{}) { controller.handleEvent(newObj, UpdateNotification) }, DeleteFunc: func(obj interface{}) { controller.handleEvent(obj, DeleteNotification) }, }) if err != nil { return nil } return controller } func (c *Controller) WaitSynced() { logger.Sugar().Info("Waiting for informer caches to sync") if !cache.WaitForCacheSync(make(chan struct{}), c.authenticationSynced, c.authorizationSynced) { logger.Sugar().Error("Timed out waiting for caches to sync") return } else { logger.Sugar().Info("Caches synced") } } func (c *Controller) handleEvent(obj interface{}, eventType NotificationType) { key, err := cache.MetaNamespaceKeyFunc(obj) if err != nil { logger.Sugar().Errorf("error getting key for object: %v", err) return } switch o := obj.(type) { case *apiV1beta1.AuthenticationPolicy: a := CopyToAuthentication(key, c.rootNamespace, o) switch eventType { case AddNotification: c.authenticationHandler.Add(key, a) case UpdateNotification: c.authenticationHandler.Update(key, a) case DeleteNotification: c.authenticationHandler.Delete(key) } return case *apiV1beta1.AuthorizationPolicy: a := CopyToAuthorization(key, c.rootNamespace, o) switch eventType { case AddNotification: c.authorizationHandler.Add(key, a) case UpdateNotification: c.authorizationHandler.Update(key, a) case DeleteNotification: c.authorizationHandler.Delete(key) } default: logger.Sugar().Errorf("unexpected object type: %v", obj) return } } func CopyToAuthentication(key, rootNamespace string, pa *apiV1beta1.AuthenticationPolicy) *authentication.Policy { a := &authentication.Policy{} a.Name = key a.Spec = &authentication.PolicySpec{} a.Spec.Action = pa.Spec.Action if pa.Spec.Selector != nil { for _, selector := range pa.Spec.Selector { r := &authentication.Selector{ Namespaces: selector.Namespaces, NotNamespaces: selector.NotNamespaces, IpBlocks: selector.IpBlocks, NotIpBlocks: selector.NotIpBlocks, Principals: selector.Principals, NotPrincipals: selector.NotPrincipals, } if selector.Extends != nil { for _, extends := range selector.Extends { r.Extends = append(r.Extends, &authentication.Extend{ Key: extends.Key, Value: extends.Value, }) } } if selector.NotExtends != nil { for _, notExtend := range selector.NotExtends { r.NotExtends = append(r.NotExtends, &authentication.Extend{ Key: notExtend.Key, Value: notExtend.Value, }) } } a.Spec.Selector = append(a.Spec.Selector, r) } } if pa.Spec.PortLevel != nil { for _, portLevel := range pa.Spec.PortLevel { r := &authentication.PortLevel{ Port: portLevel.Port, Action: portLevel.Action, } a.Spec.PortLevel = append(a.Spec.PortLevel, r) } } if rootNamespace == pa.Namespace { return a } if len(a.Spec.Selector) == 0 { a.Spec.Selector = append(a.Spec.Selector, &authentication.Selector{ Namespaces: []string{pa.Namespace}, }) } else { for _, selector := range a.Spec.Selector { if !slices.Contains(selector.Namespaces, pa.Namespace) { selector.Namespaces = append(selector.Namespaces, pa.Namespace) } } } return a } func CopyToAuthorization(key, rootNamespace string, pa *apiV1beta1.AuthorizationPolicy) *authorization.Policy { a := &authorization.Policy{} a.Name = key a.Spec = &authorization.PolicySpec{} a.Spec.Action = pa.Spec.Action if pa.Spec.Rules != nil { for _, rule := range pa.Spec.Rules { r := &authorization.PolicyRule{ From: &authorization.Source{ Namespaces: rule.From.Namespaces, NotNamespaces: rule.From.NotNamespaces, IpBlocks: rule.From.IpBlocks, NotIpBlocks: rule.From.NotIpBlocks, Principals: rule.From.Principals, NotPrincipals: rule.From.NotPrincipals, }, To: &authorization.Target{ Namespaces: rule.To.Namespaces, NotNamespaces: rule.To.NotNamespaces, IpBlocks: rule.To.IpBlocks, NotIpBlocks: rule.To.NotIpBlocks, Principals: rule.To.Principals, NotPrincipals: rule.To.NotPrincipals, }, When: &authorization.Condition{ Key: rule.When.Key, }, } if rule.From.Extends != nil { for _, extends := range rule.From.Extends { r.From.Extends = append(r.From.Extends, &authorization.Extend{ Key: extends.Key, Value: extends.Value, }) } } if rule.From.NotExtends != nil { for _, notExtend := range rule.From.NotExtends { r.From.NotExtends = append(r.From.NotExtends, &authorization.Extend{ Key: notExtend.Key, Value: notExtend.Value, }) } } if rule.To.Extends != nil { for _, extends := range rule.To.Extends { r.To.Extends = append(r.To.Extends, &authorization.Extend{ Key: extends.Key, Value: extends.Value, }) } } if rule.To.NotExtends != nil { for _, notExtend := range rule.To.NotExtends { r.To.NotExtends = append(r.To.NotExtends, &authorization.Extend{ Key: notExtend.Key, Value: notExtend.Value, }) } } if rule.When.Values != nil { for _, value := range rule.When.Values { r.When.Values = append(r.When.Values, &authorization.Match{ Type: value.Type, Value: value.Value, }) } } if rule.When.NotValues != nil { for _, notValue := range rule.When.NotValues { r.When.Values = append(r.When.Values, &authorization.Match{ Type: notValue.Type, Value: notValue.Value, }) } } a.Spec.Rules = append(a.Spec.Rules, r) } } a.Spec.Samples = pa.Spec.Samples a.Spec.Order = pa.Spec.Order a.Spec.MatchType = pa.Spec.MatchType if rootNamespace == pa.Namespace { return a } if len(a.Spec.Rules) == 0 { a.Spec.Rules = append(a.Spec.Rules, &authorization.PolicyRule{ To: &authorization.Target{ Namespaces: []string{pa.Namespace}, }, }) } else { for _, rule := range a.Spec.Rules { if rule.To != nil { rule.To = &authorization.Target{} } if !slices.Contains(rule.To.Namespaces, pa.Namespace) { rule.To.Namespaces = append(rule.To.Namespaces, pa.Namespace) } } } return a }