Skip to content

Commit

Permalink
switch to external SARs
Browse files Browse the repository at this point in the history
  • Loading branch information
deads2k committed Aug 1, 2018
1 parent e4c049c commit c3f9477
Show file tree
Hide file tree
Showing 23 changed files with 75 additions and 70 deletions.
16 changes: 8 additions & 8 deletions pkg/authorization/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@ package util
import (
"errors"

authorizationv1 "k8s.io/api/authorization/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/kubernetes/pkg/apis/authorization"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion"
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1"
)

// AddUserToSAR adds the requisite user information to a SubjectAccessReview.
// It returns the modified SubjectAccessReview.
func AddUserToSAR(user user.Info, sar *authorization.SubjectAccessReview) *authorization.SubjectAccessReview {
func AddUserToSAR(user user.Info, sar *authorizationv1.SubjectAccessReview) *authorizationv1.SubjectAccessReview {
sar.Spec.User = user.GetName()
// reminiscent of the bad old days of C. Copies copy the min number of elements of both source and dest
sar.Spec.Groups = make([]string, len(user.GetGroups()))
copy(sar.Spec.Groups, user.GetGroups())
sar.Spec.Extra = map[string]authorization.ExtraValue{}
sar.Spec.Extra = map[string]authorizationv1.ExtraValue{}

for k, v := range user.GetExtra() {
sar.Spec.Extra[k] = authorization.ExtraValue(v)
sar.Spec.Extra[k] = authorizationv1.ExtraValue(v)
}

return sar
Expand All @@ -29,9 +29,9 @@ func AddUserToSAR(user user.Info, sar *authorization.SubjectAccessReview) *autho
// Authorize verifies that a given user is permitted to carry out a given
// action. If this cannot be determined, or if the user is not permitted, an
// error is returned.
func Authorize(sarClient internalversion.SubjectAccessReviewInterface, user user.Info, resourceAttributes *authorization.ResourceAttributes) error {
sar := AddUserToSAR(user, &authorization.SubjectAccessReview{
Spec: authorization.SubjectAccessReviewSpec{
func Authorize(sarClient authorizationclient.SubjectAccessReviewInterface, user user.Info, resourceAttributes *authorizationv1.ResourceAttributes) error {
sar := AddUserToSAR(user, &authorizationv1.SubjectAccessReview{
Spec: authorizationv1.SubjectAccessReviewSpec{
ResourceAttributes: resourceAttributes,
},
})
Expand Down
28 changes: 14 additions & 14 deletions pkg/build/apiserver/admission/strategyrestrictions/admission.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@ import (
"io"
"strings"

authorizationv1 "k8s.io/api/authorization/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apiserver/pkg/admission"
"k8s.io/client-go/kubernetes"
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1"
"k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/apis/authorization"
kapihelper "k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
authorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion"
kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
rbacregistry "k8s.io/kubernetes/pkg/registry/rbac"

"github.com/openshift/api/build"
Expand All @@ -25,6 +24,7 @@ import (
"github.com/openshift/origin/pkg/build/buildscheme"
oadmission "github.com/openshift/origin/pkg/cmd/server/admission"
"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy"
"k8s.io/apiserver/pkg/admission/initializer"
)

func Register(plugins *admission.Plugins) {
Expand All @@ -40,7 +40,7 @@ type buildByStrategy struct {
buildClient buildclient.Interface
}

var _ = kubeadmission.WantsInternalKubeClientSet(&buildByStrategy{})
var _ = initializer.WantsExternalKubeClientSet(&buildByStrategy{})
var _ = oadmission.WantsRESTClientConfig(&buildByStrategy{})

// NewBuildByStrategy returns an admission control for builds that checks
Expand Down Expand Up @@ -86,8 +86,8 @@ func (a *buildByStrategy) Admit(attr admission.Attributes) error {
}
}

func (a *buildByStrategy) SetInternalKubeClientSet(c internalclientset.Interface) {
a.sarClient = c.Authorization().SubjectAccessReviews()
func (a *buildByStrategy) SetExternalKubeClientSet(c kubernetes.Interface) {
a.sarClient = c.AuthorizationV1().SubjectAccessReviews()
}

func (a *buildByStrategy) SetRESTClientConfig(restClientConfig rest.Config) {
Expand Down Expand Up @@ -146,9 +146,9 @@ func (a *buildByStrategy) checkBuildAuthorization(build *buildapi.Build, attr ad
subresource = tokens[1]
}

sar := util.AddUserToSAR(attr.GetUserInfo(), &authorization.SubjectAccessReview{
Spec: authorization.SubjectAccessReviewSpec{
ResourceAttributes: &authorization.ResourceAttributes{
sar := util.AddUserToSAR(attr.GetUserInfo(), &authorizationv1.SubjectAccessReview{
Spec: authorizationv1.SubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: attr.GetNamespace(),
Verb: "create",
Group: resource.Group,
Expand All @@ -174,9 +174,9 @@ func (a *buildByStrategy) checkBuildConfigAuthorization(buildConfig *buildapi.Bu
subresource = tokens[1]
}

sar := util.AddUserToSAR(attr.GetUserInfo(), &authorization.SubjectAccessReview{
Spec: authorization.SubjectAccessReviewSpec{
ResourceAttributes: &authorization.ResourceAttributes{
sar := util.AddUserToSAR(attr.GetUserInfo(), &authorizationv1.SubjectAccessReview{
Spec: authorizationv1.SubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: attr.GetNamespace(),
Verb: "create",
Group: resource.Group,
Expand Down Expand Up @@ -220,7 +220,7 @@ func (a *buildByStrategy) checkBuildRequestAuthorization(req *buildapi.BuildRequ
}
}

func (a *buildByStrategy) checkAccess(strategy buildapi.BuildStrategy, subjectAccessReview *authorization.SubjectAccessReview, attr admission.Attributes) error {
func (a *buildByStrategy) checkAccess(strategy buildapi.BuildStrategy, subjectAccessReview *authorizationv1.SubjectAccessReview, attr admission.Attributes) error {
resp, err := a.sarClient.Create(subjectAccessReview)
if err != nil {
return admission.NewForbidden(attr, err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,19 @@ import (
"fmt"
"testing"

authorizationv1 "k8s.io/api/authorization/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authentication/user"
fakekubeclient "k8s.io/client-go/kubernetes/fake"
clientgotesting "k8s.io/client-go/testing"
"k8s.io/kubernetes/pkg/apis/authorization"
fakekubeclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"

buildapiv1 "github.com/openshift/api/build/v1"
fakebuildclient "github.com/openshift/client-go/build/clientset/versioned/fake"
buildapi "github.com/openshift/origin/pkg/build/apis/build"
oadmission "github.com/openshift/origin/pkg/cmd/server/admission"

"github.com/openshift/api/build"
_ "github.com/openshift/origin/pkg/build/apis/build/install"
Expand All @@ -33,7 +31,7 @@ func TestBuildAdmission(t *testing.T) {
object runtime.Object
oldObject runtime.Object
responseObject runtime.Object
reviewResponse *authorization.SubjectAccessReview
reviewResponse *authorizationv1.SubjectAccessReview
expectedResource string
expectedSubresource string
expectAccept bool
Expand Down Expand Up @@ -179,7 +177,7 @@ func TestBuildAdmission(t *testing.T) {
},
}

emptyResponse := &authorization.SubjectAccessReview{}
emptyResponse := &authorizationv1.SubjectAccessReview{}
ops := []admission.Operation{admission.Create, admission.Update}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
Expand All @@ -191,7 +189,7 @@ func TestBuildAdmission(t *testing.T) {

fakeKubeClient := fakekubeclient.NewSimpleClientset()
fakeKubeClient.PrependReactor("create", "subjectaccessreviews", func(action clientgotesting.Action) (handled bool, ret runtime.Object, err error) {
review, ok := action.(clientgotesting.CreateAction).GetObject().(*authorization.SubjectAccessReview)
review, ok := action.(clientgotesting.CreateAction).GetObject().(*authorizationv1.SubjectAccessReview)
if !ok {
return true, emptyResponse, fmt.Errorf("unexpected object received: %#v", review)
}
Expand All @@ -211,8 +209,8 @@ func TestBuildAdmission(t *testing.T) {
})

c := NewBuildByStrategy()
c.(kubeadmission.WantsInternalKubeClientSet).SetInternalKubeClientSet(fakeKubeClient)
c.(oadmission.WantsOpenshiftInternalBuildClient).SetOpenshiftInternalBuildClient(fakeBuildClient)
c.(*buildByStrategy).sarClient = fakeKubeClient.AuthorizationV1().SubjectAccessReviews()
c.(*buildByStrategy).buildClient = fakeBuildClient
attrs := admission.NewAttributesRecord(test.object, test.oldObject, test.kind.WithVersion("version"), "foo", "test-build", test.resource.WithVersion("version"), test.subResource, op, fakeUser())
err := c.(admission.MutationInterface).Admit(attrs)
if err != nil && test.expectAccept {
Expand Down Expand Up @@ -298,9 +296,9 @@ func v1TestBuildConfig(strategy buildapiv1.BuildStrategy) *buildapiv1.BuildConfi
}
}

func reviewResponse(allowed bool, msg string) *authorization.SubjectAccessReview {
return &authorization.SubjectAccessReview{
Status: authorization.SubjectAccessReviewStatus{
func reviewResponse(allowed bool, msg string) *authorizationv1.SubjectAccessReview {
return &authorizationv1.SubjectAccessReview{
Status: authorizationv1.SubjectAccessReviewStatus{
Allowed: allowed,
Reason: msg,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func RunTemplateInstanceController(ctx ControllerContext) (bool, error) {
go templatecontroller.NewTemplateInstanceController(
ctx.RestMapper,
dynamicClient,
ctx.ClientBuilder.ClientGoClientOrDie(saName).AuthorizationV1(),
ctx.ClientBuilder.KubeInternalClientOrDie(saName),
ctx.ClientBuilder.OpenshiftInternalBuildClientOrDie(saName),
ctx.ClientBuilder.OpenshiftInternalTemplateClientOrDie(saName),
Expand Down
2 changes: 1 addition & 1 deletion pkg/image/apiserver/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import (
knet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/util/flowcontrol"
authorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"

imageapiv1 "github.com/openshift/api/image/v1"
Expand Down
2 changes: 1 addition & 1 deletion pkg/image/apiserver/registry/imagestream/etcd/etcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage"
authorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion"
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1"
"k8s.io/kubernetes/pkg/printers"
printerstorage "k8s.io/kubernetes/pkg/printers/storage"

Expand Down
2 changes: 1 addition & 1 deletion pkg/image/apiserver/registry/imagestream/etcd/etcd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/openshift/origin/pkg/image/apis/image/validation/fake"
admfake "github.com/openshift/origin/pkg/image/apiserver/admission/fake"
"github.com/openshift/origin/pkg/util/restoptions"
authorizationapi "k8s.io/api/authorization/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand All @@ -16,7 +17,6 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
kapihelper "k8s.io/kubernetes/pkg/apis/core/helper"

// install all APIs
Expand Down
4 changes: 2 additions & 2 deletions pkg/image/apiserver/registry/imagestream/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

"github.com/golang/glog"
authorizationapi "k8s.io/api/authorization/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -14,11 +15,10 @@ import (
"k8s.io/apiserver/pkg/authentication/user"
apirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names"
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1"
"k8s.io/kubernetes/pkg/api/legacyscheme"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
kapi "k8s.io/kubernetes/pkg/apis/core"
kapihelper "k8s.io/kubernetes/pkg/apis/core/helper"
authorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion"

authorizationutil "github.com/openshift/origin/pkg/authorization/util"
imageapi "github.com/openshift/origin/pkg/image/apis/image"
Expand Down
2 changes: 1 addition & 1 deletion pkg/image/apiserver/registry/imagestream/strategy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"
"testing"

authorizationapi "k8s.io/api/authorization/v1"
kapierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -17,7 +18,6 @@ import (
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/authentication/user"
apirequest "k8s.io/apiserver/pkg/endpoints/request"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
kapi "k8s.io/kubernetes/pkg/apis/core"
kquota "k8s.io/kubernetes/pkg/quota"

Expand Down
2 changes: 1 addition & 1 deletion pkg/image/apiserver/registry/imagestreamimage/rest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import (
etcd "github.com/coreos/etcd/clientv3"
"golang.org/x/net/context"

authorizationapi "k8s.io/api/authorization/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
apirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/etcd/etcdtest"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"

imagev1 "github.com/openshift/api/image/v1"
imageapi "github.com/openshift/origin/pkg/image/apis/image"
Expand Down
4 changes: 2 additions & 2 deletions pkg/image/apiserver/registry/imagestreamimport/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/golang/glog"
gocontext "golang.org/x/net/context"

authorizationapi "k8s.io/api/authorization/v1"
corev1 "k8s.io/api/core/v1"
kapierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -18,11 +19,10 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
apirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1"
"k8s.io/kubernetes/pkg/api/legacyscheme"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
kapi "k8s.io/kubernetes/pkg/apis/core"
kapihelper "k8s.io/kubernetes/pkg/apis/core/helper"
authorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion"

"github.com/openshift/api/image"
imageapiv1 "github.com/openshift/api/image/v1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
etcd "github.com/coreos/etcd/clientv3"
"k8s.io/apiserver/pkg/registry/rest"

authorizationapi "k8s.io/api/authorization/v1"
"k8s.io/apimachinery/pkg/api/errors"
metainternal "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -22,7 +23,6 @@ import (
"k8s.io/apiserver/pkg/storage/etcd/etcdtest"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
kapi "k8s.io/kubernetes/pkg/apis/core"

imagegroup "github.com/openshift/api/image"
Expand Down
2 changes: 1 addition & 1 deletion pkg/image/apiserver/registry/imagestreamtag/rest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"golang.org/x/net/context"
"k8s.io/apiserver/pkg/registry/rest"

authorizationapi "k8s.io/api/authorization/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -18,7 +19,6 @@ import (
"k8s.io/apiserver/pkg/storage/etcd/etcdtest"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
kapi "k8s.io/kubernetes/pkg/apis/core"

imagev1 "github.com/openshift/api/image/v1"
Expand Down
2 changes: 1 addition & 1 deletion pkg/project/apiserver/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1"
restclient "k8s.io/client-go/rest"
kclientsetinternal "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
authorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion"
kinternalinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"

projectapiv1 "github.com/openshift/api/project/v1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"time"

"github.com/golang/glog"
authorizationapi "k8s.io/api/authorization/v1"
kapierror "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metainternal "k8s.io/apimachinery/pkg/apis/meta/internalversion"
Expand All @@ -21,11 +22,10 @@ import (
apirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/client-go/dynamic"
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1"
"k8s.io/client-go/util/retry"
"k8s.io/kubernetes/pkg/api/legacyscheme"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
"k8s.io/kubernetes/pkg/apis/rbac"
authorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion"
rbaclisters "k8s.io/kubernetes/pkg/client/listers/rbac/internalversion"

"github.com/openshift/api/project"
Expand Down
2 changes: 1 addition & 1 deletion pkg/route/apiserver/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1"
restclient "k8s.io/client-go/rest"

routeapiv1 "github.com/openshift/api/route/v1"
routeetcd "github.com/openshift/origin/pkg/route/apiserver/registry/route/etcd"
routeallocationcontroller "github.com/openshift/origin/pkg/route/controller/allocation"
authorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion"
)

type ExtraConfig struct {
Expand Down
Loading

0 comments on commit c3f9477

Please sign in to comment.