From 60dd4a5d2698af19f0d9bcedf050c6d6e7649b02 Mon Sep 17 00:00:00 2001 From: deads2k Date: Tue, 19 Jul 2016 14:47:53 -0400 Subject: [PATCH] interesting changes to add tokenreviews endpoint to implement webhook --- cmd/libs/go2idl/client-gen/main.go | 2 +- .../go2idl/go-to-protobuf/protobuf/cmd.go | 1 + hack/make-rules/test-cmd.sh | 4 + hack/make-rules/test.sh | 2 +- hack/update-generated-swagger-docs.sh | 2 +- hack/update-swagger-spec.sh | 2 +- pkg/api/testapi/testapi.go | 1 + pkg/apis/authentication/doc.go | 1 - .../types.go | 16 +++- pkg/apis/authentication/v1beta1/doc.go | 2 +- .../v1beta1/types.go | 30 ++++--- .../fake/fake_generated_expansion.go | 28 +++++++ .../unversioned/generated_expansion.go | 35 +++++++++ pkg/client/unversioned/authentication.go | 77 ++++++++++++++++++ pkg/client/unversioned/client.go | 6 ++ pkg/client/unversioned/helper.go | 23 +++++- .../unversioned/import_known_versions.go | 2 +- .../testclient/fake_tokenreviews.go | 35 +++++++++ .../unversioned/testclient/testclient.go | 16 ++++ pkg/client/unversioned/tokenreviews.go | 49 ++++++++++++ .../garbagecollector/garbagecollector.go | 9 ++- pkg/master/import_known_versions.go | 1 + pkg/master/master.go | 3 + pkg/master/storage_authentication.go | 62 +++++++++++++++ pkg/registry/tokenreview/storage.go | 78 +++++++++++++++++++ .../authenticator/token/webhook/webhook.go | 4 +- .../token/webhook/webhook_test.go | 21 +++-- .../pkg/kubectl/cmd/create/tokenreview.json | 7 ++ test/integration/auth/auth_test.go | 2 +- 29 files changed, 491 insertions(+), 30 deletions(-) rename pkg/apis/{authentication.k8s.io => authentication}/types.go (81%) rename pkg/apis/{authentication.k8s.io => authentication}/v1beta1/types.go (62%) create mode 100644 pkg/client/clientset_generated/internalclientset/typed/authentication/unversioned/fake/fake_generated_expansion.go create mode 100644 pkg/client/clientset_generated/internalclientset/typed/authentication/unversioned/generated_expansion.go create mode 100644 pkg/client/unversioned/authentication.go create mode 100644 pkg/client/unversioned/testclient/fake_tokenreviews.go create mode 100644 pkg/client/unversioned/tokenreviews.go create mode 100644 pkg/master/storage_authentication.go create mode 100644 pkg/registry/tokenreview/storage.go create mode 100644 test/fixtures/pkg/kubectl/cmd/create/tokenreview.json diff --git a/cmd/libs/go2idl/client-gen/main.go b/cmd/libs/go2idl/client-gen/main.go index 490df8b6ad2..7c5d44487c3 100644 --- a/cmd/libs/go2idl/client-gen/main.go +++ b/cmd/libs/go2idl/client-gen/main.go @@ -33,7 +33,7 @@ import ( var ( test = flag.BoolP("test", "t", false, "set this flag to generate the client code for the testdata") - inputVersions = flag.StringSlice("input", []string{"api/", "extensions/", "autoscaling/", "batch/", "rbac/", "certificates/"}, "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\". Default to \"api/,extensions/,autoscaling/,batch/,rbac/\"") + inputVersions = flag.StringSlice("input", []string{"api/", "extensions/", "autoscaling/", "authentication/", "batch/", "rbac/", "certificates/"}, "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\". Default to \"api/,extensions/,autoscaling/,batch/,rbac/\"") includedTypesOverrides = flag.StringSlice("included-types-overrides", []string{}, "list of group/version/type for which client should be generated. By default, client is generated for all types which have genclient=true in types.go. This overrides that. For each groupVersion in this list, only the types mentioned here will be included. The default check of genclient=true will be used for other group versions.") basePath = flag.String("input-base", "k8s.io/kubernetes/pkg/apis", "base path to look for the api group. Default to \"k8s.io/kubernetes/pkg/apis\"") clientsetName = flag.StringP("clientset-name", "n", "internalclientset", "the name of the generated clientset package.") diff --git a/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go b/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go index fd852d65be7..4becf361b65 100644 --- a/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go +++ b/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go @@ -72,6 +72,7 @@ func New() *Generator { `k8s.io/kubernetes/pkg/apis/batch/v1`, `k8s.io/kubernetes/pkg/apis/batch/v2alpha1`, `k8s.io/kubernetes/pkg/apis/apps/v1alpha1`, + `k8s.io/kubernetes/pkg/apis/authentication/v1beta1`, `k8s.io/kubernetes/pkg/apis/rbac/v1alpha1`, `k8s.io/kubernetes/federation/apis/federation/v1beta1`, `k8s.io/kubernetes/pkg/apis/certificates/v1alpha1`, diff --git a/hack/make-rules/test-cmd.sh b/hack/make-rules/test-cmd.sh index d6f65dd9120..cc259f65691 100755 --- a/hack/make-rules/test-cmd.sh +++ b/hack/make-rules/test-cmd.sh @@ -2323,6 +2323,10 @@ __EOF__ # Post-condition: node is schedulable kube::test::get_object_assert "nodes 127.0.0.1" "{{.spec.unschedulable}}" '' + # check webhook token authentication endpoint, kubectl doesn't actually display the returned object so this isn't super useful + # but it proves that works + kubectl create -f test/fixtures/pkg/kubectl/cmd/create/tokenreview.json --validate=false + ##################### # Retrieve multiple # diff --git a/hack/make-rules/test.sh b/hack/make-rules/test.sh index 28a412ddfee..5a292031579 100755 --- a/hack/make-rules/test.sh +++ b/hack/make-rules/test.sh @@ -60,7 +60,7 @@ KUBE_GOVERALLS_BIN=${KUBE_GOVERALLS_BIN:-} # "v1,compute/v1alpha1,experimental/v1alpha2;v1,compute/v2,experimental/v1alpha3" # FIXME: due to current implementation of a test client (see: pkg/api/testapi/testapi.go) # ONLY the last version is tested in each group. -KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,autoscaling/v1,batch/v1,batch/v2alpha1,extensions/v1beta1,apps/v1alpha1,federation/v1beta1,policy/v1alpha1,rbac.authorization.k8s.io/v1alpha1,certificates/v1alpha1"} +KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,autoscaling/v1,authentication.k8s.io/v1beta1,batch/v1,batch/v2alpha1,extensions/v1beta1,apps/v1alpha1,federation/v1beta1,policy/v1alpha1,rbac.authorization.k8s.io/v1alpha1,certificates/v1alpha1"} # once we have multiple group supports # Create a junit-style XML test report in this directory if set. KUBE_JUNIT_REPORT_DIR=${KUBE_JUNIT_REPORT_DIR:-} diff --git a/hack/update-generated-swagger-docs.sh b/hack/update-generated-swagger-docs.sh index 83dc2c1d0f7..f90aab28b67 100755 --- a/hack/update-generated-swagger-docs.sh +++ b/hack/update-generated-swagger-docs.sh @@ -57,7 +57,7 @@ EOF mv "$TMPFILE" "pkg/$(kube::util::group-version-to-pkg-path "${group_version}")/types_swagger_doc_generated.go" } -GROUP_VERSIONS=(unversioned v1 authorization/v1beta1 autoscaling/v1 batch/v1 batch/v2alpha1 extensions/v1beta1 apps/v1alpha1 policy/v1alpha1 rbac/v1alpha1 certificates/v1alpha1) +GROUP_VERSIONS=(unversioned v1 authentication/v1beta1 authorization/v1beta1 autoscaling/v1 batch/v1 batch/v2alpha1 extensions/v1beta1 apps/v1alpha1 policy/v1alpha1 rbac/v1alpha1 certificates/v1alpha1) # To avoid compile errors, remove the currently existing files. for group_version in "${GROUP_VERSIONS[@]}"; do rm -f "pkg/$(kube::util::group-version-to-pkg-path "${group_version}")/types_swagger_doc_generated.go" diff --git a/hack/update-swagger-spec.sh b/hack/update-swagger-spec.sh index ea888bcb54b..4c061ceeafb 100755 --- a/hack/update-swagger-spec.sh +++ b/hack/update-swagger-spec.sh @@ -74,7 +74,7 @@ APISERVER_PID=$! kube::util::wait_for_url "http://127.0.0.1:${API_PORT}/healthz" "apiserver: " SWAGGER_API_PATH="http://127.0.0.1:${API_PORT}/swaggerapi/" -DEFAULT_GROUP_VERSIONS="v1 autoscaling/v1 batch/v1 batch/v2alpha1 extensions/v1beta1 apps/v1alpha1 policy/v1alpha1 rbac.authorization.k8s.io/v1alpha1 certificates/v1alpha1" +DEFAULT_GROUP_VERSIONS="v1 authentication.k8s.io/v1beta1 autoscaling/v1 batch/v1 batch/v2alpha1 extensions/v1beta1 apps/v1alpha1 policy/v1alpha1 rbac.authorization.k8s.io/v1alpha1 certificates/v1alpha1" VERSIONS=${VERSIONS:-$DEFAULT_GROUP_VERSIONS} kube::log::status "Updating " ${SWAGGER_ROOT_DIR} diff --git a/pkg/api/testapi/testapi.go b/pkg/api/testapi/testapi.go index e4557b39871..24ff1fb06a0 100644 --- a/pkg/api/testapi/testapi.go +++ b/pkg/api/testapi/testapi.go @@ -42,6 +42,7 @@ import ( _ "k8s.io/kubernetes/federation/apis/federation/install" _ "k8s.io/kubernetes/pkg/api/install" _ "k8s.io/kubernetes/pkg/apis/apps/install" + _ "k8s.io/kubernetes/pkg/apis/authentication/install" _ "k8s.io/kubernetes/pkg/apis/autoscaling/install" _ "k8s.io/kubernetes/pkg/apis/batch/install" _ "k8s.io/kubernetes/pkg/apis/certificates/install" diff --git a/pkg/apis/authentication/doc.go b/pkg/apis/authentication/doc.go index bf39448c328..88bdf625b73 100644 --- a/pkg/apis/authentication/doc.go +++ b/pkg/apis/authentication/doc.go @@ -16,5 +16,4 @@ limitations under the License. // +k8s:deepcopy-gen=package,register // +groupName=authentication.k8s.io - package authentication // import "k8s.io/kubernetes/pkg/apis/authentication" diff --git a/pkg/apis/authentication.k8s.io/types.go b/pkg/apis/authentication/types.go similarity index 81% rename from pkg/apis/authentication.k8s.io/types.go rename to pkg/apis/authentication/types.go index 20eac2bf750..bf65f1407c0 100644 --- a/pkg/apis/authentication.k8s.io/types.go +++ b/pkg/apis/authentication/types.go @@ -17,12 +17,20 @@ limitations under the License. package authentication import ( + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" ) +// +genclient=true +// +nonNamespaced=true +// +noMethods=true + // TokenReview attempts to authenticate a token to a known user. type TokenReview struct { unversioned.TypeMeta + // ObjectMeta fulfills the meta.ObjectMetaAccessor interface so that the stock + // REST handler paths work + api.ObjectMeta // Spec holds information about the request being evaluated Spec TokenReviewSpec @@ -38,11 +46,14 @@ type TokenReviewSpec struct { } // TokenReviewStatus is the result of the token authentication request. +// This type mirrors the authentication.Token interface type TokenReviewStatus struct { // Authenticated indicates that the token was associated with a known user. Authenticated bool // User is the UserInfo associated with the provided token. User UserInfo + // Error indicates that the token couldn't be checked + Error string } // UserInfo holds the information about the user needed to implement the @@ -57,5 +68,8 @@ type UserInfo struct { // The names of groups this user is a part of. Groups []string // Any additional information provided by the authenticator. - Extra map[string][]string + Extra map[string]ExtraValue } + +// ExtraValue masks the value so protobuf can generate +type ExtraValue []string diff --git a/pkg/apis/authentication/v1beta1/doc.go b/pkg/apis/authentication/v1beta1/doc.go index 9e9ecdf1a42..2b985104cf4 100644 --- a/pkg/apis/authentication/v1beta1/doc.go +++ b/pkg/apis/authentication/v1beta1/doc.go @@ -16,5 +16,5 @@ limitations under the License. // +k8s:deepcopy-gen=package,register // +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/authentication - +// +groupName=authentication.k8s.io package v1beta1 // import "k8s.io/kubernetes/pkg/apis/authentication/v1beta1" diff --git a/pkg/apis/authentication.k8s.io/v1beta1/types.go b/pkg/apis/authentication/v1beta1/types.go similarity index 62% rename from pkg/apis/authentication.k8s.io/v1beta1/types.go rename to pkg/apis/authentication/v1beta1/types.go index ee3220672b0..b2b4471f6c1 100644 --- a/pkg/apis/authentication.k8s.io/v1beta1/types.go +++ b/pkg/apis/authentication/v1beta1/types.go @@ -18,46 +18,58 @@ package v1beta1 import ( "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" ) +// +genclient=true +// +nonNamespaced=true +// +noMethods=true + // TokenReview attempts to authenticate a token to a known user. // Note: TokenReview requests may be cached by the webhook token authenticator // plugin in the kube-apiserver. type TokenReview struct { unversioned.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` // Spec holds information about the request being evaluated - Spec TokenReviewSpec `json:"spec"` + Spec TokenReviewSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"` // Status is filled in by the server and indicates whether the request can be authenticated. - Status TokenReviewStatus `json:"status,omitempty"` + Status TokenReviewStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` } // TokenReviewSpec is a description of the token authentication request. type TokenReviewSpec struct { // Token is the opaque bearer token. - Token string `json:"token,omitempty"` + Token string `json:"token,omitempty" protobuf:"bytes,1,opt,name=token"` } // TokenReviewStatus is the result of the token authentication request. type TokenReviewStatus struct { // Authenticated indicates that the token was associated with a known user. - Authenticated bool `json:"authenticated,omitempty"` + Authenticated bool `json:"authenticated,omitempty" protobuf:"varint,1,opt,name=authenticated"` // User is the UserInfo associated with the provided token. - User UserInfo `json:"user,omitempty"` + User UserInfo `json:"user,omitempty" protobuf:"bytes,2,opt,name=user"` + // Error indicates that the token couldn't be checked + Error string `json:"error,omitempty" protobuf:"bytes,3,opt,name=error"` } // UserInfo holds the information about the user needed to implement the // user.Info interface. type UserInfo struct { // The name that uniquely identifies this user among all active users. - Username string `json:"username,omitempty"` + Username string `json:"username,omitempty" protobuf:"bytes,1,opt,name=username"` // A unique value that identifies this user across time. If this user is // deleted and another user by the same name is added, they will have // different UIDs. - UID string `json:"uid,omitempty"` + UID string `json:"uid,omitempty" protobuf:"bytes,2,opt,name=uid"` // The names of groups this user is a part of. - Groups []string `json:"groups,omitempty"` + Groups []string `json:"groups,omitempty" protobuf:"bytes,3,rep,name=groups"` // Any additional information provided by the authenticator. - Extra map[string][]string `json:"extra,omitempty"` + Extra map[string]ExtraValue `json:"extra,omitempty" protobuf:"bytes,4,rep,name=extra"` } + +// ExtraValue masks the value so protobuf can generate +// +protobuf.nullable=true +type ExtraValue []string diff --git a/pkg/client/clientset_generated/internalclientset/typed/authentication/unversioned/fake/fake_generated_expansion.go b/pkg/client/clientset_generated/internalclientset/typed/authentication/unversioned/fake/fake_generated_expansion.go new file mode 100644 index 00000000000..2be54f43574 --- /dev/null +++ b/pkg/client/clientset_generated/internalclientset/typed/authentication/unversioned/fake/fake_generated_expansion.go @@ -0,0 +1,28 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 fake + +import ( + authenticationapi "k8s.io/kubernetes/pkg/apis/authentication" + + "k8s.io/kubernetes/pkg/client/testing/core" +) + +func (c *FakeTokenReviews) Create(tokenReview *authenticationapi.TokenReview) (result *authenticationapi.TokenReview, err error) { + obj, err := c.Fake.Invokes(core.NewRootCreateAction(authenticationapi.SchemeGroupVersion.WithResource("tokenreviews"), tokenReview), &authenticationapi.TokenReview{}) + return obj.(*authenticationapi.TokenReview), err +} diff --git a/pkg/client/clientset_generated/internalclientset/typed/authentication/unversioned/generated_expansion.go b/pkg/client/clientset_generated/internalclientset/typed/authentication/unversioned/generated_expansion.go new file mode 100644 index 00000000000..1137e520467 --- /dev/null +++ b/pkg/client/clientset_generated/internalclientset/typed/authentication/unversioned/generated_expansion.go @@ -0,0 +1,35 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 unversioned + +import ( + authenticationapi "k8s.io/kubernetes/pkg/apis/authentication" +) + +type TokenReviewExpansion interface { + Create(tokenReview *authenticationapi.TokenReview) (result *authenticationapi.TokenReview, err error) +} + +func (c *tokenReviews) Create(tokenReview *authenticationapi.TokenReview) (result *authenticationapi.TokenReview, err error) { + result = &authenticationapi.TokenReview{} + err = c.client.Post(). + Resource("tokenreviews"). + Body(tokenReview). + Do(). + Into(result) + return +} diff --git a/pkg/client/unversioned/authentication.go b/pkg/client/unversioned/authentication.go new file mode 100644 index 00000000000..6ac7f949fe3 --- /dev/null +++ b/pkg/client/unversioned/authentication.go @@ -0,0 +1,77 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 unversioned + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/apis/authentication" + "k8s.io/kubernetes/pkg/client/restclient" +) + +type AuthenticationInterface interface { + TokenReviewsInterface +} + +// AuthenticationClient is used to interact with Kubernetes authentication features. +type AuthenticationClient struct { + *restclient.RESTClient +} + +func (c *AuthenticationClient) TokenReviews() TokenReviewInterface { + return newTokenReviews(c) +} + +func NewAuthentication(c *restclient.Config) (*AuthenticationClient, error) { + config := *c + if err := setAuthenticationDefaults(&config); err != nil { + return nil, err + } + client, err := restclient.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &AuthenticationClient{client}, nil +} + +func NewAuthenticationOrDie(c *restclient.Config) *AuthenticationClient { + client, err := NewAuthentication(c) + if err != nil { + panic(err) + } + return client +} + +func setAuthenticationDefaults(config *restclient.Config) error { + // if authentication group is not registered, return an error + g, err := registered.Group(authentication.GroupName) + if err != nil { + return err + } + config.APIPath = defaultAPIPath + if config.UserAgent == "" { + config.UserAgent = restclient.DefaultKubernetesUserAgent() + } + // TODO: Unconditionally set the config.Version, until we fix the config. + //if config.Version == "" { + copyGroupVersion := g.GroupVersion + config.GroupVersion = ©GroupVersion + //} + + config.NegotiatedSerializer = api.Codecs + return nil +} diff --git a/pkg/client/unversioned/client.go b/pkg/client/unversioned/client.go index d8047cd4a1c..c87c7ba2bb9 100644 --- a/pkg/client/unversioned/client.go +++ b/pkg/client/unversioned/client.go @@ -46,6 +46,7 @@ type Interface interface { ConfigMapsNamespacer Apps() AppsInterface Autoscaling() AutoscalingInterface + Authentication() AuthenticationInterface Batch() BatchInterface Extensions() ExtensionsInterface Rbac() RbacInterface @@ -120,6 +121,7 @@ func (c *Client) ConfigMaps(namespace string) ConfigMapsInterface { type Client struct { *restclient.RESTClient *AutoscalingClient + *AuthenticationClient *BatchClient *ExtensionsClient *AppsClient @@ -155,6 +157,10 @@ func (c *Client) Autoscaling() AutoscalingInterface { return c.AutoscalingClient } +func (c *Client) Authentication() AuthenticationInterface { + return c.AuthenticationClient +} + func (c *Client) Batch() BatchInterface { return c.BatchClient } diff --git a/pkg/client/unversioned/helper.go b/pkg/client/unversioned/helper.go index 5870bc948b4..e0af69e294e 100644 --- a/pkg/client/unversioned/helper.go +++ b/pkg/client/unversioned/helper.go @@ -23,6 +23,7 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/apps" + "k8s.io/kubernetes/pkg/apis/authentication" "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/certificates" @@ -71,6 +72,15 @@ func New(c *restclient.Config) (*Client, error) { } } + var authenticationClient *AuthenticationClient + if registered.IsRegistered(authentication.GroupName) { + authenticationConfig := *c + authenticationClient, err = NewAuthentication(&authenticationConfig) + if err != nil { + return nil, err + } + } + var batchClient *BatchClient if registered.IsRegistered(batch.GroupName) { batchConfig := *c @@ -123,7 +133,18 @@ func New(c *restclient.Config) (*Client, error) { } } - return &Client{RESTClient: client, AutoscalingClient: autoscalingClient, BatchClient: batchClient, CertificatesClient: certsClient, ExtensionsClient: extensionsClient, DiscoveryClient: discoveryClient, AppsClient: appsClient, PolicyClient: policyClient, RbacClient: rbacClient}, nil + return &Client{ + RESTClient: client, + AppsClient: appsClient, + AuthenticationClient: authenticationClient, + AutoscalingClient: autoscalingClient, + BatchClient: batchClient, + CertificatesClient: certsClient, + DiscoveryClient: discoveryClient, + ExtensionsClient: extensionsClient, + PolicyClient: policyClient, + RbacClient: rbacClient, + }, nil } // MatchesServerVersion queries the server to compares the build version diff --git a/pkg/client/unversioned/import_known_versions.go b/pkg/client/unversioned/import_known_versions.go index 8508573c0a6..2dee333b075 100644 --- a/pkg/client/unversioned/import_known_versions.go +++ b/pkg/client/unversioned/import_known_versions.go @@ -23,7 +23,7 @@ import ( _ "k8s.io/kubernetes/pkg/api/install" "k8s.io/kubernetes/pkg/apimachinery/registered" _ "k8s.io/kubernetes/pkg/apis/apps/install" - _ "k8s.io/kubernetes/pkg/apis/authentication.k8s.io/install" + _ "k8s.io/kubernetes/pkg/apis/authentication/install" _ "k8s.io/kubernetes/pkg/apis/authorization/install" _ "k8s.io/kubernetes/pkg/apis/autoscaling/install" _ "k8s.io/kubernetes/pkg/apis/batch/install" diff --git a/pkg/client/unversioned/testclient/fake_tokenreviews.go b/pkg/client/unversioned/testclient/fake_tokenreviews.go new file mode 100644 index 00000000000..c0e6c910412 --- /dev/null +++ b/pkg/client/unversioned/testclient/fake_tokenreviews.go @@ -0,0 +1,35 @@ +/* +Copyright 2015 The Kubernetes Authors. + +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 testclient + +import ( + "k8s.io/kubernetes/pkg/apis/authentication" +) + +// FakeTokenReviews implements ClusterRoleInterface +type FakeTokenReviews struct { + Fake *FakeAuthentication +} + +func (c *FakeTokenReviews) Create(review *authentication.TokenReview) (*authentication.TokenReview, error) { + obj, err := c.Fake.Invokes(NewRootCreateAction("tokenreviews", review), review) + if obj == nil { + return nil, err + } + + return obj.(*authentication.TokenReview), err +} diff --git a/pkg/client/unversioned/testclient/testclient.go b/pkg/client/unversioned/testclient/testclient.go index 6d03075b074..3ca670f8641 100644 --- a/pkg/client/unversioned/testclient/testclient.go +++ b/pkg/client/unversioned/testclient/testclient.go @@ -313,6 +313,10 @@ func (c *Fake) Rbac() client.RbacInterface { return &FakeRbac{Fake: c} } +func (c *Fake) Authentication() client.AuthenticationInterface { + return &FakeAuthentication{Fake: c} +} + // SwaggerSchema returns an empty swagger.ApiDeclaration for testing func (c *Fake) SwaggerSchema(version unversioned.GroupVersion) (*swagger.ApiDeclaration, error) { action := ActionImpl{} @@ -353,6 +357,18 @@ func (c *FakeAutoscaling) HorizontalPodAutoscalers(namespace string) client.Hori return &FakeHorizontalPodAutoscalers{Fake: c, Namespace: namespace} } +func NewSimpleFakeAuthentication(objects ...runtime.Object) *FakeAuthentication { + return &FakeAuthentication{Fake: NewSimpleFake(objects...)} +} + +type FakeAuthentication struct { + *Fake +} + +func (c *FakeAuthentication) TokenReviews() client.TokenReviewInterface { + return &FakeTokenReviews{Fake: c} +} + // NewSimpleFakeBatch returns a client that will respond with the provided objects func NewSimpleFakeBatch(objects ...runtime.Object) *FakeBatch { return &FakeBatch{Fake: NewSimpleFake(objects...)} diff --git a/pkg/client/unversioned/tokenreviews.go b/pkg/client/unversioned/tokenreviews.go new file mode 100644 index 00000000000..91c6bb60034 --- /dev/null +++ b/pkg/client/unversioned/tokenreviews.go @@ -0,0 +1,49 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 unversioned + +import ( + "k8s.io/kubernetes/pkg/apis/authentication" +) + +// TokenReviews has methods to work with TokenReview resources in a namespace +type TokenReviewsInterface interface { + TokenReviews() TokenReviewInterface +} + +// TokenReviewInterface has methods to work with TokenReview resources. +type TokenReviewInterface interface { + Create(tokenReview *authentication.TokenReview) (*authentication.TokenReview, error) +} + +// tokenReviews implements TokenReviewsNamespacer interface +type tokenReviews struct { + client *AuthenticationClient +} + +// newTokenReviews returns a tokenReviews +func newTokenReviews(c *AuthenticationClient) *tokenReviews { + return &tokenReviews{ + client: c, + } +} + +func (c *tokenReviews) Create(obj *authentication.TokenReview) (result *authentication.TokenReview, err error) { + result = &authentication.TokenReview{} + err = c.client.Post().Resource("tokenreviews").Body(obj).Do().Into(result) + return +} diff --git a/pkg/controller/garbagecollector/garbagecollector.go b/pkg/controller/garbagecollector/garbagecollector.go index d0576407aec..3c2857380ed 100644 --- a/pkg/controller/garbagecollector/garbagecollector.go +++ b/pkg/controller/garbagecollector/garbagecollector.go @@ -503,10 +503,11 @@ func monitorFor(p *Propagator, clientPool dynamic.ClientPool, resource unversion } var ignoredResources = map[unversioned.GroupVersionResource]struct{}{ - unversioned.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "replicationcontrollers"}: {}, - unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "bindings"}: {}, - unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "componentstatuses"}: {}, - unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "events"}: {}, + unversioned.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "replicationcontrollers"}: {}, + unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "bindings"}: {}, + unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "componentstatuses"}: {}, + unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "events"}: {}, + unversioned.GroupVersionResource{Group: "authentication.k8s.io", Version: "v1beta1", Resource: "tokenreviews"}: {}, } func NewGarbageCollector(clientPool dynamic.ClientPool, resources []unversioned.GroupVersionResource) (*GarbageCollector, error) { diff --git a/pkg/master/import_known_versions.go b/pkg/master/import_known_versions.go index 21873a0afb8..b670c6f6f1a 100644 --- a/pkg/master/import_known_versions.go +++ b/pkg/master/import_known_versions.go @@ -23,6 +23,7 @@ import ( _ "k8s.io/kubernetes/pkg/api/install" "k8s.io/kubernetes/pkg/apimachinery/registered" _ "k8s.io/kubernetes/pkg/apis/apps/install" + _ "k8s.io/kubernetes/pkg/apis/authentication/install" _ "k8s.io/kubernetes/pkg/apis/authorization/install" _ "k8s.io/kubernetes/pkg/apis/autoscaling/install" _ "k8s.io/kubernetes/pkg/apis/batch/install" diff --git a/pkg/master/master.go b/pkg/master/master.go index 20d97ff2d0f..244d21647ee 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -34,6 +34,7 @@ import ( apiv1 "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/apimachinery/registered" appsapi "k8s.io/kubernetes/pkg/apis/apps/v1alpha1" + authenticationv1beta1 "k8s.io/kubernetes/pkg/apis/authentication/v1beta1" "k8s.io/kubernetes/pkg/apis/autoscaling" autoscalingapiv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1" "k8s.io/kubernetes/pkg/apis/batch" @@ -200,6 +201,7 @@ func New(c *Config) (*Master, error) { } c.RESTStorageProviders[policy.GroupName] = PolicyRESTStorageProvider{} c.RESTStorageProviders[rbac.GroupName] = RBACRESTStorageProvider{AuthorizerRBACSuperUser: c.AuthorizerRBACSuperUser} + c.RESTStorageProviders[authenticationv1beta1.GroupName] = AuthenticationRESTStorageProvider{Authenticator: c.Authenticator} m.InstallAPIs(c) // TODO: Attempt clean shutdown? @@ -754,6 +756,7 @@ func DefaultAPIResourceConfigSource() *genericapiserver.ResourceConfig { apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion, batchapiv1.SchemeGroupVersion, + authenticationv1beta1.SchemeGroupVersion, autoscalingapiv1.SchemeGroupVersion, appsapi.SchemeGroupVersion, policyapiv1alpha1.SchemeGroupVersion, diff --git a/pkg/master/storage_authentication.go b/pkg/master/storage_authentication.go new file mode 100644 index 00000000000..2e28ca77863 --- /dev/null +++ b/pkg/master/storage_authentication.go @@ -0,0 +1,62 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 master + +import ( + "k8s.io/kubernetes/pkg/api/rest" + "k8s.io/kubernetes/pkg/apis/authentication" + authenticationv1beta1 "k8s.io/kubernetes/pkg/apis/authentication/v1beta1" + "k8s.io/kubernetes/pkg/auth/authenticator" + "k8s.io/kubernetes/pkg/genericapiserver" + "k8s.io/kubernetes/pkg/registry/tokenreview" +) + +type AuthenticationRESTStorageProvider struct { + Authenticator authenticator.Request +} + +var _ RESTStorageProvider = &AuthenticationRESTStorageProvider{} + +func (p AuthenticationRESTStorageProvider) NewRESTStorage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) { + // TODO figure out how to make the swagger generation stable, while allowing this endpoint to be disabled. + // if p.Authenticator == nil { + // return genericapiserver.APIGroupInfo{}, false + // } + + apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(authentication.GroupName) + + if apiResourceConfigSource.AnyResourcesForVersionEnabled(authenticationv1beta1.SchemeGroupVersion) { + apiGroupInfo.VersionedResourcesStorageMap[authenticationv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter) + apiGroupInfo.GroupMeta.GroupVersion = authenticationv1beta1.SchemeGroupVersion + } + + return apiGroupInfo, true +} + +func (p AuthenticationRESTStorageProvider) v1beta1Storage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter RESTOptionsGetter) map[string]rest.Storage { + version := authenticationv1beta1.SchemeGroupVersion + + storage := map[string]rest.Storage{} + if apiResourceConfigSource.AnyResourcesForVersionEnabled(authenticationv1beta1.SchemeGroupVersion) { + if apiResourceConfigSource.ResourceEnabled(version.WithResource("tokenreviews")) { + tokenReviewStorage := tokenreview.NewREST(p.Authenticator) + storage["tokenreviews"] = tokenReviewStorage + } + } + + return storage +} diff --git a/pkg/registry/tokenreview/storage.go b/pkg/registry/tokenreview/storage.go new file mode 100644 index 00000000000..825b5f1e85f --- /dev/null +++ b/pkg/registry/tokenreview/storage.go @@ -0,0 +1,78 @@ +/* +Copyright 2015 The Kubernetes Authors. + +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 tokenreview + +import ( + "fmt" + "net/http" + + "k8s.io/kubernetes/pkg/api" + apierrors "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/apis/authentication" + "k8s.io/kubernetes/pkg/auth/authenticator" + "k8s.io/kubernetes/pkg/runtime" +) + +type REST struct { + tokenAuthenticator authenticator.Request +} + +func NewREST(tokenAuthenticator authenticator.Request) *REST { + return &REST{tokenAuthenticator: tokenAuthenticator} +} + +func (r *REST) New() runtime.Object { + return &authentication.TokenReview{} +} + +func (r *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) { + tokenReview, ok := obj.(*authentication.TokenReview) + if !ok { + return nil, apierrors.NewBadRequest(fmt.Sprintf("not a TokenReview: %#v", obj)) + } + namespace := api.NamespaceValue(ctx) + if len(namespace) != 0 { + return nil, apierrors.NewBadRequest(fmt.Sprintf("namespace is not allowed on this type: %v", namespace)) + } + + if r.tokenAuthenticator == nil { + return tokenReview, nil + } + + // create a header that contains nothing but the token + fakeReq := &http.Request{Header: http.Header{}} + fakeReq.Header.Add("Authorization", "Bearer "+tokenReview.Spec.Token) + + tokenUser, ok, err := r.tokenAuthenticator.AuthenticateRequest(fakeReq) + tokenReview.Status.Authenticated = ok + if err != nil { + tokenReview.Status.Error = err.Error() + } + if tokenUser != nil { + tokenReview.Status.User = authentication.UserInfo{ + Username: tokenUser.GetName(), + UID: tokenUser.GetUID(), + Groups: tokenUser.GetGroups(), + Extra: map[string]authentication.ExtraValue{}, + } + for k, v := range tokenUser.GetExtra() { + tokenReview.Status.User.Extra[k] = authentication.ExtraValue(v) + } + } + + return tokenReview, nil +} diff --git a/plugin/pkg/auth/authenticator/token/webhook/webhook.go b/plugin/pkg/auth/authenticator/token/webhook/webhook.go index 2c55e1b1566..67c02dac73d 100644 --- a/plugin/pkg/auth/authenticator/token/webhook/webhook.go +++ b/plugin/pkg/auth/authenticator/token/webhook/webhook.go @@ -22,14 +22,14 @@ import ( "time" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/authentication.k8s.io/v1beta1" + "k8s.io/kubernetes/pkg/apis/authentication/v1beta1" "k8s.io/kubernetes/pkg/auth/authenticator" "k8s.io/kubernetes/pkg/auth/user" "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/util/cache" "k8s.io/kubernetes/plugin/pkg/webhook" - _ "k8s.io/kubernetes/pkg/apis/authentication.k8s.io/install" + _ "k8s.io/kubernetes/pkg/apis/authentication/install" ) var ( diff --git a/plugin/pkg/auth/authenticator/token/webhook/webhook_test.go b/plugin/pkg/auth/authenticator/token/webhook/webhook_test.go index e960ab05bdc..4a988657b0a 100644 --- a/plugin/pkg/auth/authenticator/token/webhook/webhook_test.go +++ b/plugin/pkg/auth/authenticator/token/webhook/webhook_test.go @@ -30,7 +30,7 @@ import ( "time" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/authentication.k8s.io/v1beta1" + "k8s.io/kubernetes/pkg/apis/authentication/v1beta1" "k8s.io/kubernetes/pkg/auth/user" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/v1" ) @@ -353,10 +353,21 @@ func TestWebhookTokenAuthenticator(t *testing.T) { type authenticationUserInfo v1beta1.UserInfo -func (a *authenticationUserInfo) GetName() string { return a.Username } -func (a *authenticationUserInfo) GetUID() string { return a.UID } -func (a *authenticationUserInfo) GetGroups() []string { return a.Groups } -func (a *authenticationUserInfo) GetExtra() map[string][]string { return a.Extra } +func (a *authenticationUserInfo) GetName() string { return a.Username } +func (a *authenticationUserInfo) GetUID() string { return a.UID } +func (a *authenticationUserInfo) GetGroups() []string { return a.Groups } + +func (a *authenticationUserInfo) GetExtra() map[string][]string { + if a.Extra == nil { + return nil + } + ret := map[string][]string{} + for k, v := range a.Extra { + ret[k] = []string(v) + } + + return ret +} // Ensure v1beta1.UserInfo contains the fields necessary to implement the // user.Info interface. diff --git a/test/fixtures/pkg/kubectl/cmd/create/tokenreview.json b/test/fixtures/pkg/kubectl/cmd/create/tokenreview.json new file mode 100644 index 00000000000..0a4e60ca773 --- /dev/null +++ b/test/fixtures/pkg/kubectl/cmd/create/tokenreview.json @@ -0,0 +1,7 @@ +{ + "apiVersion": "authentication.k8s.io/v1beta1", + "kind": "TokenReview", + "spec": { + "token": "test-token" + } +} \ No newline at end of file diff --git a/test/integration/auth/auth_test.go b/test/integration/auth/auth_test.go index 4370becd0cb..424d3bf7948 100644 --- a/test/integration/auth/auth_test.go +++ b/test/integration/auth/auth_test.go @@ -38,7 +38,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" - authenticationv1beta1 "k8s.io/kubernetes/pkg/apis/authentication.k8s.io/v1beta1" + authenticationv1beta1 "k8s.io/kubernetes/pkg/apis/authentication/v1beta1" "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apiserver"