Skip to content

Commit

Permalink
move GetNamespaceTenant logic to utils from webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
Maksim Fedotov committed Sep 3, 2020
1 parent 9d845b3 commit 1666f21
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 58 deletions.
89 changes: 89 additions & 0 deletions pkg/utils/tenant.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
Copyright 2020 Clastix Labs.
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
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 utils

import (
"context"
"fmt"
"strings"

"github.com/clastix/capsule/api/v1alpha1"
authenticationv1 "k8s.io/api/authentication/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)

type TenantNotFoundError struct{}

func (t TenantNotFoundError) Error() string {
return "You do not have any Tenant assigned: please, reach out the system administrators"
}

func GetNamespaceTenant(ctx context.Context, namespaceName string, forceTenantPrefix bool, userInfo authenticationv1.UserInfo, client client.Client) (*v1alpha1.Tenant, error) {
if forceTenantPrefix {
t := &v1alpha1.Tenant{}
tenantName := strings.Split(namespaceName, "-")[0]
if err := client.Get(ctx, types.NamespacedName{Name: tenantName}, t); err != nil {
return nil, err
}
return t, nil
}

tl, err := listTenantsForOwner(ctx, "User", userInfo.Username, client)
if err != nil {
return nil, err
}
if len(tl.Items) > 0 {
return &tl.Items[0], nil
}

if len(userInfo.Groups) > 0 {
for _, group := range userInfo.Groups {
tl, err := listTenantsForOwner(ctx, "Group", group, client)
if err != nil {
return nil, err
}
if len(tl.Items) > 0 {
return &tl.Items[0], nil
}
}
}
return nil, &TenantNotFoundError{}
}

func IsTenantOwner(os v1alpha1.OwnerSpec, userInfo authenticationv1.UserInfo) bool {
if os.Kind == "User" && userInfo.Username == os.Name {
return true
}
if os.Kind == "Group" {
for _, group := range userInfo.Groups {
if group == os.Name {
return true
}
}
}
return false
}

func listTenantsForOwner(ctx context.Context, ownerKind string, ownerName string, clt client.Client) (*v1alpha1.TenantList, error) {
tl := &v1alpha1.TenantList{}
f := client.MatchingFields{
".spec.owner.ownerkind": fmt.Sprintf("%s:%s", ownerKind, ownerName),
}
err := clt.List(ctx, tl, f)
return tl, err
}
68 changes: 10 additions & 58 deletions pkg/webhook/owner_reference/patching.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ package owner_reference
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -31,6 +29,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

"github.com/clastix/capsule/api/v1alpha1"
"github.com/clastix/capsule/pkg/utils"
"github.com/clastix/capsule/pkg/webhook"
)

Expand Down Expand Up @@ -76,7 +75,7 @@ func (r *handler) OnCreate(ctx context.Context, req admission.Request, clt clien
return admission.Errored(http.StatusBadRequest, err)
}
// Tenant owner must adhere to user that asked for NS creation
if !r.isTenantOwner(t.Spec.Owner, req) {
if !utils.IsTenantOwner(t.Spec.Owner, req.UserInfo) {
return admission.Denied("Cannot assign the desired namespace to a non-owned Tenant")
}
// Patching the response
Expand All @@ -85,43 +84,19 @@ func (r *handler) OnCreate(ctx context.Context, req admission.Request, clt clien

}

// assigning namespace to Tenant in case of --force-tenant-prefix flag enabled
if r.forceTenantPrefix {
tenantName := strings.Split(ns.GetName(), "-")[0]
// retrieving the selected Tenant
t := &v1alpha1.Tenant{}
if err := clt.Get(ctx, types.NamespacedName{Name: tenantName}, t); err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
// Tenant owner must adhere to user that asked for NS creation
if !r.isTenantOwner(t.Spec.Owner, req) {
return admission.Denied("Cannot assign the desired namespace to a non-owned Tenant")
}
// Patching the response
return r.patchResponseForOwnerRef(t, ns)
}

tl, err := r.listTenantsForOwner(ctx, "User", req.UserInfo.Username, clt)
tenant, err := utils.GetNamespaceTenant(ctx, ns.Name, r.forceTenantPrefix, req.UserInfo, clt)
if err != nil {
if err, ok := err.(*utils.TenantNotFoundError); ok {
return admission.Denied(err.Error())
}
return admission.Errored(http.StatusBadRequest, err)
}
if len(tl.Items) > 0 {
return r.patchResponseForOwnerRef(&tl.Items[0], ns)
// Tenant owner must adhere to user that asked for NS creation
if !utils.IsTenantOwner(tenant.Spec.Owner, req.UserInfo) {
return admission.Denied("Cannot assign the desired namespace to a non-owned Tenant")
}

if len(req.UserInfo.Groups) > 0 {
for _, group := range req.UserInfo.Groups {
tl, err := r.listTenantsForOwner(ctx, "Group", group, clt)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
if len(tl.Items) > 0 {
return r.patchResponseForOwnerRef(&tl.Items[0], ns)
}
}
}

return admission.Denied("You do not have any Tenant assigned: please, reach out the system administrators")
return r.patchResponseForOwnerRef(tenant, ns)
}

func (r *handler) OnDelete(ctx context.Context, req admission.Request, client client.Client, decoder *admission.Decoder) admission.Response {
Expand All @@ -132,15 +107,6 @@ func (r *handler) OnUpdate(ctx context.Context, req admission.Request, client cl
return admission.Denied("Capsule user cannot update a Namespace")
}

func (r *handler) listTenantsForOwner(ctx context.Context, ownerKind string, ownerName string, clt client.Client) (*v1alpha1.TenantList, error) {
tl := &v1alpha1.TenantList{}
f := client.MatchingFields{
".spec.owner.ownerkind": fmt.Sprintf("%s:%s", ownerKind, ownerName),
}
err := clt.List(ctx, tl, f)
return tl, err
}

func (r *handler) patchResponseForOwnerRef(tenant *v1alpha1.Tenant, ns *corev1.Namespace) admission.Response {
scheme := runtime.NewScheme()
_ = v1alpha1.AddToScheme(scheme)
Expand All @@ -153,17 +119,3 @@ func (r *handler) patchResponseForOwnerRef(tenant *v1alpha1.Tenant, ns *corev1.N
c, _ := json.Marshal(ns)
return admission.PatchResponseFromRaw(o, c)
}

func (r *handler) isTenantOwner(os v1alpha1.OwnerSpec, req admission.Request) bool {
if os.Kind == "User" && req.UserInfo.Username == os.Name {
return true
}
if os.Kind == "Group" {
for _, group := range req.UserInfo.Groups {
if group == os.Name {
return true
}
}
}
return false
}

0 comments on commit 1666f21

Please sign in to comment.