Skip to content

Commit

Permalink
Add unbinding subresource to policy
Browse files Browse the repository at this point in the history
  • Loading branch information
yadzhang authored and choujimmy committed Dec 25, 2019
1 parent 96de07d commit ab3b06b
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 125 deletions.
53 changes: 15 additions & 38 deletions pkg/auth/controller/policy/policy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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)))
}
}

Expand All @@ -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)
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -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)
}
15 changes: 1 addition & 14 deletions pkg/auth/registry/policy/storage/binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}
}
Expand All @@ -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
}
}
Expand Down
19 changes: 10 additions & 9 deletions pkg/auth/registry/policy/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 := &registry.Store{
NewFunc: func() runtime.Object { return &auth.Policy{} },
NewListFunc: func() runtime.Object { return &auth.PolicyList{} },
Expand All @@ -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,
Expand All @@ -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},
}
}

Expand Down
78 changes: 78 additions & 0 deletions pkg/auth/registry/policy/storage/unbinding.go
Original file line number Diff line number Diff line change
@@ -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)
}

Loading

0 comments on commit ab3b06b

Please sign in to comment.