add subjectaccessreviews resource

pull/6/head
deads2k 2016-02-03 13:08:10 -05:00
parent 5f3cc4a941
commit 32920b5617
36 changed files with 914 additions and 43 deletions

View File

@ -32,8 +32,17 @@ 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/", "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/\"")
test = flag.BoolP("test", "t", false, "set this flag to generate the client code for the testdata")
inputVersions = flag.StringSlice("input", []string{
"api/",
"authentication/",
"authorization/",
"autoscaling/",
"batch/",
"certificates/",
"extensions/",
"rbac/",
}, "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.")

View File

@ -69,6 +69,7 @@ func New() *Generator {
`k8s.io/kubernetes/pkg/apis/policy/v1alpha1`,
`k8s.io/kubernetes/pkg/apis/extensions/v1beta1`,
`k8s.io/kubernetes/pkg/apis/autoscaling/v1`,
`k8s.io/kubernetes/pkg/apis/authorization/v1beta1`,
`k8s.io/kubernetes/pkg/apis/batch/v1`,
`k8s.io/kubernetes/pkg/apis/batch/v2alpha1`,
`k8s.io/kubernetes/pkg/apis/apps/v1alpha1`,

View File

@ -2328,6 +2328,26 @@ __EOF__
kubectl create -f test/fixtures/pkg/kubectl/cmd/create/tokenreview.json --validate=false
########################
# authorization.k8s.io #
########################
# check remote authorization 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/sar.json --validate=false
SAR_RESULT_FILE="${KUBE_TEMP}/sar-result.json"
curl -k -H "Content-Type:" http://localhost:8080/apis/authorization.k8s.io/v1beta1/subjectaccessreviews -XPOST -d @test/fixtures/pkg/kubectl/cmd/create/sar.json > "${SAR_RESULT_FILE}"
if grep -q '"allowed": true' "${SAR_RESULT_FILE}"; then
kube::log::status "\"authorization.k8s.io/subjectaccessreviews\" returns as expected: $(cat "${SAR_RESULT_FILE}")"
else
kube::log::status "\"authorization.k8s.io/subjectaccessreviews\" does not return as expected: $(cat "${SAR_RESULT_FILE}")"
exit 1
fi
rm "${SAR_RESULT_FILE}"
#####################
# Retrieve multiple #
#####################

View File

@ -27,7 +27,7 @@ source "${KUBE_ROOT}/hack/lib/init.sh"
# KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1"}
# 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,apps/v1alpha1,policy/v1alpha1,extensions/v1beta1,rbac.authorization.k8s.io/v1alpha1,certificates/v1alpha1"}
KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,apps/v1alpha1,policy/v1alpha1,extensions/v1beta1,rbac.authorization.k8s.io/v1alpha1,certificates/v1alpha1"}
# Give integration tests longer to run
# TODO: allow a larger value to be passed in

View File

@ -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,authentication.k8s.io/v1beta1,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,apps/v1alpha1,authentication.k8s.io/v1beta1,authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,batch/v2alpha1,certificates/v1alpha1,extensions/v1beta1,federation/v1beta1,policy/v1alpha1,rbac.authorization.k8s.io/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:-}

View File

@ -39,7 +39,7 @@ setgen=$(kube::util::find-binary "set-gen")
# update- and verify- scripts.
${clientgen} "$@"
${clientgen} -t "$@"
${clientgen} --clientset-name="release_1_4" --input="api/v1,extensions/v1beta1,autoscaling/v1,batch/v1"
${clientgen} --clientset-name="release_1_4" --input="api/v1,authorization/v1beta1,autoscaling/v1,batch/v1,extensions/v1beta1"
# Clientgen for federation clientset.
${clientgen} --clientset-name=federation_internalclientset --clientset-path=k8s.io/kubernetes/federation/client/clientset_generated --input="../../federation/apis/federation/","api/","extensions/" --included-types-overrides="api/Service,api/Namespace,extensions/ReplicaSet,api/Secret" "$@"
${clientgen} --clientset-name=federation_release_1_4 --clientset-path=k8s.io/kubernetes/federation/client/clientset_generated --input="../../federation/apis/federation/v1beta1","api/v1","extensions/v1beta1" --included-types-overrides="api/v1/Service,api/v1/Namespace,extensions/v1beta1/ReplicaSet,api/v1/Secret" "$@"

View File

@ -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 authentication.k8s.io/v1beta1 autoscaling/v1 batch/v1 batch/v2alpha1 extensions/v1beta1 apps/v1alpha1 policy/v1alpha1 rbac.authorization.k8s.io/v1alpha1 certificates/v1alpha1"
DEFAULT_GROUP_VERSIONS="v1 apps/v1alpha1 authentication.k8s.io/v1beta1 authorization.k8s.io/v1beta1 autoscaling/v1 batch/v1 batch/v2alpha1 extensions/v1beta1 certificates/v1alpha1 policy/v1alpha1 rbac.authorization.k8s.io/v1alpha1"
VERSIONS=${VERSIONS:-$DEFAULT_GROUP_VERSIONS}
kube::log::status "Updating " ${SWAGGER_ROOT_DIR}

View File

