From ab3b06b6a8e51af15714c501345cab9788376ebc Mon Sep 17 00:00:00 2001 From: yadongzhang Date: Wed, 4 Dec 2019 09:58:09 +0800 Subject: [PATCH] Add unbinding subresource to policy --- .../controller/policy/policy_controller.go | 53 ++++--------- pkg/auth/registry/policy/storage/binding.go | 15 +--- pkg/auth/registry/policy/storage/storage.go | 19 ++--- pkg/auth/registry/policy/storage/unbinding.go | 78 +++++++++++++++++++ pkg/auth/registry/policy/strategy.go | 72 ++++------------- pkg/auth/registry/policy/validation.go | 73 +++++++++++++++-- 6 files changed, 185 insertions(+), 125 deletions(-) create mode 100644 pkg/auth/registry/policy/storage/unbinding.go diff --git a/pkg/auth/controller/policy/policy_controller.go b/pkg/auth/controller/policy/policy_controller.go index 077e39528..b19037e6f 100644 --- a/pkg/auth/controller/policy/policy_controller.go +++ b/pkg/auth/controller/policy/policy_controller.go @@ -25,7 +25,6 @@ import ( "time" "github.com/casbin/casbin/v2" - "github.com/casbin/casbin/v2/model" "k8s.io/apimachinery/pkg/api/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/runtime" @@ -37,10 +36,8 @@ import ( clientset "tkestack.io/tke/api/client/clientset/versioned" authv1informer "tkestack.io/tke/api/client/informers/externalversions/auth/v1" authv1lister "tkestack.io/tke/api/client/listers/auth/v1" - "tkestack.io/tke/pkg/auth/authorization/enforcer" "tkestack.io/tke/pkg/auth/controller/policy/deletion" authutil "tkestack.io/tke/pkg/auth/util" - "tkestack.io/tke/pkg/auth/util/adapter" controllerutil "tkestack.io/tke/pkg/controller" "tkestack.io/tke/pkg/util" "tkestack.io/tke/pkg/util/log" @@ -70,16 +67,18 @@ type Controller struct { ruleListerSynced cache.InformerSynced // helper to delete all resources in the policy when the policy is deleted. policyedResourcesDeleter deletion.PoliciedResourcesDeleterInterface - enforcer *enforcer.PolicyEnforcer + enforcer *casbin.SyncedEnforcer } // NewController creates a new policy object. -func NewController(client clientset.Interface, policyInformer authv1informer.PolicyInformer, ruleInformer authv1informer.RuleInformer, resyncPeriod time.Duration, finalizerToken v1.FinalizerName) *Controller { +func NewController(client clientset.Interface, policyInformer authv1informer.PolicyInformer, ruleInformer authv1informer.RuleInformer, enforcer *casbin.SyncedEnforcer, resyncPeriod time.Duration, finalizerToken v1.FinalizerName) *Controller { // create the controller so we can inject the enqueue function controller := &Controller{ - client: client, - cache: &policyCache{policyMap: make(map[string]*cachedPolicy)}, - queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), controllerName), + client: client, + cache: &policyCache{policyMap: make(map[string]*cachedPolicy)}, + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), controllerName), + enforcer: enforcer, + policyedResourcesDeleter: deletion.NewPoliciedResourcesDeleter(client.AuthV1().Policies(), client.AuthV1(), enforcer, finalizerToken, true), } if client != nil && client.AuthV1().RESTClient().GetRateLimiter() != nil { @@ -107,18 +106,6 @@ func NewController(client clientset.Interface, policyInformer authv1informer.Pol controller.ruleLister = ruleInformer.Lister() controller.ruleListerSynced = ruleInformer.Informer().HasSynced - adpt := adapter.NewAdapter(client.AuthV1().Rules(), controller.ruleLister) - m, err := model.NewModelFromString(auth.DefaultRuleModel) - if err != nil { - panic(err) - } - e, err := casbin.NewSyncedEnforcer(m, adpt) - if err != nil { - panic(err) - } - controller.enforcer = enforcer.NewPolicyEnforcer(e, nil) - controller.policyedResourcesDeleter = deletion.NewPoliciedResourcesDeleter(client.AuthV1().Policies(), client.AuthV1(), controller.enforcer.Enforcer, finalizerToken, true) - return controller } @@ -161,7 +148,6 @@ func (c *Controller) Run(workers int, stopCh <-chan struct{}) { if ok := cache.WaitForCacheSync(stopCh, c.policyListerSynced, c.ruleListerSynced); !ok { log.Error("Failed to wait for policy caches to sync") } - c.enforcer.Enforcer.StartAutoLoadPolicy(1 * time.Second) for i := 0; i < workers; i++ { go wait.Until(c.worker, time.Second, stopCh) @@ -276,7 +262,7 @@ func (c *Controller) handlePhase(key string, cachedPolicy *cachedPolicy, policy } func (c *Controller) handleSpec(key string, cachedPolicy *cachedPolicy, policy *v1.Policy) error { - existedRule := c.enforcer.Enforcer.GetFilteredPolicy(0, key) + existedRule := c.enforcer.GetFilteredPolicy(0, key) var outPolicy = &auth.Policy{} err := v1.Convert_v1_Policy_To_auth_Policy(policy, outPolicy, nil) @@ -292,7 +278,7 @@ func (c *Controller) handleSpec(key string, cachedPolicy *cachedPolicy, policy * var errs []error if len(added) != 0 { for _, add := range added { - if _, err := c.enforcer.Enforcer.AddPolicy(add); err != nil { + if _, err := c.enforcer.AddPolicy(add); err != nil { log.Errorf("Add policy failed", log.Strings("rule", add), log.Err(err)) errs = append(errs, err) } @@ -301,7 +287,7 @@ func (c *Controller) handleSpec(key string, cachedPolicy *cachedPolicy, policy * if len(removed) != 0 { for _, remove := range removed { - if _, err := c.enforcer.Enforcer.RemovePolicy(remove); err != nil { + if _, err := c.enforcer.RemovePolicy(remove); err != nil { log.Errorf("Remove policy failed", log.Strings("rule", remove), log.Err(err)) errs = append(errs, err) } @@ -312,12 +298,12 @@ func (c *Controller) handleSpec(key string, cachedPolicy *cachedPolicy, policy * } func (c *Controller) handleSubjects(key string, policy *v1.Policy) error { - rules := c.enforcer.Enforcer.GetFilteredGroupingPolicy(1, policy.Name) + rules := c.enforcer.GetFilteredGroupingPolicy(1, policy.Name) log.Debugf("Get grouping rules for policy: %s, %v", policy.Name, rules) var existSubj []string for _, rule := range rules { - if strings.HasPrefix(rule[0], userPrefix(policy.Spec.TenantID)) { - existSubj = append(existSubj, strings.TrimPrefix(rule[0], userPrefix(policy.Spec.TenantID))) + if strings.HasPrefix(rule[0], authutil.UserPrefix(policy.Spec.TenantID)) { + existSubj = append(existSubj, strings.TrimPrefix(rule[0], authutil.UserPrefix(policy.Spec.TenantID))) } } @@ -328,11 +314,10 @@ func (c *Controller) handleSubjects(key string, policy *v1.Policy) error { var errs []error added, removed := util.DiffStringSlice(existSubj, expectedSubj) - log.Info("Handle policy subjects changed", log.String("policy", key), log.Strings("added", added), log.Strings("removed", removed)) if len(added) > 0 { for _, add := range added { - if _, err := c.enforcer.Enforcer.AddRoleForUser(keyUser(policy.Spec.TenantID, add), policy.Name); err != nil { + if _, err := c.enforcer.AddRoleForUser(authutil.UserKey(policy.Spec.TenantID, add), policy.Name); err != nil { log.Errorf("Bind policy to user failed", log.String("policy", policy.Name), log.String("user", add), log.Err(err)) errs = append(errs, err) } @@ -341,7 +326,7 @@ func (c *Controller) handleSubjects(key string, policy *v1.Policy) error { if len(removed) > 0 { for _, remove := range removed { - if _, err := c.enforcer.Enforcer.DeleteRoleForUser(keyUser(policy.Spec.TenantID, remove), policy.Name); err != nil { + if _, err := c.enforcer.DeleteRoleForUser(authutil.UserKey(policy.Spec.TenantID, remove), policy.Name); err != nil { log.Errorf("Bind policy to user failed", log.String("policy", policy.Name), log.String("user", remove), log.Err(err)) errs = append(errs, err) } @@ -370,11 +355,3 @@ func (c *Controller) processDelete(cachedNamespace *cachedPolicy, key string) er return nil } - -func keyUser(tenantID string, name string) string { - return fmt.Sprintf("%s%s", userPrefix(tenantID), name) -} - -func userPrefix(tenantID string) string { - return fmt.Sprintf("%s::", tenantID) -} diff --git a/pkg/auth/registry/policy/storage/binding.go b/pkg/auth/registry/policy/storage/binding.go index e50145a8e..126b62fcc 100644 --- a/pkg/auth/registry/policy/storage/binding.go +++ b/pkg/auth/registry/policy/storage/binding.go @@ -21,11 +21,9 @@ package storage import ( "context" - "tkestack.io/tke/pkg/auth/util" "tkestack.io/tke/pkg/util/log" "k8s.io/apimachinery/pkg/api/errors" - apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/endpoints/request" @@ -69,18 +67,7 @@ func (r *BindingREST) Create(ctx context.Context, obj runtime.Object, createVali for _, sub := range bind.Subjects { if sub.Name != "" { - localIdentity, err := util.GetLocalIdentity(r.authClient, policy.Spec.TenantID, sub.Name) - if err != nil && !apierrors.IsNotFound(err) { - log.Error("Get localIdentity failed", log.String("user", sub.Name), log.Err(err)) - continue - } - - if err == nil { - sub.ID = localIdentity.Name - } - log.Info("1") if !inSubjects(sub, policy.Status.Subjects) { - log.Info("2") policy.Status.Subjects = append(policy.Status.Subjects, sub) } } @@ -93,7 +80,7 @@ func (r *BindingREST) Create(ctx context.Context, obj runtime.Object, createVali func inSubjects(subject auth.Subject, slice []auth.Subject) bool { for _, s := range slice { - if subject.ID == s.ID && subject.Name == s.Name { + if subject.Name == s.Name { return true } } diff --git a/pkg/auth/registry/policy/storage/storage.go b/pkg/auth/registry/policy/storage/storage.go index ad79db74d..6d14ebb55 100644 --- a/pkg/auth/registry/policy/storage/storage.go +++ b/pkg/auth/registry/policy/storage/storage.go @@ -48,14 +48,15 @@ import ( type Storage struct { Policy *REST - Status *StatusREST - Finalize *FinalizeREST - Binding *BindingREST + Status *StatusREST + Finalize *FinalizeREST + Binding *BindingREST + Unbinding *UnbindingREST } // NewStorage returns a Storage object that will work against policies. func NewStorage(optsGetter generic.RESTOptionsGetter, enforcer *enforcer.PolicyEnforcer, authClient authinternalclient.AuthInterface, privilegedUsername string) *Storage { - strategy := policy.NewStrategy(enforcer) + strategy := policy.NewStrategy(enforcer, authClient) store := ®istry.Store{ NewFunc: func() runtime.Object { return &auth.Policy{} }, NewListFunc: func() runtime.Object { return &auth.PolicyList{} }, @@ -66,7 +67,6 @@ func NewStorage(optsGetter generic.RESTOptionsGetter, enforcer *enforcer.PolicyE AfterCreate: strategy.AfterCreate, UpdateStrategy: strategy, DeleteStrategy: strategy, - AfterDelete: strategy.AfterDelete, } options := &generic.StoreOptions{ RESTOptions: optsGetter, @@ -86,10 +86,11 @@ func NewStorage(optsGetter generic.RESTOptionsGetter, enforcer *enforcer.PolicyE finalizeStore.ExportStrategy = policy.NewFinalizerStrategy(strategy) return &Storage{ - Policy: &REST{store, privilegedUsername}, - Status: &StatusREST{&statusStore}, - Finalize: &FinalizeREST{&finalizeStore}, - Binding: &BindingREST{store, authClient}, + Policy: &REST{store, privilegedUsername}, + Status: &StatusREST{&statusStore}, + Finalize: &FinalizeREST{&finalizeStore}, + Binding: &BindingREST{store, authClient}, + Unbinding: &UnbindingREST{store, authClient}, } } diff --git a/pkg/auth/registry/policy/storage/unbinding.go b/pkg/auth/registry/policy/storage/unbinding.go new file mode 100644 index 000000000..bf9d666a9 --- /dev/null +++ b/pkg/auth/registry/policy/storage/unbinding.go @@ -0,0 +1,78 @@ +/* + * Tencent is pleased to support the open source community by making TKEStack + * available. + * + * Copyright (C) 2012-2019 Tencent. All Rights Reserved. + * + * Licensed 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 + * + * https://opensource.org/licenses/Apache-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 OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package storage + +import ( + "context" + + "tkestack.io/tke/pkg/util/log" + + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/generic/registry" + "k8s.io/apiserver/pkg/registry/rest" + + "tkestack.io/tke/api/auth" + authinternalclient "tkestack.io/tke/api/client/clientset/internalversion/typed/auth/internalversion" +) + +// UnbindingREST implements the REST endpoint. +type UnbindingREST struct { + *registry.Store + + authClient authinternalclient.AuthInterface +} + +var _ = rest.Creater(&UnbindingREST{}) + +// New returns an empty object that can be used with Create after request data +// has been put into it. +func (r *UnbindingREST) New() runtime.Object { + return &auth.Binding{} +} + +func (r *UnbindingREST) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) { + requestInfo, ok := request.RequestInfoFrom(ctx) + if !ok { + return nil, errors.NewBadRequest("unable to get request info from context") + } + + log.Info("requestinfo", log.Any("requestInfo", requestInfo)) + + bind := obj.(*auth.Binding) + polObj, err := r.Get(ctx, requestInfo.Name, &metav1.GetOptions{}) + if err != nil { + return nil, err + } + policy := polObj.(*auth.Policy) + var remained []auth.Subject + for _, sub := range policy.Status.Subjects { + if !inSubjects(sub, bind.Subjects) { + remained = append(remained, sub) + } + } + + policy.Status.Subjects = remained + + log.Info("policies", log.Any("subjects", policy.Status.Subjects)) + return r.authClient.Policies().UpdateStatus(policy) +} + diff --git a/pkg/auth/registry/policy/strategy.go b/pkg/auth/registry/policy/strategy.go index 559774c7b..39730eb81 100644 --- a/pkg/auth/registry/policy/strategy.go +++ b/pkg/auth/registry/policy/strategy.go @@ -44,16 +44,19 @@ type Strategy struct { runtime.ObjectTyper names.NameGenerator - enforcer *enforcer.PolicyEnforcer + enforcer *enforcer.PolicyEnforcer + authClient authinternalclient.AuthInterface } // NewStrategy creates a strategy that is the default logic that applies when // creating and updating policy objects. -func NewStrategy(enforcer *enforcer.PolicyEnforcer) *Strategy { +func NewStrategy(enforcer *enforcer.PolicyEnforcer, authClient authinternalclient.AuthInterface) *Strategy { return &Strategy{ ObjectTyper: auth.Scheme, NameGenerator: namesutil.Generator, - enforcer: enforcer} + enforcer: enforcer, + authClient: authClient, + } } // DefaultGarbageCollectionPolicy returns the default garbage collection behavior. @@ -90,11 +93,11 @@ func (Strategy) Export(ctx context.Context, obj runtime.Object, exact bool) erro func (Strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { policy, _ := obj.(*auth.Policy) username, tenantID := authentication.GetUsernameAndTenantID(ctx) - if len(tenantID) != 0 { + if tenantID != "" { policy.Spec.TenantID = tenantID } - if policy.Spec.Username == "" { + if username != "" { policy.Spec.Username = username } @@ -113,7 +116,6 @@ func (Strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { // created and before it is decorated, optional. func (s *Strategy) AfterCreate(obj runtime.Object) error { policy, _ := obj.(*auth.Policy) - if err := func() error { return s.enforcer.AddPolicy(policy) }(); err != nil { @@ -123,24 +125,9 @@ func (s *Strategy) AfterCreate(obj runtime.Object) error { return nil } -// AfterDelete implements a further operation to run after a resource -// has been deleted. -func (s *Strategy) AfterDelete(obj runtime.Object) error { - policy, _ := obj.(*auth.Policy) - - if err := func() error { - // return s.enforcer.DeletePolicy(policy) - return nil - }(); err != nil { - return fmt.Errorf("failed to delete policy '%s', for '%s'", policy.Name, err) - } - - return nil -} - // Validate validates a new policy. -func (Strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { - return ValidatePolicy(obj.(*auth.Policy)) +func (s *Strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { + return ValidatePolicy(obj.(*auth.Policy), s.authClient) } // AllowCreateOnUpdate is false for policies. @@ -160,8 +147,8 @@ func (Strategy) Canonicalize(obj runtime.Object) { } // ValidateUpdate is the default update validation for an end policy. -func (Strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { - return ValidatePolicyUpdate(obj.(*auth.Policy), old.(*auth.Policy)) +func (s *Strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { + return ValidatePolicyUpdate(obj.(*auth.Policy), old.(*auth.Policy), s.authClient) } // GetAttrs returns labels and fields of a given object for filtering purposes. @@ -182,6 +169,7 @@ func MatchPolicy(label labels.Selector, field fields.Selector) storage.Selection IndexFields: []string{ "spec.tenantID", "spec.username", + "spec.category", }, } } @@ -192,6 +180,7 @@ func ToSelectableFields(policy *auth.Policy) fields.Set { specificFieldsSet := fields.Set{ "spec.tenantID": policy.Spec.TenantID, "spec.username": policy.Spec.Username, + "spec.category": policy.Spec.Category, } return generic.MergeFieldsSets(objectMetaFieldsSet, specificFieldsSet) } @@ -222,7 +211,7 @@ func (StatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Obj // filled in before the object is persisted. This method should not mutate // the object. func (s *StatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { - return nil + return ValidatePolicyUpdate(obj.(*auth.Policy), old.(*auth.Policy), s.authClient) } // FinalizeStrategy implements finalizer logic for Machine. @@ -251,34 +240,5 @@ func (FinalizeStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.O // filled in before the object is persisted. This method should not mutate // the object. func (s *FinalizeStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { - return nil -} - -// BindingStrategy implements Binding logic for Machine. -type BindingStrategy struct { - *Strategy - authClient authinternalclient.AuthInterface -} - -var _ rest.RESTUpdateStrategy = &BindingStrategy{} - -// NewFinalizerStrategy create the FinalizeStrategy object by given strategy. -func NewBindingStrategy(strategy *Strategy, authClient authinternalclient.AuthInterface) *BindingStrategy { - return &BindingStrategy{strategy, authClient} -} - -// PrepareForCreate is invoked on create before validation to normalize -// the object. -func (BindingStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { - //binding, _ := obj.(*auth.Binding) - // - //_, tenantID := authentication.GetUsernameAndTenantID(ctx) - // - //// fufill user ID and userName - //for _, subj := range binding.Subjects { - // if subj.ID == "" { - // - // } - //} - return + return ValidatePolicyUpdate(obj.(*auth.Policy), old.(*auth.Policy), s.authClient) } diff --git a/pkg/auth/registry/policy/validation.go b/pkg/auth/registry/policy/validation.go index b6f5f29a1..aec602999 100644 --- a/pkg/auth/registry/policy/validation.go +++ b/pkg/auth/registry/policy/validation.go @@ -19,9 +19,15 @@ package policy import ( + apierrors "k8s.io/apimachinery/pkg/api/errors" apiMachineryValidation "k8s.io/apimachinery/pkg/api/validation" "k8s.io/apimachinery/pkg/util/validation/field" "tkestack.io/tke/api/auth" + "tkestack.io/tke/pkg/auth/util" + "tkestack.io/tke/pkg/util/validation" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + authinternalclient "tkestack.io/tke/api/client/clientset/internalversion/typed/auth/internalversion" ) // ValidatePolicyName is a ValidateNameFunc for names that must be a DNS @@ -29,23 +35,69 @@ import ( var ValidatePolicyName = apiMachineryValidation.NameIsDNSLabel // ValidatePolicy tests if required fields in the policy are set. -func ValidatePolicy(policy *auth.Policy) field.ErrorList { +func ValidatePolicy(policy *auth.Policy, authClient authinternalclient.AuthInterface) field.ErrorList { allErrs := apiMachineryValidation.ValidateObjectMeta(&policy.ObjectMeta, false, ValidatePolicyName, field.NewPath("metadata")) - fldStatPath := field.NewPath("spec", "statement") + fldSpecPath := field.NewPath("spec") + if err := validation.IsDisplayName(policy.Spec.DisplayName); err != nil { + allErrs = append(allErrs, field.Invalid(fldSpecPath.Child("displayName"), policy.Spec.DisplayName, err.Error())) + } + if policy.Spec.Category == "" { + allErrs = append(allErrs, field.Required(fldSpecPath.Child("category"), policy.Spec.Category)) + } + + fldStmtPath := field.NewPath("spec", "statement") if len(policy.Spec.Statement.Actions) == 0 { - allErrs = append(allErrs, field.Required(fldStatPath.Child("actions"), "must specify actions")) + allErrs = append(allErrs, field.Required(fldStmtPath.Child("actions"), "must specify actions")) } if len(policy.Spec.Statement.Resources) == 0 { - allErrs = append(allErrs, field.Required(fldStatPath.Child("resources"), "must specify resources")) + allErrs = append(allErrs, field.Required(fldStmtPath.Child("resources"), "must specify resources")) } if policy.Spec.Statement.Effect == "" { - allErrs = append(allErrs, field.Required(fldStatPath.Child( "effect"), "must specify resources")) + allErrs = append(allErrs, field.Required(fldStmtPath.Child("effect"), "must specify effect")) } else if policy.Spec.Statement.Effect != auth.Allow && policy.Spec.Statement.Effect != auth.Deny { - allErrs = append(allErrs, field.Invalid(fldStatPath.Child( "effect"), policy.Spec.Statement.Effect, "must specify one of: `allow` or `deny`")) + allErrs = append(allErrs, field.Invalid(fldStmtPath.Child("effect"), policy.Spec.Statement.Effect, "must specify one of: `allow` or `deny`")) + } + + fldStatPath := field.NewPath("status") + if len(policy.Status.Subjects) != 0 { + for i, subj := range policy.Status.Subjects { + if subj.ID == "" && subj.Name == "" { + allErrs = append(allErrs, field.Required(fldStatPath.Child("subjects"), "must specify id or name")) + continue + } + + // if specify id, ensure name + if subj.ID != "" { + val, err := authClient.LocalIdentities().Get(subj.ID, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + allErrs = append(allErrs, field.NotFound(fldStatPath.Child("subjects"), subj.ID)) + } else { + allErrs = append(allErrs, field.InternalError(fldStatPath.Child("subjects"), err)) + } + } else { + if val.Spec.TenantID != val.Spec.TenantID { + allErrs = append(allErrs, field.Invalid(fldStmtPath.Child("subjects"), subj.ID, "must in the same tenant with the policy")) + } else { + policy.Status.Subjects[i].Name = val.Name + } + } + } else { + localIdentity, err := util.GetLocalIdentity(authClient, policy.Spec.TenantID, subj.Name) + if err != nil && apierrors.IsNotFound(err) { + continue + } + if err != nil { + allErrs = append(allErrs, field.InternalError(fldStatPath.Child("subjects"), err)) + } else { + policy.Status.Subjects[i].ID = localIdentity.Name + } + } + } } return allErrs @@ -53,8 +105,13 @@ func ValidatePolicy(policy *auth.Policy) field.ErrorList { // ValidatePolicyUpdate tests if required fields in the policy are set during // an update. -func ValidatePolicyUpdate(policy *auth.Policy, old *auth.Policy) field.ErrorList { +func ValidatePolicyUpdate(policy *auth.Policy, old *auth.Policy, authClient authinternalclient.AuthInterface) field.ErrorList { allErrs := apiMachineryValidation.ValidateObjectMetaUpdate(&policy.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata")) - allErrs = append(allErrs, ValidatePolicy(policy)...) + allErrs = append(allErrs, ValidatePolicy(policy, authClient)...) + + fldSpecPath := field.NewPath("spec") + if policy.Spec.TenantID != old.Spec.TenantID { + allErrs = append(allErrs, field.Invalid(fldSpecPath.Child("tenantID"), policy.Spec.TenantID, "disallowed change the tenant")) + } return allErrs }