@ -43,6 +43,7 @@ import (
_ "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/authorization/install"
_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
_ "k8s.io/kubernetes/pkg/apis/batch/install"
_ "k8s.io/kubernetes/pkg/apis/certificates/install"

View File

@ -16,4 +16,5 @@ limitations under the License.
// +k8s:deepcopy-gen=package,register
// +groupName=authorization.k8s.io
package authorization // import "k8s.io/kubernetes/pkg/apis/authorization"

View File

@ -17,13 +17,19 @@ limitations under the License.
package authorization
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
)
// +genclient=true
// +nonNamespaced=true
// +noMethods=true
// SubjectAccessReview checks whether or not a user or group can perform an action. Not filling in a
// spec.namespace means "in all namespaces".
type SubjectAccessReview struct {
unversioned.TypeMeta
api.ObjectMeta
// Spec holds information about the request being evaluated
Spec SubjectAccessReviewSpec
@ -37,6 +43,7 @@ type SubjectAccessReview struct {
// to check whether they can perform an action
type SelfSubjectAccessReview struct {
unversioned.TypeMeta
api.ObjectMeta
// Spec holds information about the request being evaluated.
Spec SelfSubjectAccessReviewSpec
@ -50,6 +57,7 @@ type SelfSubjectAccessReview struct {
// checking.
type LocalSubjectAccessReview struct {
unversioned.TypeMeta
api.ObjectMeta
// Spec holds information about the request being evaluated. spec.namespace must be equal to the namespace
// you made the request against. If empty, it is defaulted.
@ -103,9 +111,13 @@ type SubjectAccessReviewSpec struct {
Groups []string
// Extra corresponds to the user.Info.GetExtra() method from the authenticator. Since that is input to the authorizer
// it needs a reflection here.
Extra map[string][]string
Extra map[string]ExtraValue
}
// ExtraValue masks the value so protobuf can generate
// +protobuf.nullable=true
type ExtraValue []string
// SelfSubjectAccessReviewSpec is a description of the access request. Exactly one of ResourceAttributes
// and NonResourceAttributes must be set
type SelfSubjectAccessReviewSpec struct {
@ -121,4 +133,8 @@ type SubjectAccessReviewStatus struct {
Allowed bool
// Reason is optional. It indicates why a request was allowed or denied.
Reason string
// EvaluationError is an indication that some error occurred during the authorization check.
// It is entirely possible to get an error and be able to continue determine authorization status in spite of it.
// For instance, RBAC can be missing a role, but enough roles are still present and bound to reason about the request.
EvaluationError string
}

View File

@ -17,4 +17,5 @@ limitations under the License.
// +k8s:deepcopy-gen=package,register
// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/authorization
// +groupName=authorization.k8s.io
package v1beta1 // import "k8s.io/kubernetes/pkg/apis/authorization/v1beta1"

View File

@ -18,7 +18,9 @@ package v1beta1
import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/runtime"
versionedwatch "k8s.io/kubernetes/pkg/watch/versioned"
)
// GroupName is the group name use in this package
@ -37,10 +39,15 @@ func AddToScheme(scheme *runtime.Scheme) {
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) {
scheme.AddKnownTypes(SchemeGroupVersion,
&v1.ListOptions{},
&v1.DeleteOptions{},
&SelfSubjectAccessReview{},
&SubjectAccessReview{},
&LocalSubjectAccessReview{},
)
versionedwatch.AddToGroupVersion(scheme, SchemeGroupVersion)
}
func (obj *LocalSubjectAccessReview) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }

View File

@ -17,18 +17,26 @@ limitations under the License.
package v1beta1
import (
"fmt"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
)
// +genclient=true
// +nonNamespaced=true
// +noMethods=true
// SubjectAccessReview checks whether or not a user or group can perform an action.
type SubjectAccessReview 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 SubjectAccessReviewSpec `json:"spec"`
Spec SubjectAccessReviewSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"`
// Status is filled in by the server and indicates whether the request is allowed or not
Status SubjectAccessReviewStatus `json:"status,omitempty"`
Status SubjectAccessReviewStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
// SelfSubjectAccessReview checks whether or the current user can perform an action. Not filling in a
@ -36,12 +44,13 @@ type SubjectAccessReview struct {
// to check whether they can perform an action
type SelfSubjectAccessReview struct {
unversioned.TypeMeta `json:",inline"`
v1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Spec holds information about the request being evaluated. user and groups must be empty
Spec SelfSubjectAccessReviewSpec `json:"spec"`
Spec SelfSubjectAccessReviewSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"`
// Status is filled in by the server and indicates whether the request is allowed or not
Status SubjectAccessReviewStatus `json:"status,omitempty"`
Status SubjectAccessReviewStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
// LocalSubjectAccessReview checks whether or not a user or group can perform an action in a given namespace.
@ -49,13 +58,14 @@ type SelfSubjectAccessReview struct {
// checking.
type LocalSubjectAccessReview 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.namespace must be equal to the namespace
// you made the request against. If empty, it is defaulted.
Spec SubjectAccessReviewSpec `json:"spec"`
Spec SubjectAccessReviewSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"`
// Status is filled in by the server and indicates whether the request is allowed or not
Status SubjectAccessReviewStatus `json:"status,omitempty"`
Status SubjectAccessReviewStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
// ResourceAttributes includes the authorization attributes available for resource requests to the Authorizer interface
@ -64,60 +74,73 @@ type ResourceAttributes struct {
// "" (empty) is defaulted for LocalSubjectAccessReviews
// "" (empty) is empty for cluster-scoped resources
// "" (empty) means "all" for namespace scoped resources from a SubjectAccessReview or SelfSubjectAccessReview
Namespace string `json:"namespace,omitempty"`
Namespace string `json:"namespace,omitempty" protobuf:"bytes,1,opt,name=namespace"`
// Verb is a kubernetes resource API verb, like: get, list, watch, create, update, delete, proxy. "*" means all.
Verb string `json:"verb,omitempty"`
Verb string `json:"verb,omitempty" protobuf:"bytes,2,opt,name=verb"`
// Group is the API Group of the Resource. "*" means all.
Group string `json:"group,omitempty"`
Group string `json:"group,omitempty" protobuf:"bytes,3,opt,name=group"`
// Version is the API Version of the Resource. "*" means all.
Version string `json:"version,omitempty"`
Version string `json:"version,omitempty" protobuf:"bytes,4,opt,name=version"`
// Resource is one of the existing resource types. "*" means all.
Resource string `json:"resource,omitempty"`
Resource string `json:"resource,omitempty" protobuf:"bytes,5,opt,name=resource"`
// Subresource is one of the existing resource types. "" means none.
Subresource string `json:"subresource,omitempty"`
Subresource string `json:"subresource,omitempty" protobuf:"bytes,6,opt,name=subresource"`
// Name is the name of the resource being requested for a "get" or deleted for a "delete". "" (empty) means all.
Name string `json:"name,omitempty"`
Name string `json:"name,omitempty" protobuf:"bytes,7,opt,name=name"`
}
// NonResourceAttributes includes the authorization attributes available for non-resource requests to the Authorizer interface
type NonResourceAttributes struct {
// Path is the URL path of the request
Path string `json:"path,omitempty"`
Path string `json:"path,omitempty" protobuf:"bytes,1,opt,name=path"`
// Verb is the standard HTTP verb
Verb string `json:"verb,omitempty"`
Verb string `json:"verb,omitempty" protobuf:"bytes,2,opt,name=verb"`
}
// SubjectAccessReviewSpec is a description of the access request. Exactly one of ResourceAuthorizationAttributes
// and NonResourceAuthorizationAttributes must be set
type SubjectAccessReviewSpec struct {
// ResourceAuthorizationAttributes describes information for a resource access request
ResourceAttributes *ResourceAttributes `json:"resourceAttributes,omitempty"`
ResourceAttributes *ResourceAttributes `json:"resourceAttributes,omitempty" protobuf:"bytes,1,opt,name=resourceAttributes"`
// NonResourceAttributes describes information for a non-resource access request
NonResourceAttributes *NonResourceAttributes `json:"nonResourceAttributes,omitempty"`
NonResourceAttributes *NonResourceAttributes `json:"nonResourceAttributes,omitempty" protobuf:"bytes,2,opt,name=nonResourceAttributes"`
// User is the user you're testing for.
// If you specify "User" but not "Group", then is it interpreted as "What if User were not a member of any groups
User string `json:"user,omitempty"`
User string `json:"user,omitempty" protobuf:"bytes,3,opt,name=verb"`
// Groups is the groups you're testing for.
Groups []string `json:"group,omitempty"`
Groups []string `json:"group,omitempty" protobuf:"bytes,4,rep,name=group"`
// Extra corresponds to the user.Info.GetExtra() method from the authenticator. Since that is input to the authorizer
// it needs a reflection here.
Extra map[string][]string `json:"extra,omitempty"`
Extra map[string]ExtraValue `json:"extra,omitempty" protobuf:"bytes,5,rep,name=extra"`
}
// ExtraValue masks the value so protobuf can generate
// +protobuf.nullable=true
// +protobuf.options.(gogoproto.goproto_stringer)=false
type ExtraValue []string
func (t ExtraValue) String() string {
return fmt.Sprintf("%v", []string(t))
}
// SelfSubjectAccessReviewSpec is a description of the access request. Exactly one of ResourceAuthorizationAttributes
// and NonResourceAuthorizationAttributes must be set
type SelfSubjectAccessReviewSpec struct {
// ResourceAuthorizationAttributes describes information for a resource access request
ResourceAttributes *ResourceAttributes `json:"resourceAttributes,omitempty"`
ResourceAttributes *ResourceAttributes `json:"resourceAttributes,omitempty" protobuf:"bytes,1,opt,name=resourceAttributes"`
// NonResourceAttributes describes information for a non-resource access request
NonResourceAttributes *NonResourceAttributes `json:"nonResourceAttributes,omitempty"`
NonResourceAttributes *NonResourceAttributes `json:"nonResourceAttributes,omitempty" protobuf:"bytes,2,opt,name=nonResourceAttributes"`
}
// SubjectAccessReviewStatus
type SubjectAccessReviewStatus struct {
// Allowed is required. True if the action would be allowed, false otherwise.
Allowed bool `json:"allowed"`
Allowed bool `json:"allowed" protobuf:"varint,1,opt,name=allowed"`
// Reason is optional. It indicates why a request was allowed or denied.
Reason string `json:"reason,omitempty"`
Reason string `json:"reason,omitempty" protobuf:"bytes,2,opt,name=reason"`
// EvaluationError is an indication that some error occurred during the authorization check.
// It is entirely possible to get an error and be able to continue determine authorization status in spite of it.
// For instance, RBAC can be missing a role, but enough roles are still present and bound to reason about the request.
EvaluationError string `json:"evaluationError,omitempty" protobuf:"bytes,3,opt,name=evaluationError"`
}

View File

@ -54,6 +54,10 @@ func autoConvert_v1beta1_LocalSubjectAccessReview_To_authorization_LocalSubjectA
if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err
}
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.ObjectMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if err := Convert_v1beta1_SubjectAccessReviewSpec_To_authorization_SubjectAccessReviewSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
@ -71,6 +75,10 @@ func autoConvert_authorization_LocalSubjectAccessReview_To_v1beta1_LocalSubjectA
if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err
}
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.ObjectMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if err := Convert_authorization_SubjectAccessReviewSpec_To_v1beta1_SubjectAccessReviewSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
@ -138,6 +146,10 @@ func autoConvert_v1beta1_SelfSubjectAccessReview_To_authorization_SelfSubjectAcc
if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err
}
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.ObjectMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if err := Convert_v1beta1_SelfSubjectAccessReviewSpec_To_authorization_SelfSubjectAccessReviewSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
@ -155,6 +167,10 @@ func autoConvert_authorization_SelfSubjectAccessReview_To_v1beta1_SelfSubjectAcc
if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err
}
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.ObjectMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if err := Convert_authorization_SelfSubjectAccessReviewSpec_To_v1beta1_SelfSubjectAccessReviewSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
@ -224,6 +240,10 @@ func autoConvert_v1beta1_SubjectAccessReview_To_authorization_SubjectAccessRevie
if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err
}
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.ObjectMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if err := Convert_v1beta1_SubjectAccessReviewSpec_To_authorization_SubjectAccessReviewSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
@ -241,6 +261,10 @@ func autoConvert_authorization_SubjectAccessReview_To_v1beta1_SubjectAccessRevie
if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err
}
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.ObjectMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if err := Convert_authorization_SubjectAccessReviewSpec_To_v1beta1_SubjectAccessReviewSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
@ -275,7 +299,20 @@ func autoConvert_v1beta1_SubjectAccessReviewSpec_To_authorization_SubjectAccessR
}
out.User = in.User
out.Groups = in.Groups
out.Extra = in.Extra
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make(map[string]authorization.ExtraValue, len(*in))
for key, val := range *in {
newVal := new(authorization.ExtraValue)
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&val, newVal, 0); err != nil {
return err
}
(*out)[key] = *newVal
}
} else {
out.Extra = nil
}
return nil
}
@ -304,7 +341,20 @@ func autoConvert_authorization_SubjectAccessReviewSpec_To_v1beta1_SubjectAccessR
}
out.User = in.User
out.Groups = in.Groups
out.Extra = in.Extra
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make(map[string]ExtraValue, len(*in))
for key, val := range *in {
newVal := new(ExtraValue)
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&val, newVal, 0); err != nil {
return err
}
(*out)[key] = *newVal
}
} else {
out.Extra = nil
}
return nil
}
@ -315,6 +365,7 @@ func Convert_authorization_SubjectAccessReviewSpec_To_v1beta1_SubjectAccessRevie
func autoConvert_v1beta1_SubjectAccessReviewStatus_To_authorization_SubjectAccessReviewStatus(in *SubjectAccessReviewStatus, out *authorization.SubjectAccessReviewStatus, s conversion.Scope) error {
out.Allowed = in.Allowed
out.Reason = in.Reason
out.EvaluationError = in.EvaluationError
return nil
}
@ -325,6 +376,7 @@ func Convert_v1beta1_SubjectAccessReviewStatus_To_authorization_SubjectAccessRev
func autoConvert_authorization_SubjectAccessReviewStatus_To_v1beta1_SubjectAccessReviewStatus(in *authorization.SubjectAccessReviewStatus, out *SubjectAccessReviewStatus, s conversion.Scope) error {
out.Allowed = in.Allowed
out.Reason = in.Reason
out.EvaluationError = in.EvaluationError
return nil
}

View File

@ -22,6 +22,7 @@ package v1beta1
import (
api "k8s.io/kubernetes/pkg/api"
v1 "k8s.io/kubernetes/pkg/api/v1"
conversion "k8s.io/kubernetes/pkg/conversion"
reflect "reflect"
)
@ -47,6 +48,9 @@ func DeepCopy_v1beta1_LocalSubjectAccessReview(in interface{}, out interface{},
in := in.(*LocalSubjectAccessReview)
out := out.(*LocalSubjectAccessReview)
out.TypeMeta = in.TypeMeta
if err := v1.DeepCopy_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if err := DeepCopy_v1beta1_SubjectAccessReviewSpec(&in.Spec, &out.Spec, c); err != nil {
return err
}
@ -85,6 +89,9 @@ func DeepCopy_v1beta1_SelfSubjectAccessReview(in interface{}, out interface{}, c
in := in.(*SelfSubjectAccessReview)
out := out.(*SelfSubjectAccessReview)
out.TypeMeta = in.TypeMeta
if err := v1.DeepCopy_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if err := DeepCopy_v1beta1_SelfSubjectAccessReviewSpec(&in.Spec, &out.Spec, c); err != nil {
return err
}
@ -120,6 +127,9 @@ func DeepCopy_v1beta1_SubjectAccessReview(in interface{}, out interface{}, c *co
in := in.(*SubjectAccessReview)
out := out.(*SubjectAccessReview)
out.TypeMeta = in.TypeMeta
if err := v1.DeepCopy_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if err := DeepCopy_v1beta1_SubjectAccessReviewSpec(&in.Spec, &out.Spec, c); err != nil {
return err
}
@ -156,12 +166,12 @@ func DeepCopy_v1beta1_SubjectAccessReviewSpec(in interface{}, out interface{}, c
}
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make(map[string][]string)
*out = make(map[string]ExtraValue)
for key, val := range *in {
if newVal, err := c.DeepCopy(&val); err != nil {
return err
} else {
(*out)[key] = *newVal.(*[]string)
(*out)[key] = *newVal.(*ExtraValue)
}
}
} else {
@ -177,6 +187,7 @@ func DeepCopy_v1beta1_SubjectAccessReviewStatus(in interface{}, out interface{},
out := out.(*SubjectAccessReviewStatus)
out.Allowed = in.Allowed
out.Reason = in.Reason
out.EvaluationError = in.EvaluationError
return nil
}
}

View File

@ -17,6 +17,7 @@ limitations under the License.
package validation
import (
"k8s.io/kubernetes/pkg/api"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
"k8s.io/kubernetes/pkg/util/validation/field"
)
@ -50,15 +51,24 @@ func ValidateSelfSubjectAccessReviewSpec(spec authorizationapi.SelfSubjectAccess
func ValidateSubjectAccessReview(sar *authorizationapi.SubjectAccessReview) field.ErrorList {
allErrs := ValidateSubjectAccessReviewSpec(sar.Spec, field.NewPath("spec"))
if !api.Semantic.DeepEqual(api.ObjectMeta{}, sar.ObjectMeta) {
allErrs = append(allErrs, field.Invalid(field.NewPath("metadata"), sar.ObjectMeta, `must be empty`))
}
return allErrs
}
func ValidateSelfSubjectAccessReview(sar *authorizationapi.SelfSubjectAccessReview) field.ErrorList {
allErrs := ValidateSelfSubjectAccessReviewSpec(sar.Spec, field.NewPath("spec"))
if !api.Semantic.DeepEqual(api.ObjectMeta{}, sar.ObjectMeta) {
allErrs = append(allErrs, field.Invalid(field.NewPath("metadata"), sar.ObjectMeta, `must be empty`))
}
return allErrs
}
func ValidateLocalSubjectAccessReview(sar *authorizationapi.LocalSubjectAccessReview) field.ErrorList {
allErrs := ValidateSubjectAccessReviewSpec(sar.Spec, field.NewPath("spec"))
if !api.Semantic.DeepEqual(api.ObjectMeta{}, sar.ObjectMeta) {
allErrs = append(allErrs, field.Invalid(field.NewPath("metadata"), sar.ObjectMeta, `must be empty`))
}
return allErrs
}

View File

@ -47,6 +47,9 @@ func DeepCopy_authorization_LocalSubjectAccessReview(in interface{}, out interfa
in := in.(*LocalSubjectAccessReview)
out := out.(*LocalSubjectAccessReview)
out.TypeMeta = in.TypeMeta
if err := api.DeepCopy_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if err := DeepCopy_authorization_SubjectAccessReviewSpec(&in.Spec, &out.Spec, c); err != nil {
return err
}
@ -85,6 +88,9 @@ func DeepCopy_authorization_SelfSubjectAccessReview(in interface{}, out interfac
in := in.(*SelfSubjectAccessReview)
out := out.(*SelfSubjectAccessReview)
out.TypeMeta = in.TypeMeta
if err := api.DeepCopy_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if err := DeepCopy_authorization_SelfSubjectAccessReviewSpec(&in.Spec, &out.Spec, c); err != nil {
return err
}
@ -120,6 +126,9 @@ func DeepCopy_authorization_SubjectAccessReview(in interface{}, out interface{},
in := in.(*SubjectAccessReview)
out := out.(*SubjectAccessReview)
out.TypeMeta = in.TypeMeta
if err := api.DeepCopy_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if err := DeepCopy_authorization_SubjectAccessReviewSpec(&in.Spec, &out.Spec, c); err != nil {
return err
}
@ -156,12 +165,12 @@ func DeepCopy_authorization_SubjectAccessReviewSpec(in interface{}, out interfac
}
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make(map[string][]string)
*out = make(map[string]ExtraValue)
for key, val := range *in {
if newVal, err := c.DeepCopy(&val); err != nil {
return err
} else {
(*out)[key] = *newVal.(*[]string)
(*out)[key] = *newVal.(*ExtraValue)
}
}
} else {
@ -177,6 +186,7 @@ func DeepCopy_authorization_SubjectAccessReviewStatus(in interface{}, out interf
out := out.(*SubjectAccessReviewStatus)
out.Allowed = in.Allowed
out.Reason = in.Reason
out.EvaluationError = in.EvaluationError
return nil
}
}

View File

@ -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 (
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
"k8s.io/kubernetes/pkg/client/testing/core"
)
func (c *FakeSubjectAccessReviews) Create(sar *authorizationapi.SubjectAccessReview) (result *authorizationapi.SubjectAccessReview, err error) {
obj, err := c.Fake.Invokes(core.NewRootCreateAction(authorizationapi.SchemeGroupVersion.WithResource("subjectaccessreviews"), sar), &authorizationapi.SubjectAccessReview{})
return obj.(*authorizationapi.SubjectAccessReview), err
}

View File

@ -0,0 +1,36 @@
/*
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 (
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
)
// The PodExpansion interface allows manually adding extra methods to the PodInterface.
type SubjectAccessReviewExpansion interface {
Create(sar *authorizationapi.SubjectAccessReview) (result *authorizationapi.SubjectAccessReview, err error)
}
func (c *subjectAccessReviews) Create(sar *authorizationapi.SubjectAccessReview) (result *authorizationapi.SubjectAccessReview, err error) {
result = &authorizationapi.SubjectAccessReview{}
err = c.client.Post().
Resource("subjectaccessreviews").
Body(sar).
Do().
Into(result)
return
}

View File

@ -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 (
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization/v1beta1"
"k8s.io/kubernetes/pkg/client/testing/core"
)
func (c *FakeSubjectAccessReviews) Create(sar *authorizationapi.SubjectAccessReview) (result *authorizationapi.SubjectAccessReview, err error) {
obj, err := c.Fake.Invokes(core.NewRootCreateAction(authorizationapi.SchemeGroupVersion.WithResource("subjectaccessreviews"), sar), &authorizationapi.SubjectAccessReview{})
return obj.(*authorizationapi.SubjectAccessReview), err
}

View File

@ -0,0 +1,36 @@
/*
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 v1beta1
import (
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization/v1beta1"
)
// The PodExpansion interface allows manually adding extra methods to the PodInterface.
type SubjectAccessReviewExpansion interface {
Create(sar *authorizationapi.SubjectAccessReview) (result *authorizationapi.SubjectAccessReview, err error)
}
func (c *subjectAccessReviews) Create(sar *authorizationapi.SubjectAccessReview) (result *authorizationapi.SubjectAccessReview, err error) {
result = &authorizationapi.SubjectAccessReview{}
err = c.client.Post().
Resource("subjectaccessreviews").
Body(sar).
Do().
Into(result)
return
}

View File

@ -19,6 +19,7 @@ package internalclientset
import (
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
unversionedauthentication "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authentication/unversioned"
unversionedauthorization "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/unversioned"
unversionedautoscaling "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/autoscaling/unversioned"
unversionedbatch "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/unversioned"
unversionedcore "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned"
@ -47,6 +48,11 @@ func FromUnversionedClient(c *unversioned.Client) *internalclientset.Clientset {
} else {
clientset.BatchClient = unversionedbatch.New(nil)
}
if c != nil && c.AuthorizationClient != nil {
clientset.AuthorizationClient = unversionedauthorization.New(c.AuthorizationClient.RESTClient)
} else {
clientset.AuthorizationClient = unversionedauthorization.New(nil)
}
if c != nil && c.AutoscalingClient != nil {
clientset.AutoscalingClient = unversionedautoscaling.New(c.AutoscalingClient.RESTClient)
} else {

View File

@ -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/authorization"
"k8s.io/kubernetes/pkg/client/restclient"
)
type AuthorizationInterface interface {
SubjectAccessReviewsInterface
}
// AuthorizationClient is used to interact with Kubernetes authorization features.
type AuthorizationClient struct {
*restclient.RESTClient
}
func (c *AuthorizationClient) SubjectAccessReviews() SubjectAccessReviewInterface {
return newSubjectAccessReviews(c)
}
func NewAuthorization(c *restclient.Config) (*AuthorizationClient, error) {
config := *c
if err := setAuthorizationDefaults(&config); err != nil {
return nil, err
}
client, err := restclient.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &AuthorizationClient{client}, nil
}
func NewAuthorizationOrDie(c *restclient.Config) *AuthorizationClient {
client, err := NewAuthorization(c)
if err != nil {
panic(err)
}
return client
}
func setAuthorizationDefaults(config *restclient.Config) error {
// if authorization group is not registered, return an error
g, err := registered.Group(authorization.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 = &copyGroupVersion
//}
config.NegotiatedSerializer = api.Codecs
return nil
}

View File

@ -45,6 +45,7 @@ type Interface interface {
ComponentStatusesInterface
ConfigMapsNamespacer
Apps() AppsInterface
Authorization() AuthorizationInterface
Autoscaling() AutoscalingInterface
Authentication() AuthenticationInterface
Batch() BatchInterface
@ -120,6 +121,7 @@ func (c *Client) ConfigMaps(namespace string) ConfigMapsInterface {
// Client is the implementation of a Kubernetes client.
type Client struct {
*restclient.RESTClient
*AuthorizationClient
*AutoscalingClient
*AuthenticationClient
*BatchClient
@ -153,6 +155,10 @@ func IsTimeout(err error) bool {
return false
}
func (c *Client) Authorization() AuthorizationInterface {
return c.AuthorizationClient
}
func (c *Client) Autoscaling() AutoscalingInterface {
return c.AutoscalingClient
}

View File

@ -24,6 +24,7 @@ import (
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/authentication"
"k8s.io/kubernetes/pkg/apis/authorization"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/certificates"
@ -63,6 +64,15 @@ func New(c *restclient.Config) (*Client, error) {
return nil, err
}
var authorizationClient *AuthorizationClient
if registered.IsRegistered(authorization.GroupName) {
authorizationConfig := *c
authorizationClient, err = NewAuthorization(&authorizationConfig)
if err != nil {
return nil, err
}
}
var autoscalingClient *AutoscalingClient
if registered.IsRegistered(autoscaling.GroupName) {
autoscalingConfig := *c
@ -137,6 +147,7 @@ func New(c *restclient.Config) (*Client, error) {
RESTClient: client,
AppsClient: appsClient,
AuthenticationClient: authenticationClient,
AuthorizationClient: authorizationClient,
AutoscalingClient: autoscalingClient,
BatchClient: batchClient,
CertificatesClient: certsClient,

View File

@ -0,0 +1,45 @@
/*
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 unversioned
import (
"k8s.io/kubernetes/pkg/apis/authorization"
)
type SubjectAccessReviewsInterface interface {
SubjectAccessReviews() SubjectAccessReviewInterface
}
type SubjectAccessReviewInterface interface {
Create(subjectAccessReview *authorization.SubjectAccessReview) (*authorization.SubjectAccessReview, error)
}
type subjectAccessReviews struct {
client *AuthorizationClient
}
func newSubjectAccessReviews(c *AuthorizationClient) *subjectAccessReviews {
return &subjectAccessReviews{
client: c,
}
}
func (c *subjectAccessReviews) Create(subjectAccessReview *authorization.SubjectAccessReview) (result *authorization.SubjectAccessReview, err error) {
result = &authorization.SubjectAccessReview{}
err = c.client.Post().Resource("subjectAccessReviews").Body(subjectAccessReview).Do().Into(result)
return
}

View File

@ -0,0 +1,36 @@
/*
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/authorization"
)
// FakeSubjectAccessReviews implements SubjectAccessReviewInterface. Meant to be embedded into a struct to get a default
// implementation. This makes faking out just the methods you want to test easier.
type FakeSubjectAccessReviews struct {
Fake *FakeAuthorization
}
func (c *FakeSubjectAccessReviews) Create(a *authorization.SubjectAccessReview) (*authorization.SubjectAccessReview, error) {
obj, err := c.Fake.Invokes(NewRootCreateAction("subjectaccessreviews", a), a)
if obj == nil {
return nil, err
}
return obj.(*authorization.SubjectAccessReview), err
}

View File

@ -281,6 +281,10 @@ func (c *Fake) Apps() client.AppsInterface {
return &FakeApps{c}
}
func (c *Fake) Authorization() client.AuthorizationInterface {
return &FakeAuthorization{c}
}
func (c *Fake) Autoscaling() client.AutoscalingInterface {
return &FakeAutoscaling{c}
}
@ -344,6 +348,19 @@ func (c *FakeApps) PetSets(namespace string) client.PetSetInterface {
return &FakePetSets{Fake: c, Namespace: namespace}
}
// NewSimpleFakeAuthorization returns a client that will respond with the provided objects
func NewSimpleFakeAuthorization(objects ...runtime.Object) *FakeAuthorization {
return &FakeAuthorization{Fake: NewSimpleFake(objects...)}
}
type FakeAuthorization struct {
*Fake
}
func (c *FakeAuthorization) SubjectAccessReviews() client.SubjectAccessReviewInterface {
return &FakeSubjectAccessReviews{Fake: c}
}
// NewSimpleFakeAutoscaling returns a client that will respond with the provided objects
func NewSimpleFakeAutoscaling(objects ...runtime.Object) *FakeAutoscaling {
return &FakeAutoscaling{Fake: NewSimpleFake(objects...)}

View File

@ -503,11 +503,12 @@ 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: "authentication.k8s.io", Version: "v1beta1", Resource: "tokenreviews"}: {},
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"}: {},
unversioned.GroupVersionResource{Group: "authorization.k8s.io", Version: "v1beta1", Resource: "subjectaccessreviews"}: {},
}
func NewGarbageCollector(clientPool dynamic.ClientPool, resources []unversioned.GroupVersionResource) (*GarbageCollector, error) {

View File

@ -35,6 +35,8 @@ import (
"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/authorization"
authorizationapiv1beta1 "k8s.io/kubernetes/pkg/apis/authorization/v1beta1"
"k8s.io/kubernetes/pkg/apis/autoscaling"
autoscalingapiv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
"k8s.io/kubernetes/pkg/apis/batch"
@ -202,6 +204,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}
c.RESTStorageProviders[authorization.GroupName] = AuthorizationRESTStorageProvider{Authorizer: c.Authorizer}
m.InstallAPIs(c)
// TODO: Attempt clean shutdown?
@ -762,6 +765,7 @@ func DefaultAPIResourceConfigSource() *genericapiserver.ResourceConfig {
policyapiv1alpha1.SchemeGroupVersion,
rbacapi.SchemeGroupVersion,
certificatesapiv1alpha1.SchemeGroupVersion,
authorizationapiv1beta1.SchemeGroupVersion,
)
// all extensions resources except these are disabled by default

View File

@ -0,0 +1,58 @@
/*
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/authorization"
authorizationv1beta1 "k8s.io/kubernetes/pkg/apis/authorization/v1beta1"
"k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/registry/authorization/subjectaccessreview"
)
type AuthorizationRESTStorageProvider struct {
Authorizer authorizer.Authorizer
}
var _ RESTStorageProvider = &AuthorizationRESTStorageProvider{}
func (p AuthorizationRESTStorageProvider) NewRESTStorage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
if p.Authorizer == nil {
return genericapiserver.APIGroupInfo{}, false
}
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(authorization.GroupName)
if apiResourceConfigSource.AnyResourcesForVersionEnabled(authorizationv1beta1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[authorizationv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = authorizationv1beta1.SchemeGroupVersion
}
return apiGroupInfo, true
}
func (p AuthorizationRESTStorageProvider) v1beta1Storage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter RESTOptionsGetter) map[string]rest.Storage {
version := authorizationv1beta1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("subjectaccessreviews")) {
storage["subjectaccessreviews"] = subjectaccessreview.NewREST(p.Authorizer)
}
return storage
}

View File

@ -0,0 +1,89 @@
/*
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 subjectaccessreview
import (
"fmt"
kapi "k8s.io/kubernetes/pkg/api"
kapierrors "k8s.io/kubernetes/pkg/api/errors"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
authorizationvalidation "k8s.io/kubernetes/pkg/apis/authorization/validation"
"k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/auth/user"
authorizationutil "k8s.io/kubernetes/pkg/registry/authorization/util"
"k8s.io/kubernetes/pkg/runtime"
)
type REST struct {
authorizer authorizer.Authorizer
}
func NewREST(authorizer authorizer.Authorizer) *REST {
return &REST{authorizer}
}
func (r *REST) New() runtime.Object {
return &authorizationapi.SubjectAccessReview{}
}
func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) {
subjectAccessReview, ok := obj.(*authorizationapi.SubjectAccessReview)
if !ok {
return nil, kapierrors.NewBadRequest(fmt.Sprintf("not a SubjectAccessReview: %#v", obj))
}
if errs := authorizationvalidation.ValidateSubjectAccessReview(subjectAccessReview); len(errs) > 0 {
return nil, kapierrors.NewInvalid(authorizationapi.Kind(subjectAccessReview.Kind), "", errs)
}
userToCheck := &user.DefaultInfo{
Name: subjectAccessReview.Spec.User,
Groups: subjectAccessReview.Spec.Groups,
Extra: convertToUserInfoExtra(subjectAccessReview.Spec.Extra),
}
var authorizationAttributes authorizer.AttributesRecord
if subjectAccessReview.Spec.ResourceAttributes != nil {
authorizationAttributes = authorizationutil.ResourceAttributesFrom(userToCheck, *subjectAccessReview.Spec.ResourceAttributes)
} else {
authorizationAttributes = authorizationutil.NonResourceAttributesFrom(userToCheck, *subjectAccessReview.Spec.NonResourceAttributes)
}
allowed, reason, evaluationErr := r.authorizer.Authorize(authorizationAttributes)
subjectAccessReview.Status = authorizationapi.SubjectAccessReviewStatus{
Allowed: allowed,
Reason: reason,
}
if evaluationErr != nil {
subjectAccessReview.Status.EvaluationError = evaluationErr.Error()
}
return subjectAccessReview, nil
}
func convertToUserInfoExtra(extra map[string]authorizationapi.ExtraValue) map[string][]string {
if extra == nil {
return nil
}
ret := map[string][]string{}
for k, v := range extra {
ret[k] = []string(v)
}
return ret
}

View File

@ -0,0 +1,44 @@
/*
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 util
import (
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
"k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/auth/user"
)
// ResourceAttributesFrom combines the API object information and the user.Info from the context to build a full authorizer.AttributesRecord for resource access
func ResourceAttributesFrom(user user.Info, in authorizationapi.ResourceAttributes) authorizer.AttributesRecord {
return authorizer.AttributesRecord{
User: user,
Verb: in.Verb,
Namespace: in.Namespace,
APIGroup: in.Group,
Resource: in.Resource,
ResourceRequest: true,
}
}
// NonResourceAttributesFrom combines the API object information and the user.Info from the context to build a full authorizer.AttributesRecord for non resource access
func NonResourceAttributesFrom(user user.Info, in authorizationapi.NonResourceAttributes) authorizer.AttributesRecord {
return authorizer.AttributesRecord{
User: user,
ResourceRequest: false,
Path: in.Path,
}
}

View File

@ -133,7 +133,7 @@ func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (authorized bo
r.Spec = v1beta1.SubjectAccessReviewSpec{
User: user.GetName(),
Groups: user.GetGroups(),
Extra: user.GetExtra(),
Extra: convertToSARExtra(user.GetExtra()),
}
}
@ -186,3 +186,15 @@ func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (authorized bo
}
return r.Status.Allowed, r.Status.Reason, nil
}
func convertToSARExtra(extra map[string][]string) map[string]v1beta1.ExtraValue {
if extra == nil {
return nil
}
ret := map[string]v1beta1.ExtraValue{}
for k, v := range extra {
ret[k] = v1beta1.ExtraValue(v)
}
return ret
}

View File

@ -0,0 +1,16 @@
{
"apiVersion": "authorization.k8s.io/v1beta1",
"kind": "SubjectAccessReview",
"spec": {
"user": "bob",
"groups": [
"the-group"
],
"resourceAttributes": {
"namespace": "ns",
"verb": "create",
"group": "autoscaling",
"resource": "horizontalpodautoscalers"
}
}
}

View File

@ -0,0 +1,159 @@
// +build integration,!no-etcd
/*
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 auth
import (
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
"k8s.io/kubernetes/pkg/auth/authenticator"
"k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/auth/user"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/master"
"k8s.io/kubernetes/plugin/pkg/admission/admit"
"k8s.io/kubernetes/test/integration/framework"
)
// Inject into master an authorizer that uses user info.
// TODO(etune): remove this test once a more comprehensive built-in authorizer is implemented.
type sarAuthorizer struct{}
func (sarAuthorizer) Authorize(a authorizer.Attributes) (bool, string, error) {
if a.GetUser().GetName() == "dave" {
return false, "no", errors.New("I'm sorry, Dave")
}
return true, "you're not dave", nil
}
func alwaysAlice(req *http.Request) (user.Info, bool, error) {
return &user.DefaultInfo{
Name: "alice",
}, true, nil
}
func TestSubjectAccessReview(t *testing.T) {
// Set up a master
var m *master.Master
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
m.Handler.ServeHTTP(w, req)
}))
defer s.Close()
masterConfig := framework.NewIntegrationTestMasterConfig()
masterConfig.Authenticator = authenticator.RequestFunc(alwaysAlice)
masterConfig.Authorizer = sarAuthorizer{}
masterConfig.AdmissionControl = admit.NewAlwaysAdmit()
m, err := master.New(masterConfig)
if err != nil {
t.Fatalf("error in bringing up the master: %v", err)
}
clientset := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}})
tests := []struct {
name string
sar *authorizationapi.SubjectAccessReview
expectedError string
expectedStatus authorizationapi.SubjectAccessReviewStatus
}{
{
name: "simple allow",
sar: &authorizationapi.SubjectAccessReview{
Spec: authorizationapi.SubjectAccessReviewSpec{
ResourceAttributes: &authorizationapi.ResourceAttributes{
Verb: "list",
Group: api.GroupName,
Version: "v1",
Resource: "pods",
},
User: "alice",
},
},
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
Allowed: true,
Reason: "you're not dave",
},
},
{
name: "simple deny",
sar: &authorizationapi.SubjectAccessReview{
Spec: authorizationapi.SubjectAccessReviewSpec{
ResourceAttributes: &authorizationapi.ResourceAttributes{
Verb: "list",
Group: api.GroupName,
Version: "v1",
Resource: "pods",
},
User: "dave",
},
},
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
Allowed: false,
Reason: "no",
EvaluationError: "I'm sorry, Dave",
},
},
{
name: "simple error",
sar: &authorizationapi.SubjectAccessReview{
Spec: authorizationapi.SubjectAccessReviewSpec{
ResourceAttributes: &authorizationapi.ResourceAttributes{
Verb: "list",
Group: api.GroupName,
Version: "v1",
Resource: "pods",
},
},
},
expectedError: "at least one of user or group must be specified",
},
}
for _, test := range tests {
response, err := clientset.Authorization().SubjectAccessReviews().Create(test.sar)
switch {
case err == nil && len(test.expectedError) == 0:
case err != nil && strings.Contains(err.Error(), test.expectedError):
continue
case err != nil && len(test.expectedError) != 0:
t.Errorf("%s: unexpected error: %v", test.name, err)
continue
default:
t.Errorf("%s: expected %v, got %v", test.name, test.expectedError, err)
continue
}
if response.Status != test.expectedStatus {
t.Errorf("%s: expected %v, got %v", test.name, test.expectedStatus, response.Status)
continue
}
}
}