diff --git a/pkg/authorization/api/helpers.go b/pkg/authorization/api/helpers.go new file mode 100644 index 000000000000..6735d961c1ec --- /dev/null +++ b/pkg/authorization/api/helpers.go @@ -0,0 +1,37 @@ +package api + +import ( + "fmt" + "strings" + + kutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util" +) + +func ExpandResources(rawResources kutil.StringSet) kutil.StringSet { + ret := kutil.StringSet{} + toVisit := rawResources.List() + visited := kutil.StringSet{} + + for i := 0; i < len(toVisit); i++ { + currResource := toVisit[i] + if visited.Has(currResource) { + continue + } + visited.Insert(currResource) + + if strings.Index(currResource, ResourceGroupPrefix+":") != 0 { + ret.Insert(strings.ToLower(currResource)) + continue + } + + if resourceTypes, exists := GroupsToResources[currResource]; exists { + toVisit = append(toVisit, resourceTypes...) + } + } + + return ret +} + +func (r PolicyRule) String() string { + return fmt.Sprintf("PolicyRule{Verbs:%v, Resources:%v, ResourceNames:%v, Restrictions:%v}", r.Verbs.List(), r.Resources.List(), r.ResourceNames.List(), r.AttributeRestrictions) +} diff --git a/pkg/authorization/api/register.go b/pkg/authorization/api/register.go index 85a752941217..0717afe03dba 100644 --- a/pkg/authorization/api/register.go +++ b/pkg/authorization/api/register.go @@ -16,6 +16,7 @@ func init() { &SubjectAccessReviewResponse{}, &PolicyList{}, &PolicyBindingList{}, + &RoleBindingList{}, ) } @@ -29,3 +30,4 @@ func (*ResourceAccessReviewResponse) IsAnAPIObject() {} func (*SubjectAccessReviewResponse) IsAnAPIObject() {} func (*PolicyList) IsAnAPIObject() {} func (*PolicyBindingList) IsAnAPIObject() {} +func (*RoleBindingList) IsAnAPIObject() {} diff --git a/pkg/authorization/api/types.go b/pkg/authorization/api/types.go index 353ae353c49c..24b1bcec6730 100644 --- a/pkg/authorization/api/types.go +++ b/pkg/authorization/api/types.go @@ -70,12 +70,12 @@ var ( // about who the rule applies to or which namespace the rule applies to. type PolicyRule struct { // Verbs is a list of Verbs that apply to ALL the ResourceKinds and AttributeRestrictions contained in this rule. VerbAll represents all kinds. - Verbs []string + Verbs kutil.StringSet // AttributeRestrictions will vary depending on what the Authorizer/AuthorizationAttributeBuilder pair supports. // If the Authorizer does not recognize how to handle the AttributeRestrictions, the Authorizer should report an error. AttributeRestrictions kruntime.EmbeddedObject // Resources is a list of resources this rule applies to. ResourceAll represents all resources. - Resources []string + Resources kutil.StringSet `json:"resources"` // ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed. ResourceNames kutil.StringSet // NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path @@ -100,9 +100,9 @@ type RoleBinding struct { kapi.ObjectMeta // UserNames holds all the usernames directly bound to the role - UserNames []string + Users kutil.StringSet // GroupNames holds all the groups directly bound to the role - GroupNames []string + Groups kutil.StringSet // Since Policy is a singleton, this is sufficient knowledge to locate a role // RoleRefs can only reference the current namespace and the global namespace @@ -208,3 +208,10 @@ type PolicyBindingList struct { kapi.ListMeta Items []PolicyBinding } + +// RoleBindingList is a collection of PolicyBindings +type RoleBindingList struct { + kapi.TypeMeta + kapi.ListMeta + Items []RoleBinding +} diff --git a/pkg/authorization/api/v1beta1/conversion.go b/pkg/authorization/api/v1beta1/conversion.go index 194200efc806..6a39c4cf4743 100644 --- a/pkg/authorization/api/v1beta1/conversion.go +++ b/pkg/authorization/api/v1beta1/conversion.go @@ -17,12 +17,12 @@ func init() { return err } - out.Verbs = []string{} - out.Verbs = append(out.Verbs, in.Verbs...) + out.Resources = util.StringSet{} + out.Resources.Insert(in.Resources...) + out.Resources.Insert(in.ResourceKinds...) - out.Resources = []string{} - out.Resources = append(out.Resources, in.Resources...) - out.Resources = append(out.Resources, in.ResourceKinds...) + out.Verbs = util.StringSet{} + out.Verbs.Insert(in.Verbs...) out.ResourceNames = util.NewStringSet(in.ResourceNames...) @@ -35,11 +35,11 @@ func init() { return err } - out.Verbs = []string{} - out.Verbs = append(out.Verbs, in.Verbs...) - out.Resources = []string{} - out.Resources = append(out.Resources, in.Resources...) + out.Resources = append(out.Resources, in.Resources.List()...) + + out.Verbs = []string{} + out.Verbs = append(out.Verbs, in.Verbs.List()...) out.ResourceNames = in.ResourceNames.List() @@ -57,6 +57,26 @@ func init() { out.Roles = make([]NamedRole, 0, 0) return s.DefaultConvert(in, out, conversion.IgnoreMissingFields) }, + func(in *RoleBinding, out *newer.RoleBinding, s conversion.Scope) error { + if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields|conversion.AllowDifferentFieldTypeNames); err != nil { + return err + } + + out.Users = util.NewStringSet(in.UserNames...) + out.Groups = util.NewStringSet(in.GroupNames...) + + return nil + }, + func(in *newer.RoleBinding, out *RoleBinding, s conversion.Scope) error { + if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields|conversion.AllowDifferentFieldTypeNames); err != nil { + return err + } + + out.UserNames = in.Users.List() + out.GroupNames = in.Groups.List() + + return nil + }, func(in *[]NamedRole, out *map[string]newer.Role, s conversion.Scope) error { for _, curr := range *in { newRole := &newer.Role{} diff --git a/pkg/authorization/api/v1beta1/register.go b/pkg/authorization/api/v1beta1/register.go index 4d7dbd8a6a09..ca01adec2292 100644 --- a/pkg/authorization/api/v1beta1/register.go +++ b/pkg/authorization/api/v1beta1/register.go @@ -16,6 +16,7 @@ func init() { &SubjectAccessReviewResponse{}, &PolicyList{}, &PolicyBindingList{}, + &RoleBindingList{}, ) } @@ -29,3 +30,4 @@ func (*ResourceAccessReviewResponse) IsAnAPIObject() {} func (*SubjectAccessReviewResponse) IsAnAPIObject() {} func (*PolicyList) IsAnAPIObject() {} func (*PolicyBindingList) IsAnAPIObject() {} +func (*RoleBindingList) IsAnAPIObject() {} diff --git a/pkg/authorization/api/v1beta1/types.go b/pkg/authorization/api/v1beta1/types.go index a9e249aa8d85..98ad9749d271 100644 --- a/pkg/authorization/api/v1beta1/types.go +++ b/pkg/authorization/api/v1beta1/types.go @@ -168,3 +168,10 @@ type PolicyBindingList struct { kapi.ListMeta `json:"metadata,omitempty"` Items []PolicyBinding `json:"items"` } + +// RoleBindingList is a collection of PolicyBindings +type RoleBindingList struct { + kapi.TypeMeta + kapi.ListMeta + Items []RoleBinding +} diff --git a/pkg/authorization/authorizer/authorizer.go b/pkg/authorization/authorizer/authorizer.go index e0710b36493d..d913e638a08f 100644 --- a/pkg/authorization/authorizer/authorizer.go +++ b/pkg/authorization/authorizer/authorizer.go @@ -1,7 +1,6 @@ package authorizer import ( - "errors" "fmt" "net/http" "path" @@ -10,18 +9,16 @@ import ( kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" kapiserver "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user" - klabels "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors" authorizationapi "github.com/openshift/origin/pkg/authorization/api" - policyregistry "github.com/openshift/origin/pkg/authorization/registry/policy" - policybindingregistry "github.com/openshift/origin/pkg/authorization/registry/policybinding" + "github.com/openshift/origin/pkg/authorization/rulevalidation" ) type Authorizer interface { - Authorize(a AuthorizationAttributes) (allowed bool, reason string, err error) - GetAllowedSubjects(attributes AuthorizationAttributes) ([]string, []string, error) + Authorize(ctx kapi.Context, a AuthorizationAttributes) (allowed bool, reason string, err error) + GetAllowedSubjects(ctx kapi.Context, attributes AuthorizationAttributes) ([]string, []string, error) } type AuthorizationAttributeBuilder interface { @@ -29,15 +26,9 @@ type AuthorizationAttributeBuilder interface { } type AuthorizationAttributes interface { - // GetUserInfo returns the user requesting the action - GetUserInfo() user.Info - // GetVerb returns the verb associated with the action. For resource requests, this verb is the logical kube verb. For non-resource requests it is the http method tolowered. GetVerb() string // GetResource returns the resource type. If IsNonResourceURL() is true, then GetResource() is "". GetResource() string - // GetNamespace returns the namespace of a resource request. If IsNonResourceURL() is true, then GetNamespace() is "". - GetNamespace() string - // GetResourceName returns the name of the resource being acted upon. Not all resource actions have one (list as a for instance). If IsNonResourceURL() is true, then GetResourceName() is "". GetResourceName() string // GetRequestAttributes is of type interface{} because different verbs and different Authorizer/AuthorizationAttributeBuilder pairs may have different contract requirements. GetRequestAttributes() interface{} @@ -49,20 +40,17 @@ type AuthorizationAttributes interface { type openshiftAuthorizer struct { masterAuthorizationNamespace string - policyRegistry policyregistry.Registry - policyBindingRegistry policybindingregistry.Registry + ruleResolver rulevalidation.AuthorizationRuleResolver } -func NewAuthorizer(masterAuthorizationNamespace string, policyRuleBindingRegistry policyregistry.Registry, policyBindingRegistry policybindingregistry.Registry) Authorizer { - return &openshiftAuthorizer{masterAuthorizationNamespace, policyRuleBindingRegistry, policyBindingRegistry} +func NewAuthorizer(masterAuthorizationNamespace string, ruleResolver rulevalidation.AuthorizationRuleResolver) Authorizer { + return &openshiftAuthorizer{masterAuthorizationNamespace, ruleResolver} } type DefaultAuthorizationAttributes struct { - User user.Info Verb string Resource string ResourceName string - Namespace string RequestAttributes interface{} NonResourceURL bool URL string @@ -77,13 +65,13 @@ func NewAuthorizationAttributeBuilder(contextMapper kapi.RequestContextMapper, i return &openshiftAuthorizationAttributeBuilder{contextMapper, infoResolver} } -func doesApplyToUser(ruleUsers, ruleGroups []string, user user.Info) bool { - if contains(ruleUsers, user.GetName()) { +func doesApplyToUser(ruleUsers, ruleGroups util.StringSet, user user.Info) bool { + if ruleUsers.Has(user.GetName()) { return true } for _, currGroup := range user.GetGroups() { - if contains(ruleGroups, currGroup) { + if ruleGroups.Has(currGroup) { return true } } @@ -98,94 +86,10 @@ func contains(list []string, token string) bool { } return false } - -// getPolicy provides a point for easy caching -func (a *openshiftAuthorizer) getPolicy(namespace string) (*authorizationapi.Policy, error) { - ctx := kapi.WithNamespace(kapi.NewContext(), namespace) - policy, err := a.policyRegistry.GetPolicy(ctx, authorizationapi.PolicyName) - if err != nil && !strings.Contains(err.Error(), "not found") { - return nil, err - } - - return policy, nil -} - -// getPolicyBindings provides a point for easy caching -func (a *openshiftAuthorizer) getPolicyBindings(namespace string) ([]authorizationapi.PolicyBinding, error) { - ctx := kapi.WithNamespace(kapi.NewContext(), namespace) - policyBindingList, err := a.policyBindingRegistry.ListPolicyBindings(ctx, klabels.Everything(), klabels.Everything()) - if err != nil { - return nil, err - } - - return policyBindingList.Items, nil -} - -// getRoleBindings provides a point for easy caching -func (a *openshiftAuthorizer) getRoleBindings(namespace string) ([]authorizationapi.RoleBinding, error) { - policyBindings, err := a.getPolicyBindings(namespace) - if err != nil { - return nil, err - } - - ret := make([]authorizationapi.RoleBinding, 0, len(policyBindings)) - for _, policyBinding := range policyBindings { - for _, value := range policyBinding.RoleBindings { - ret = append(ret, value) - } - } - - return ret, nil -} - -func (a *openshiftAuthorizer) getRole(roleBinding authorizationapi.RoleBinding) (*authorizationapi.Role, error) { - roleNamespace := roleBinding.RoleRef.Namespace - roleName := roleBinding.RoleRef.Name - - rolePolicy, err := a.getPolicy(roleNamespace) - if err != nil { - return nil, err - } - - role, exists := rolePolicy.Roles[roleName] - if !exists { - return nil, fmt.Errorf("role %#v not found", roleBinding.RoleRef) - } - - return &role, nil -} - -// getEffectivePolicyRules returns the list of rules that apply to a given user in a given namespace and error. If an error is returned, the slice of -// PolicyRules may not be complete, but it contains all retrievable rules. This is done because policy rules are purely additive and policy determinations -// can be made on the basis of those rules that are found. -func (a *openshiftAuthorizer) getEffectivePolicyRules(namespace string, user user.Info) ([]authorizationapi.PolicyRule, error) { - roleBindings, err := a.getRoleBindings(namespace) - if err != nil { - return nil, err - } - - errs := []error{} - effectiveRules := make([]authorizationapi.PolicyRule, 0, len(roleBindings)) - for _, roleBinding := range roleBindings { - role, err := a.getRole(roleBinding) - if err != nil { - errs = append(errs, err) - continue - } - - for _, curr := range role.Rules { - if doesApplyToUser(roleBinding.UserNames, roleBinding.GroupNames, user) { - effectiveRules = append(effectiveRules, curr) - } - } - } - - return effectiveRules, kerrors.NewAggregate(errs) -} -func (a *openshiftAuthorizer) getAllowedSubjectsFromNamespaceBindings(namespace string, passedAttributes AuthorizationAttributes) (util.StringSet, util.StringSet, error) { +func (a *openshiftAuthorizer) getAllowedSubjectsFromNamespaceBindings(ctx kapi.Context, passedAttributes AuthorizationAttributes) (util.StringSet, util.StringSet, error) { attributes := coerceToDefaultAuthorizationAttributes(passedAttributes) - roleBindings, err := a.getRoleBindings(namespace) + roleBindings, err := a.ruleResolver.GetRoleBindings(ctx) if err != nil { return nil, nil, err } @@ -193,7 +97,7 @@ func (a *openshiftAuthorizer) getAllowedSubjectsFromNamespaceBindings(namespace users := util.StringSet{} groups := util.StringSet{} for _, roleBinding := range roleBindings { - role, err := a.getRole(roleBinding) + role, err := a.ruleResolver.GetRole(roleBinding) if err != nil { return nil, nil, err } @@ -205,8 +109,8 @@ func (a *openshiftAuthorizer) getAllowedSubjectsFromNamespaceBindings(namespace } if matches { - users.Insert(roleBinding.UserNames...) - groups.Insert(roleBinding.GroupNames...) + users.Insert(roleBinding.Users.List()...) + groups.Insert(roleBinding.Groups.List()...) } } } @@ -214,12 +118,13 @@ func (a *openshiftAuthorizer) getAllowedSubjectsFromNamespaceBindings(namespace return users, groups, nil } -func (a *openshiftAuthorizer) GetAllowedSubjects(attributes AuthorizationAttributes) ([]string, []string, error) { - globalUsers, globalGroups, err := a.getAllowedSubjectsFromNamespaceBindings(a.masterAuthorizationNamespace, attributes) +func (a *openshiftAuthorizer) GetAllowedSubjects(ctx kapi.Context, attributes AuthorizationAttributes) ([]string, []string, error) { + masterContext := kapi.WithNamespace(ctx, a.masterAuthorizationNamespace) + globalUsers, globalGroups, err := a.getAllowedSubjectsFromNamespaceBindings(masterContext, attributes) if err != nil { return nil, nil, err } - localUsers, localGroups, err := a.getAllowedSubjectsFromNamespaceBindings(attributes.GetNamespace(), attributes) + localUsers, localGroups, err := a.getAllowedSubjectsFromNamespaceBindings(ctx, attributes) if err != nil { return nil, nil, err } @@ -235,7 +140,7 @@ func (a *openshiftAuthorizer) GetAllowedSubjects(attributes AuthorizationAttribu return users.List(), groups.List(), nil } -func (a *openshiftAuthorizer) Authorize(passedAttributes AuthorizationAttributes) (bool, string, error) { +func (a *openshiftAuthorizer) Authorize(ctx kapi.Context, passedAttributes AuthorizationAttributes) (bool, string, error) { attributes := coerceToDefaultAuthorizationAttributes(passedAttributes) // keep track of errors in case we are unable to authorize the action. @@ -243,7 +148,8 @@ func (a *openshiftAuthorizer) Authorize(passedAttributes AuthorizationAttributes // This is most common when a bound role is missing, but enough roles are still present and bound to authorize the request. errs := []error{} - globalAllowed, globalReason, err := a.authorizeWithNamespaceRules(a.masterAuthorizationNamespace, attributes) + masterContext := kapi.WithNamespace(ctx, a.masterAuthorizationNamespace) + globalAllowed, globalReason, err := a.authorizeWithNamespaceRules(masterContext, attributes) if globalAllowed { return true, globalReason, nil } @@ -251,8 +157,9 @@ func (a *openshiftAuthorizer) Authorize(passedAttributes AuthorizationAttributes errs = append(errs, err) } - if len(attributes.GetNamespace()) != 0 { - namespaceAllowed, namespaceReason, err := a.authorizeWithNamespaceRules(attributes.GetNamespace(), attributes) + namespace, _ := kapi.NamespaceFrom(ctx) + if len(namespace) != 0 { + namespaceAllowed, namespaceReason, err := a.authorizeWithNamespaceRules(ctx, attributes) if namespaceAllowed { return true, namespaceReason, nil } @@ -271,10 +178,10 @@ func (a *openshiftAuthorizer) Authorize(passedAttributes AuthorizationAttributes // authorizeWithNamespaceRules returns isAllowed, reason, and error. If an error is returned, isAllowed and reason are still valid. This seems strange // but errors are not always fatal to the authorization process. It is entirely possible to get an error and be able to continue determine authorization // status in spite of it. This is most common when a bound role is missing, but enough roles are still present and bound to authorize the request. -func (a *openshiftAuthorizer) authorizeWithNamespaceRules(namespace string, passedAttributes AuthorizationAttributes) (bool, string, error) { +func (a *openshiftAuthorizer) authorizeWithNamespaceRules(ctx kapi.Context, passedAttributes AuthorizationAttributes) (bool, string, error) { attributes := coerceToDefaultAuthorizationAttributes(passedAttributes) - allRules, ruleRetrievalError := a.getEffectivePolicyRules(namespace, attributes.GetUserInfo()) + allRules, ruleRetrievalError := a.ruleResolver.GetEffectivePolicyRules(ctx) for _, rule := range allRules { matches, err := attributes.RuleMatches(rule) @@ -282,7 +189,7 @@ func (a *openshiftAuthorizer) authorizeWithNamespaceRules(namespace string, pass return false, "", err } if matches { - return true, fmt.Sprintf("allowed by rule in %v: %#v", namespace, rule), nil + return true, fmt.Sprintf("allowed by rule in %v: %#v", kapi.NamespaceValue(ctx), rule), nil } } @@ -295,12 +202,10 @@ func coerceToDefaultAuthorizationAttributes(passedAttributes AuthorizationAttrib attributes, ok := passedAttributes.(*DefaultAuthorizationAttributes) if !ok { attributes = &DefaultAuthorizationAttributes{ - Namespace: passedAttributes.GetNamespace(), Verb: passedAttributes.GetVerb(), RequestAttributes: passedAttributes.GetRequestAttributes(), Resource: passedAttributes.GetResource(), ResourceName: passedAttributes.GetResourceName(), - User: passedAttributes.GetUserInfo(), NonResourceURL: passedAttributes.IsNonResourceURL(), URL: passedAttributes.GetURL(), } @@ -312,7 +217,7 @@ func coerceToDefaultAuthorizationAttributes(passedAttributes AuthorizationAttrib func (a DefaultAuthorizationAttributes) RuleMatches(rule authorizationapi.PolicyRule) (bool, error) { if a.IsNonResourceURL() { if a.nonResourceMatches(rule) { - if a.verbMatches(util.NewStringSet(rule.Verbs...)) { + if a.verbMatches(rule.Verbs) { return true, nil } } @@ -320,8 +225,8 @@ func (a DefaultAuthorizationAttributes) RuleMatches(rule authorizationapi.Policy return false, nil } - if a.verbMatches(util.NewStringSet(rule.Verbs...)) { - allowedResourceTypes := resolveResources(rule) + if a.verbMatches(rule.Verbs) { + allowedResourceTypes := authorizationapi.ExpandResources(rule.Resources) if a.resourceMatches(allowedResourceTypes) { if a.nameMatches(rule.ResourceNames) { @@ -333,31 +238,6 @@ func (a DefaultAuthorizationAttributes) RuleMatches(rule authorizationapi.Policy return false, nil } -func resolveResources(rule authorizationapi.PolicyRule) util.StringSet { - ret := util.StringSet{} - toVisit := rule.Resources - visited := util.StringSet{} - - for i := 0; i < len(toVisit); i++ { - currResource := toVisit[i] - if visited.Has(currResource) { - continue - } - visited.Insert(currResource) - - if strings.Index(currResource, authorizationapi.ResourceGroupPrefix+":") != 0 { - ret.Insert(strings.ToLower(currResource)) - continue - } - - if resourceTypes, exists := authorizationapi.GroupsToResources[currResource]; exists { - toVisit = append(toVisit, resourceTypes...) - } - } - - return ret -} - func (a DefaultAuthorizationAttributes) verbMatches(verbs util.StringSet) bool { return verbs.Has(authorizationapi.VerbAll) || verbs.Has(strings.ToLower(a.GetVerb())) } @@ -378,9 +258,6 @@ func (a DefaultAuthorizationAttributes) nameMatches(allowedResourceNames util.St return allowedResourceNames.Has(a.GetResourceName()) } -func (a DefaultAuthorizationAttributes) GetUserInfo() user.Info { - return a.User -} func (a DefaultAuthorizationAttributes) GetVerb() string { return a.Verb } @@ -421,9 +298,6 @@ func (a DefaultAuthorizationAttributes) GetResourceName() string { return a.ResourceName } -func (a DefaultAuthorizationAttributes) GetNamespace() string { - return a.Namespace -} func (a DefaultAuthorizationAttributes) GetRequestAttributes() interface{} { return a.RequestAttributes } @@ -437,15 +311,6 @@ func (a DefaultAuthorizationAttributes) GetURL() string { } func (a *openshiftAuthorizationAttributeBuilder) GetAttributes(req *http.Request) (AuthorizationAttributes, error) { - ctx, ok := a.contextMapper.Get(req) - if !ok { - return nil, errors.New("could not get request context") - } - userInfo, ok := kapi.UserFrom(ctx) - if !ok { - return nil, errors.New("could not get user") - } - // any url that starts with an API prefix and is more than one step long is considered to be a resource URL. // That means that /api is non-resource, /api/v1beta1 is resource, /healthz is non-resource, and /swagger/anything is non-resource urlSegments := splitPath(req.URL.Path) @@ -453,7 +318,6 @@ func (a *openshiftAuthorizationAttributeBuilder) GetAttributes(req *http.Request if !isResourceURL { return DefaultAuthorizationAttributes{ - User: userInfo, Verb: strings.ToLower(req.Method), NonResourceURL: true, URL: req.URL.Path, @@ -472,11 +336,9 @@ func (a *openshiftAuthorizationAttributeBuilder) GetAttributes(req *http.Request } return DefaultAuthorizationAttributes{ - User: userInfo, Verb: requestInfo.Verb, Resource: requestInfo.Resource, ResourceName: requestInfo.Name, - Namespace: requestInfo.Namespace, RequestAttributes: nil, NonResourceURL: false, URL: req.URL.Path, @@ -499,11 +361,11 @@ func GetBootstrapPolicy(masterNamespace string) *authorizationapi.Policy { }, Rules: []authorizationapi.PolicyRule{ { - Verbs: []string{authorizationapi.VerbAll}, - Resources: []string{authorizationapi.ResourceAll}, + Verbs: util.NewStringSet(authorizationapi.VerbAll), + Resources: util.NewStringSet(authorizationapi.ResourceAll), }, { - Verbs: []string{authorizationapi.VerbAll}, + Verbs: util.NewStringSet(authorizationapi.VerbAll), NonResourceURLs: util.NewStringSet(authorizationapi.NonResourceAll), }, }, @@ -515,12 +377,12 @@ func GetBootstrapPolicy(masterNamespace string) *authorizationapi.Policy { }, Rules: []authorizationapi.PolicyRule{ { - Verbs: []string{"get", "list", "watch", "create", "update", "delete"}, - Resources: []string{authorizationapi.OpenshiftExposedGroupName, authorizationapi.PermissionGrantingGroupName, authorizationapi.KubeExposedGroupName}, + Verbs: util.NewStringSet("get", "list", "watch", "create", "update", "delete"), + Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.PermissionGrantingGroupName, authorizationapi.KubeExposedGroupName), }, { - Verbs: []string{"get", "list", "watch"}, - Resources: []string{authorizationapi.PolicyOwnerGroupName, authorizationapi.KubeAllGroupName}, + Verbs: util.NewStringSet("get", "list", "watch"), + Resources: util.NewStringSet(authorizationapi.PolicyOwnerGroupName, authorizationapi.KubeAllGroupName), }, }, }, @@ -531,12 +393,12 @@ func GetBootstrapPolicy(masterNamespace string) *authorizationapi.Policy { }, Rules: []authorizationapi.PolicyRule{ { - Verbs: []string{"get", "list", "watch", "create", "update", "delete"}, - Resources: []string{authorizationapi.OpenshiftExposedGroupName, authorizationapi.KubeExposedGroupName}, + Verbs: util.NewStringSet("get", "list", "watch", "create", "update", "delete"), + Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.KubeExposedGroupName), }, { - Verbs: []string{"get", "list", "watch"}, - Resources: []string{authorizationapi.KubeAllGroupName}, + Verbs: util.NewStringSet("get", "list", "watch"), + Resources: util.NewStringSet(authorizationapi.KubeAllGroupName), }, }, }, @@ -547,8 +409,8 @@ func GetBootstrapPolicy(masterNamespace string) *authorizationapi.Policy { }, Rules: []authorizationapi.PolicyRule{ { - Verbs: []string{"get", "list", "watch"}, - Resources: []string{authorizationapi.OpenshiftExposedGroupName, authorizationapi.KubeAllGroupName}, + Verbs: util.NewStringSet("get", "list", "watch"), + Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.KubeAllGroupName), }, }, }, @@ -558,8 +420,8 @@ func GetBootstrapPolicy(masterNamespace string) *authorizationapi.Policy { Namespace: masterNamespace, }, Rules: []authorizationapi.PolicyRule{ - {Verbs: []string{"get"}, Resources: []string{"users"}, ResourceNames: util.NewStringSet("~")}, - {Verbs: []string{"list"}, Resources: []string{"projects"}}, + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("users"), ResourceNames: util.NewStringSet("~")}, + {Verbs: util.NewStringSet("list"), Resources: util.NewStringSet("projects")}, }, }, "cluster-status": { @@ -569,7 +431,7 @@ func GetBootstrapPolicy(masterNamespace string) *authorizationapi.Policy { }, Rules: []authorizationapi.PolicyRule{ { - Verbs: []string{"get"}, + Verbs: util.NewStringSet("get"), NonResourceURLs: util.NewStringSet("/healthz", "/version", "/api", "/osapi"), }, }, @@ -581,8 +443,8 @@ func GetBootstrapPolicy(masterNamespace string) *authorizationapi.Policy { }, Rules: []authorizationapi.PolicyRule{ { - Verbs: []string{authorizationapi.VerbAll}, - Resources: []string{authorizationapi.ResourceAll}, + Verbs: util.NewStringSet(authorizationapi.VerbAll), + Resources: util.NewStringSet(authorizationapi.ResourceAll), }, }, }, @@ -593,8 +455,8 @@ func GetBootstrapPolicy(masterNamespace string) *authorizationapi.Policy { }, Rules: []authorizationapi.PolicyRule{ { - Verbs: []string{authorizationapi.VerbAll}, - Resources: []string{authorizationapi.ResourceAll}, + Verbs: util.NewStringSet(authorizationapi.VerbAll), + Resources: util.NewStringSet(authorizationapi.ResourceAll), }, }, }, @@ -605,8 +467,8 @@ func GetBootstrapPolicy(masterNamespace string) *authorizationapi.Policy { }, Rules: []authorizationapi.PolicyRule{ { - Verbs: []string{"delete"}, - Resources: []string{"oauthaccesstoken", "oauthauthorizetoken"}, + Verbs: util.NewStringSet("delete"), + Resources: util.NewStringSet("oauthaccesstoken", "oauthauthorizetoken"), }, }, }, @@ -633,7 +495,7 @@ func GetBootstrapPolicyBinding(masterNamespace string) *authorizationapi.PolicyB Name: "system:component", Namespace: masterNamespace, }, - UserNames: []string{"system:openshift-client", "system:kube-client"}, + Users: util.NewStringSet("system:openshift-client", "system:kube-client"), }, "system:deployer-binding": { ObjectMeta: kapi.ObjectMeta{ @@ -644,7 +506,7 @@ func GetBootstrapPolicyBinding(masterNamespace string) *authorizationapi.PolicyB Name: "system:deployer", Namespace: masterNamespace, }, - UserNames: []string{"system:openshift-deployer"}, + Users: util.NewStringSet("system:openshift-deployer"), }, "cluster-admin-binding": { ObjectMeta: kapi.ObjectMeta{ @@ -655,7 +517,7 @@ func GetBootstrapPolicyBinding(masterNamespace string) *authorizationapi.PolicyB Name: "cluster-admin", Namespace: masterNamespace, }, - UserNames: []string{"system:admin"}, + Users: util.NewStringSet("system:admin"), }, "basic-user-binding": { ObjectMeta: kapi.ObjectMeta{ @@ -666,7 +528,7 @@ func GetBootstrapPolicyBinding(masterNamespace string) *authorizationapi.PolicyB Name: "basic-user", Namespace: masterNamespace, }, - GroupNames: []string{"system:authenticated"}, + Groups: util.NewStringSet("system:authenticated"), }, "insecure-cluster-admin-binding": { ObjectMeta: kapi.ObjectMeta{ @@ -678,7 +540,7 @@ func GetBootstrapPolicyBinding(masterNamespace string) *authorizationapi.PolicyB Namespace: masterNamespace, }, // TODO until we decide to enforce policy, simply allow every one access - GroupNames: []string{"system:authenticated", "system:unauthenticated"}, + Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"), }, "system:delete-tokens-binding": { ObjectMeta: kapi.ObjectMeta{ @@ -689,7 +551,7 @@ func GetBootstrapPolicyBinding(masterNamespace string) *authorizationapi.PolicyB Name: "system:delete-tokens", Namespace: masterNamespace, }, - GroupNames: []string{"system:authenticated", "system:unauthenticated"}, + Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"), }, "cluster-status-binding": { ObjectMeta: kapi.ObjectMeta{ @@ -700,7 +562,7 @@ func GetBootstrapPolicyBinding(masterNamespace string) *authorizationapi.PolicyB Name: "cluster-status", Namespace: masterNamespace, }, - GroupNames: []string{"system:authenticated", "system:unauthenticated"}, + Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"), }, }, } diff --git a/pkg/authorization/authorizer/authorizer_test.go b/pkg/authorization/authorizer/authorizer_test.go index b6edb7f9abc9..2b5a9e5e650b 100644 --- a/pkg/authorization/authorizer/authorizer_test.go +++ b/pkg/authorization/authorizer/authorizer_test.go @@ -8,22 +8,23 @@ import ( kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" authorizationapi "github.com/openshift/origin/pkg/authorization/api" testpolicyregistry "github.com/openshift/origin/pkg/authorization/registry/test" + "github.com/openshift/origin/pkg/authorization/rulevalidation" ) const testMasterNamespace = "master" type authorizeTest struct { - globalPolicy []authorizationapi.Policy - namespacedPolicy []authorizationapi.Policy + policies []authorizationapi.Policy policyRetrievalError error - globalPolicyBinding []authorizationapi.PolicyBinding - namespacedPolicyBinding []authorizationapi.PolicyBinding - policyBindingRetrievalError error + bindings []authorizationapi.PolicyBinding + bindingRetrievalError error + context kapi.Context attributes *DefaultAuthorizationAttributes expectedAllowed bool @@ -33,55 +34,51 @@ type authorizeTest struct { func TestResourceNameDeny(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), testMasterNamespace), &user.DefaultInfo{Name: "just-a-user"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "just-a-user", - }, Verb: "get", Resource: "users", ResourceName: "just-a-user", - Namespace: testMasterNamespace, }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() + test.policies = newDefaultGlobalPolicies() + test.bindings = newDefaultGlobalBinding() test.test(t) } func TestResourceNameAllow(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), testMasterNamespace), &user.DefaultInfo{Name: "just-a-user"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "just-a-user", - }, Verb: "get", Resource: "users", ResourceName: "~", - Namespace: testMasterNamespace, }, expectedAllowed: true, expectedReason: "allowed by rule in master", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() + test.policies = newDefaultGlobalPolicies() + test.bindings = newDefaultGlobalBinding() test.test(t) } func TestDeniedWithError(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Anna"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Anna", - }, - Verb: "update", - Resource: "roles", - Namespace: "adze", + Verb: "update", + Resource: "roles", }, expectedAllowed: false, expectedError: "my special error", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.globalPolicyBinding[0].RoleBindings["missing"] = authorizationapi.RoleBinding{ + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings[0].RoleBindings["missing"] = authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{ Name: "missing", Namespace: testMasterNamespace, @@ -90,9 +87,8 @@ func TestDeniedWithError(t *testing.T) { Name: "not-a-real-binding", Namespace: testMasterNamespace, }, - UserNames: []string{"Anna"}, + Users: util.NewStringSet("Anna"), } - test.namespacedPolicy, test.namespacedPolicyBinding = newAdzePolicy() test.policyRetrievalError = errors.New("my special error") test.test(t) @@ -100,19 +96,19 @@ func TestDeniedWithError(t *testing.T) { func TestAllowedWithMissingBinding(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Anna"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Anna", - }, - Verb: "update", - Resource: "roles", - Namespace: "adze", + Verb: "update", + Resource: "roles", }, expectedAllowed: true, expectedReason: "allowed by rule in adze", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.globalPolicyBinding[0].RoleBindings["missing"] = authorizationapi.RoleBinding{ + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings[0].RoleBindings["missing"] = authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{ Name: "missing", Namespace: testMasterNamespace, @@ -121,19 +117,16 @@ func TestAllowedWithMissingBinding(t *testing.T) { Name: "not-a-real-binding", Namespace: testMasterNamespace, }, - UserNames: []string{"Anna"}, + Users: util.NewStringSet("Anna"), } - test.namespacedPolicy, test.namespacedPolicyBinding = newAdzePolicy() + test.test(t) } func TestHealthAllow(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.NewContext(), &user.DefaultInfo{Name: "no-one", Groups: []string{"system:unauthenticated"}}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "no-one", - Groups: []string{"system:unauthenticated"}, - }, Verb: "get", NonResourceURL: true, URL: "/healthz", @@ -141,48 +134,50 @@ func TestHealthAllow(t *testing.T) { expectedAllowed: true, expectedReason: "allowed by rule in master", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() + test.policies = newDefaultGlobalPolicies() + test.bindings = newDefaultGlobalBinding() + test.test(t) } func TestNonResourceAllow(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.NewContext(), &user.DefaultInfo{Name: "ClusterAdmin"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "ClusterAdmin", - }, - Verb: "get", - URL: "not-specified", + Verb: "get", + NonResourceURL: true, + URL: "not-specified", }, expectedAllowed: true, expectedReason: "allowed by rule in master", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() + test.policies = newDefaultGlobalPolicies() + test.bindings = newDefaultGlobalBinding() + test.test(t) } func TestNonResourceDeny(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.NewContext(), &user.DefaultInfo{Name: "no-one"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "no-one", - }, - Verb: "get", - URL: "not-allowed", + Verb: "get", + NonResourceURL: true, + URL: "not-allowed", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() + test.policies = newDefaultGlobalPolicies() + test.bindings = newDefaultGlobalBinding() + test.test(t) } func TestHealthDeny(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.NewContext(), &user.DefaultInfo{Name: "no-one"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "no-one", - }, Verb: "get", NonResourceURL: true, URL: "/healthz", @@ -190,221 +185,200 @@ func TestHealthDeny(t *testing.T) { expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() + test.policies = newDefaultGlobalPolicies() + test.bindings = newDefaultGlobalBinding() + test.test(t) } func TestAdminEditingGlobalDeploymentConfig(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), testMasterNamespace), &user.DefaultInfo{Name: "ClusterAdmin"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "ClusterAdmin", - }, - Verb: "update", - Resource: "deploymentConfigs", - Namespace: testMasterNamespace, + Verb: "update", + Resource: "deploymentConfigs", }, expectedAllowed: true, expectedReason: "allowed by rule in master", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() + test.policies = newDefaultGlobalPolicies() + test.bindings = newDefaultGlobalBinding() + test.test(t) } func TestDisallowedViewingGlobalPods(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), testMasterNamespace), &user.DefaultInfo{Name: "SomeYahoo"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "SomeYahoo", - }, - Verb: "get", - Resource: "pods", - Namespace: testMasterNamespace, + Verb: "get", + Resource: "pods", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() + test.policies = newDefaultGlobalPolicies() + test.bindings = newDefaultGlobalBinding() + test.test(t) } func TestProjectAdminEditPolicy(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Anna"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Anna", - }, - Verb: "update", - Resource: "roles", - Namespace: "adze", + Verb: "update", + Resource: "roles", }, expectedAllowed: true, expectedReason: "allowed by rule in adze", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = newAdzePolicy() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.test(t) } func TestGlobalPolicyOutranksLocalPolicy(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "ClusterAdmin"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "ClusterAdmin", - }, - Verb: "update", - Resource: "roles", - Namespace: "adze", + Verb: "update", + Resource: "roles", }, expectedAllowed: true, expectedReason: "allowed by rule in master", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = newAdzePolicy() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.test(t) } func TestResourceRestrictionsWork(t *testing.T) { test1 := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Rachel"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Rachel", - }, - Verb: "get", - Resource: "buildConfigs", - Namespace: "adze", + Verb: "get", + Resource: "buildConfigs", }, expectedAllowed: true, expectedReason: "allowed by rule in adze", } - test1.globalPolicy, test1.globalPolicyBinding = newDefaultGlobalPolicy() - test1.namespacedPolicy, test1.namespacedPolicyBinding = newAdzePolicy() + test1.policies = newDefaultGlobalPolicies() + test1.policies = append(test1.policies, newAdzePolicies()...) + test1.bindings = newDefaultGlobalBinding() + test1.bindings = append(test1.bindings, newAdzeBindings()...) test1.test(t) test2 := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Rachel"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Rachel", - }, - Verb: "get", - Resource: "pods", - Namespace: "adze", + Verb: "get", + Resource: "pods", }, expectedAllowed: false, expectedReason: "denied by default", } - test2.globalPolicy, test2.globalPolicyBinding = newDefaultGlobalPolicy() - test2.namespacedPolicy, test2.namespacedPolicyBinding = newAdzePolicy() + test2.policies = newDefaultGlobalPolicies() + test2.policies = append(test2.policies, newAdzePolicies()...) + test2.bindings = newDefaultGlobalBinding() + test2.bindings = append(test2.bindings, newAdzeBindings()...) test2.test(t) } func TestResourceRestrictionsWithWeirdWork(t *testing.T) { test1 := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Rachel"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Rachel", - }, - Verb: "get", - Resource: "BUILDCONFIGS", - Namespace: "adze", + Verb: "get", + Resource: "BUILDCONFIGS", }, expectedAllowed: true, expectedReason: "allowed by rule in adze", } - test1.globalPolicy, test1.globalPolicyBinding = newDefaultGlobalPolicy() - test1.namespacedPolicy, test1.namespacedPolicyBinding = newAdzePolicy() + test1.policies = newDefaultGlobalPolicies() + test1.policies = append(test1.policies, newAdzePolicies()...) + test1.bindings = newDefaultGlobalBinding() + test1.bindings = append(test1.bindings, newAdzeBindings()...) test1.test(t) test2 := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Rachel"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Rachel", - }, - Verb: "get", - Resource: "buildconfigs", - Namespace: "adze", + Verb: "get", + Resource: "buildconfigs", }, expectedAllowed: true, expectedReason: "allowed by rule in adze", } - test2.globalPolicy, test2.globalPolicyBinding = newDefaultGlobalPolicy() - test2.namespacedPolicy, test2.namespacedPolicyBinding = newAdzePolicy() + test2.policies = newDefaultGlobalPolicies() + test2.policies = append(test2.policies, newAdzePolicies()...) + test2.bindings = newDefaultGlobalBinding() + test2.bindings = append(test2.bindings, newAdzeBindings()...) test2.test(t) } func TestLocalRightsDoNotGrantGlobalRights(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "backsaw"), &user.DefaultInfo{Name: "Rachel"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Rachel", - }, - Verb: "get", - Resource: "buildConfigs", - Namespace: "backsaw", + Verb: "get", + Resource: "buildConfigs", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = newAdzePolicy() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.test(t) } func TestVerbRestrictionsWork(t *testing.T) { test1 := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Valerie"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Valerie", - }, - Verb: "get", - Resource: "buildConfigs", - Namespace: "adze", + Verb: "get", + Resource: "buildConfigs", }, expectedAllowed: true, expectedReason: "allowed by rule in adze", } - test1.globalPolicy, test1.globalPolicyBinding = newDefaultGlobalPolicy() - test1.namespacedPolicy, test1.namespacedPolicyBinding = newAdzePolicy() + test1.policies = newDefaultGlobalPolicies() + test1.policies = append(test1.policies, newAdzePolicies()...) + test1.bindings = newDefaultGlobalBinding() + test1.bindings = append(test1.bindings, newAdzeBindings()...) test1.test(t) test2 := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Valerie"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Valerie", - }, - Verb: "create", - Resource: "buildConfigs", - Namespace: "adze", + Verb: "create", + Resource: "buildConfigs", }, expectedAllowed: false, expectedReason: "denied by default", } - test2.globalPolicy, test2.globalPolicyBinding = newDefaultGlobalPolicy() - test2.namespacedPolicy, test2.namespacedPolicyBinding = newAdzePolicy() + test2.policies = newDefaultGlobalPolicies() + test2.policies = append(test2.policies, newAdzePolicies()...) + test2.bindings = newDefaultGlobalBinding() + test2.bindings = append(test2.bindings, newAdzeBindings()...) test2.test(t) } func (test *authorizeTest) test(t *testing.T) { - policies := make([]authorizationapi.Policy, 0, 0) - policies = append(policies, test.namespacedPolicy...) - policies = append(policies, test.globalPolicy...) - policyRegistry := &testpolicyregistry.PolicyRegistry{ - Err: test.policyRetrievalError, - MasterNamespace: testMasterNamespace, - Policies: policies, - } - - policyBindings := make([]authorizationapi.PolicyBinding, 0, 0) - policyBindings = append(policyBindings, test.namespacedPolicyBinding...) - policyBindings = append(policyBindings, test.globalPolicyBinding...) - policyBindingRegistry := &testpolicyregistry.PolicyBindingRegistry{ - Err: test.policyBindingRetrievalError, - MasterNamespace: testMasterNamespace, - PolicyBindings: policyBindings, - } - authorizer := NewAuthorizer(testMasterNamespace, policyRegistry, policyBindingRegistry) + policyRegistry := testpolicyregistry.NewPolicyRegistry(test.policies, test.policyRetrievalError) + policyBindingRegistry := testpolicyregistry.NewPolicyBindingRegistry(test.bindings, test.bindingRetrievalError) + authorizer := NewAuthorizer(testMasterNamespace, rulevalidation.NewDefaultRuleResolver(policyRegistry, policyBindingRegistry)) - actualAllowed, actualReason, actualError := authorizer.Authorize(*test.attributes) + actualAllowed, actualReason, actualError := authorizer.Authorize(test.context, *test.attributes) matchBool(test.expectedAllowed, actualAllowed, "allowed", t) if actualAllowed { @@ -457,125 +431,130 @@ func matchError(expected string, actual error, field string, t *testing.T) { } } -func newDefaultGlobalPolicy() ([]authorizationapi.Policy, []authorizationapi.PolicyBinding) { - return []authorizationapi.Policy{*GetBootstrapPolicy(testMasterNamespace)}, - []authorizationapi.PolicyBinding{ - { +func newDefaultGlobalPolicies() []authorizationapi.Policy { + return []authorizationapi.Policy{*GetBootstrapPolicy(testMasterNamespace)} +} +func newDefaultGlobalBinding() []authorizationapi.PolicyBinding { + policyBinding := authorizationapi.PolicyBinding{ + ObjectMeta: kapi.ObjectMeta{ + Name: testMasterNamespace, + Namespace: testMasterNamespace, + }, + RoleBindings: map[string]authorizationapi.RoleBinding{ + "cluster-admins": { ObjectMeta: kapi.ObjectMeta{ - Name: testMasterNamespace, + Name: "cluster-admins", Namespace: testMasterNamespace, }, - RoleBindings: map[string]authorizationapi.RoleBinding{ - "cluster-admins": { - ObjectMeta: kapi.ObjectMeta{ - Name: "cluster-admins", - Namespace: testMasterNamespace, - }, - RoleRef: kapi.ObjectReference{ - Name: "cluster-admin", - Namespace: testMasterNamespace, - }, - UserNames: []string{"ClusterAdmin"}, - GroupNames: []string{"RootUsers"}, - }, - "user-only": { - ObjectMeta: kapi.ObjectMeta{ - Name: "user-only", - Namespace: testMasterNamespace, - }, - RoleRef: kapi.ObjectReference{ - Name: "basic-user", - Namespace: testMasterNamespace, - }, - UserNames: []string{"just-a-user"}, - }, + RoleRef: kapi.ObjectReference{ + Name: "cluster-admin", + Namespace: testMasterNamespace, }, + Users: util.NewStringSet("ClusterAdmin"), + Groups: util.NewStringSet("RootUsers"), }, - *GetBootstrapPolicyBinding(testMasterNamespace), - } + "user-only": { + ObjectMeta: kapi.ObjectMeta{ + Name: "user-only", + Namespace: testMasterNamespace, + }, + RoleRef: kapi.ObjectReference{ + Name: "basic-user", + Namespace: testMasterNamespace, + }, + Users: util.NewStringSet("just-a-user"), + }, + }, + } + for key, value := range GetBootstrapPolicyBinding(testMasterNamespace).RoleBindings { + policyBinding.RoleBindings[key] = value + } + return []authorizationapi.PolicyBinding{policyBinding} } -func newAdzePolicy() ([]authorizationapi.Policy, []authorizationapi.PolicyBinding) { +func newAdzePolicies() []authorizationapi.Policy { return []authorizationapi.Policy{ - { - ObjectMeta: kapi.ObjectMeta{ - Name: authorizationapi.PolicyName, - Namespace: "adze", - }, - Roles: map[string]authorizationapi.Role{ - "restrictedViewer": { - ObjectMeta: kapi.ObjectMeta{ - Name: "admin", - Namespace: "adze", - }, - Rules: append(make([]authorizationapi.PolicyRule, 0), - authorizationapi.PolicyRule{ - Verbs: []string{"watch", "list", "get"}, - Resources: []string{"buildConfigs"}, - }), + { + ObjectMeta: kapi.ObjectMeta{ + Name: authorizationapi.PolicyName, + Namespace: "adze", + }, + Roles: map[string]authorizationapi.Role{ + "restrictedViewer": { + ObjectMeta: kapi.ObjectMeta{ + Name: "admin", + Namespace: "adze", }, + Rules: append(make([]authorizationapi.PolicyRule, 0), + authorizationapi.PolicyRule{ + Verbs: util.NewStringSet("watch", "list", "get"), + Resources: util.NewStringSet("buildConfigs"), + }), }, - }}, - []authorizationapi.PolicyBinding{ - { - ObjectMeta: kapi.ObjectMeta{ - Name: testMasterNamespace, - Namespace: "adze", + }, + }} +} +func newAdzeBindings() []authorizationapi.PolicyBinding { + return []authorizationapi.PolicyBinding{ + { + ObjectMeta: kapi.ObjectMeta{ + Name: testMasterNamespace, + Namespace: "adze", + }, + RoleBindings: map[string]authorizationapi.RoleBinding{ + "projectAdmins": { + ObjectMeta: kapi.ObjectMeta{ + Name: "projectAdmins", + Namespace: "adze", + }, + RoleRef: kapi.ObjectReference{ + Name: "admin", + Namespace: testMasterNamespace, + }, + Users: util.NewStringSet("Anna"), }, - RoleBindings: map[string]authorizationapi.RoleBinding{ - "projectAdmins": { - ObjectMeta: kapi.ObjectMeta{ - Name: "projectAdmins", - Namespace: "adze", - }, - RoleRef: kapi.ObjectReference{ - Name: "admin", - Namespace: testMasterNamespace, - }, - UserNames: []string{"Anna"}, + "viewers": { + ObjectMeta: kapi.ObjectMeta{ + Name: "viewers", + Namespace: "adze", + }, + RoleRef: kapi.ObjectReference{ + Name: "view", + Namespace: testMasterNamespace, }, - "viewers": { - ObjectMeta: kapi.ObjectMeta{ - Name: "viewers", - Namespace: "adze", - }, - RoleRef: kapi.ObjectReference{ - Name: "view", - Namespace: testMasterNamespace, - }, - UserNames: []string{"Valerie"}, + Users: util.NewStringSet("Valerie"), + }, + "editors": { + ObjectMeta: kapi.ObjectMeta{ + Name: "editors", + Namespace: "adze", }, - "editors": { - ObjectMeta: kapi.ObjectMeta{ - Name: "editors", - Namespace: "adze", - }, - RoleRef: kapi.ObjectReference{ - Name: "edit", - Namespace: testMasterNamespace, - }, - UserNames: []string{"Ellen"}, + RoleRef: kapi.ObjectReference{ + Name: "edit", + Namespace: testMasterNamespace, }, + Users: util.NewStringSet("Ellen"), }, }, - { - ObjectMeta: kapi.ObjectMeta{ - Name: "adze", - Namespace: "adze", - }, - RoleBindings: map[string]authorizationapi.RoleBinding{ - "restrictedViewers": { - ObjectMeta: kapi.ObjectMeta{ - Name: "restrictedViewers", - Namespace: "adze", - }, - RoleRef: kapi.ObjectReference{ - Name: "restrictedViewer", - Namespace: "adze", - }, - UserNames: []string{"Rachel"}, + }, + { + ObjectMeta: kapi.ObjectMeta{ + Name: "adze", + Namespace: "adze", + }, + RoleBindings: map[string]authorizationapi.RoleBinding{ + "restrictedViewers": { + ObjectMeta: kapi.ObjectMeta{ + Name: "restrictedViewers", + Namespace: "adze", }, + RoleRef: kapi.ObjectReference{ + Name: "restrictedViewer", + Namespace: "adze", + }, + Users: util.NewStringSet("Rachel"), }, }, - } + }, + } } diff --git a/pkg/authorization/authorizer/bootstrap_policy_test.go b/pkg/authorization/authorizer/bootstrap_policy_test.go index 29219137fd7e..520b17cc011a 100644 --- a/pkg/authorization/authorizer/bootstrap_policy_test.go +++ b/pkg/authorization/authorizer/bootstrap_policy_test.go @@ -5,391 +5,414 @@ import ( kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" authorizationapi "github.com/openshift/origin/pkg/authorization/api" ) func TestViewerGetAllowedKindInMallet(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Victor"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Victor", - }, - Verb: "get", - Resource: "pods", - Namespace: "mallet", + Verb: "get", + Resource: "pods", }, expectedAllowed: true, expectedReason: "allowed by rule in mallet", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestViewerGetAllowedKindInAdze(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Victor"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Victor", - }, - Verb: "get", - Resource: "pods", - Namespace: "adze", + Verb: "get", + Resource: "pods", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestViewerGetDisallowedKindInMallet(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Victor"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Victor", - }, - Verb: "get", - Resource: "policies", - Namespace: "mallet", + Verb: "get", + Resource: "policies", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestViewerGetDisallowedKindInAdze(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Victor"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Victor", - }, - Verb: "get", - Resource: "policies", - Namespace: "adze", + Verb: "get", + Resource: "policies", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestViewerCreateAllowedKindInMallet(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Victor"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Victor", - }, - Verb: "create", - Resource: "pods", - Namespace: "mallet", + Verb: "create", + Resource: "pods", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestViewerCreateAllowedKindInAdze(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Victor"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Victor", - }, - Verb: "create", - Resource: "pods", - Namespace: "adze", + Verb: "create", + Resource: "pods", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestEditorUpdateAllowedKindInMallet(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Edgar"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Edgar", - }, - Verb: "update", - Resource: "pods", - Namespace: "mallet", + Verb: "update", + Resource: "pods", }, expectedAllowed: true, expectedReason: "allowed by rule in mallet", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestEditorUpdateAllowedKindInAdze(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Edgar"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Edgar", - }, - Verb: "update", - Resource: "pods", - Namespace: "adze", + Verb: "update", + Resource: "pods", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestEditorUpdateDisallowedKindInMallet(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Edgar"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Edgar", - }, - Verb: "update", - Resource: "roleBindings", - Namespace: "mallet", + Verb: "update", + Resource: "roleBindings", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestEditorUpdateDisallowedKindInAdze(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Edgar"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Edgar", - }, - Verb: "update", - Resource: "roleBindings", - Namespace: "adze", + Verb: "update", + Resource: "roleBindings", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestEditorGetAllowedKindInMallet(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Edgar"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Edgar", - }, - Verb: "get", - Resource: "pods", - Namespace: "mallet", + Verb: "get", + Resource: "pods", }, expectedAllowed: true, expectedReason: "allowed by rule in mallet", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestEditorGetAllowedKindInAdze(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Edgar"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Edgar", - }, - Verb: "get", - Resource: "pods", - Namespace: "adze", + Verb: "get", + Resource: "pods", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestAdminUpdateAllowedKindInMallet(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Matthew"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Matthew", - }, - Verb: "update", - Resource: "roleBindings", - Namespace: "mallet", + Verb: "update", + Resource: "roleBindings", }, expectedAllowed: true, expectedReason: "allowed by rule in mallet", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestAdminUpdateAllowedKindInAdze(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Matthew"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Matthew", - }, - Verb: "update", - Resource: "roleBindings", - Namespace: "adze", + Verb: "update", + Resource: "roleBindings", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestAdminUpdateDisallowedKindInMallet(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Matthew"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Matthew", - }, - Verb: "update", - Resource: "policies", - Namespace: "mallet", + Verb: "update", + Resource: "policies", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestAdminUpdateDisallowedKindInAdze(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Matthew"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Matthew", - }, - Verb: "update", - Resource: "roles", - Namespace: "adze", + Verb: "update", + Resource: "roles", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestAdminGetAllowedKindInMallet(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Matthew"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Matthew", - }, - Verb: "get", - Resource: "policies", - Namespace: "mallet", + Verb: "get", + Resource: "policies", }, expectedAllowed: true, expectedReason: "allowed by rule in mallet", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } func TestAdminGetAllowedKindInAdze(t *testing.T) { test := &authorizeTest{ + context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Matthew"}), attributes: &DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: "Matthew", - }, - Verb: "get", - Resource: "policies", - Namespace: "adze", + Verb: "get", + Resource: "policies", }, expectedAllowed: false, expectedReason: "denied by default", } - test.globalPolicy, test.globalPolicyBinding = newDefaultGlobalPolicy() - test.namespacedPolicy, test.namespacedPolicyBinding = allNamespacedPolicies() + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.policies = append(test.policies, newMalletPolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) + test.bindings = append(test.bindings, newMalletBindings()...) + test.test(t) } -func allNamespacedPolicies() ([]authorizationapi.Policy, []authorizationapi.PolicyBinding) { - adzePolicy, adzeBinding := newMalletPolicy() - malletPolicy, malletBinding := newMalletPolicy() - - policies := make([]authorizationapi.Policy, 0) - policies = append(policies, adzePolicy...) - policies = append(policies, malletPolicy...) - - bindings := make([]authorizationapi.PolicyBinding, 0) - bindings = append(bindings, adzeBinding...) - bindings = append(bindings, malletBinding...) - - return policies, bindings - +func newMalletPolicies() []authorizationapi.Policy { + return []authorizationapi.Policy{ + { + ObjectMeta: kapi.ObjectMeta{ + Name: authorizationapi.PolicyName, + Namespace: "mallet", + }, + Roles: map[string]authorizationapi.Role{}, + }} } - -func newMalletPolicy() ([]authorizationapi.Policy, []authorizationapi.PolicyBinding) { - return append(make([]authorizationapi.Policy, 0, 0), - authorizationapi.Policy{ - ObjectMeta: kapi.ObjectMeta{ - Name: authorizationapi.PolicyName, - Namespace: "mallet", - }, - Roles: map[string]authorizationapi.Role{}, - }), - append(make([]authorizationapi.PolicyBinding, 0, 0), - authorizationapi.PolicyBinding{ - ObjectMeta: kapi.ObjectMeta{ - Name: testMasterNamespace, - Namespace: "mallet", +func newMalletBindings() []authorizationapi.PolicyBinding { + return []authorizationapi.PolicyBinding{ + { + ObjectMeta: kapi.ObjectMeta{ + Name: testMasterNamespace, + Namespace: "mallet", + }, + RoleBindings: map[string]authorizationapi.RoleBinding{ + "projectAdmins": { + ObjectMeta: kapi.ObjectMeta{ + Name: "projectAdmins", + Namespace: "mallet", + }, + RoleRef: kapi.ObjectReference{ + Name: "admin", + Namespace: testMasterNamespace, + }, + Users: util.NewStringSet("Matthew"), }, - RoleBindings: map[string]authorizationapi.RoleBinding{ - "projectAdmins": { - ObjectMeta: kapi.ObjectMeta{ - Name: "projectAdmins", - Namespace: "mallet", - }, - RoleRef: kapi.ObjectReference{ - Name: "admin", - Namespace: testMasterNamespace, - }, - UserNames: append(make([]string, 0), "Matthew"), + "viewers": { + ObjectMeta: kapi.ObjectMeta{ + Name: "viewers", + Namespace: "mallet", + }, + RoleRef: kapi.ObjectReference{ + Name: "view", + Namespace: testMasterNamespace, }, - "viewers": { - ObjectMeta: kapi.ObjectMeta{ - Name: "viewers", - Namespace: "mallet", - }, - RoleRef: kapi.ObjectReference{ - Name: "view", - Namespace: testMasterNamespace, - }, - UserNames: append(make([]string, 0), "Victor"), + Users: util.NewStringSet("Victor"), + }, + "editors": { + ObjectMeta: kapi.ObjectMeta{ + Name: "editors", + Namespace: "mallet", }, - "editors": { - ObjectMeta: kapi.ObjectMeta{ - Name: "editors", - Namespace: "mallet", - }, - RoleRef: kapi.ObjectReference{ - Name: "edit", - Namespace: testMasterNamespace, - }, - UserNames: append(make([]string, 0), "Edgar"), + RoleRef: kapi.ObjectReference{ + Name: "edit", + Namespace: testMasterNamespace, }, + Users: util.NewStringSet("Edgar"), }, }, - ) + }, + } } diff --git a/pkg/authorization/authorizer/subjects_test.go b/pkg/authorization/authorizer/subjects_test.go index f3d2871d1ae8..6e2e6530d4ba 100644 --- a/pkg/authorization/authorizer/subjects_test.go +++ b/pkg/authorization/authorizer/subjects_test.go @@ -3,16 +3,20 @@ package authorizer import ( "testing" + kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + authorizationapi "github.com/openshift/origin/pkg/authorization/api" testpolicyregistry "github.com/openshift/origin/pkg/authorization/registry/test" + "github.com/openshift/origin/pkg/authorization/rulevalidation" ) type subjectsTest struct { - policies []authorizationapi.Policy - bindings []authorizationapi.PolicyBinding - policyRetrievalError error - policyBindingRetrievalError error + policies []authorizationapi.Policy + bindings []authorizationapi.PolicyBinding + policyRetrievalError error + bindingRetrievalError error + context kapi.Context attributes *DefaultAuthorizationAttributes expectedUsers []string @@ -22,41 +26,28 @@ type subjectsTest struct { func TestSubjects(t *testing.T) { test := &subjectsTest{ + context: kapi.WithNamespace(kapi.NewContext(), "adze"), attributes: &DefaultAuthorizationAttributes{ - Verb: "get", - Resource: "pods", - Namespace: "adze", + Verb: "get", + Resource: "pods", }, expectedUsers: []string{"Anna", "ClusterAdmin", "Ellen", "Valerie", "system:admin", "system:kube-client", "system:openshift-client", "system:openshift-deployer"}, expectedGroups: []string{"RootUsers", "system:authenticated", "system:unauthenticated"}, } - globalPolicy, globalPolicyBinding := newDefaultGlobalPolicy() - namespacedPolicy, namespacedPolicyBinding := newAdzePolicy() - test.policies = make([]authorizationapi.Policy, 0, 0) - test.policies = append(test.policies, namespacedPolicy...) - test.policies = append(test.policies, globalPolicy...) - test.bindings = make([]authorizationapi.PolicyBinding, 0, 0) - test.bindings = append(test.bindings, namespacedPolicyBinding...) - test.bindings = append(test.bindings, globalPolicyBinding...) + test.policies = newDefaultGlobalPolicies() + test.policies = append(test.policies, newAdzePolicies()...) + test.bindings = newDefaultGlobalBinding() + test.bindings = append(test.bindings, newAdzeBindings()...) test.test(t) } func (test *subjectsTest) test(t *testing.T) { - policyRegistry := &testpolicyregistry.PolicyRegistry{ - Err: test.policyRetrievalError, - MasterNamespace: testMasterNamespace, - Policies: test.policies, - } - - policyBindingRegistry := &testpolicyregistry.PolicyBindingRegistry{ - Err: test.policyBindingRetrievalError, - MasterNamespace: testMasterNamespace, - PolicyBindings: test.bindings, - } - authorizer := NewAuthorizer(testMasterNamespace, policyRegistry, policyBindingRegistry) + policyRegistry := testpolicyregistry.NewPolicyRegistry(test.policies, test.policyRetrievalError) + policyBindingRegistry := testpolicyregistry.NewPolicyBindingRegistry(test.bindings, test.bindingRetrievalError) + authorizer := NewAuthorizer(testMasterNamespace, rulevalidation.NewDefaultRuleResolver(policyRegistry, policyBindingRegistry)) - actualUsers, actualGroups, actualError := authorizer.GetAllowedSubjects(*test.attributes) + actualUsers, actualGroups, actualError := authorizer.GetAllowedSubjects(test.context, *test.attributes) matchStringSlice(test.expectedUsers, actualUsers, "users", t) matchStringSlice(test.expectedGroups, actualGroups, "groups", t) diff --git a/pkg/authorization/registry/policy/rest_test.go b/pkg/authorization/registry/policy/rest_test.go index 2a6e9884830a..3c043f60b046 100644 --- a/pkg/authorization/registry/policy/rest_test.go +++ b/pkg/authorization/registry/policy/rest_test.go @@ -31,22 +31,17 @@ func TestGetError(t *testing.T) { } func TestGetValid(t *testing.T) { - registry := test.PolicyRegistry{ - Policies: append(make([]authorizationapi.Policy, 0), - authorizationapi.Policy{ - ObjectMeta: kapi.ObjectMeta{Name: authorizationapi.PolicyName, Namespace: "unittest"}, - }), - } - storage := REST{ - registry: ®istry, - } + testPolicy := authorizationapi.Policy{ObjectMeta: kapi.ObjectMeta{Name: authorizationapi.PolicyName, Namespace: "unittest"}} + registry := test.NewPolicyRegistry([]authorizationapi.Policy{testPolicy}, nil) + storage := REST{registry: registry} + ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") policy, err := storage.Get(ctx, authorizationapi.PolicyName) if err != nil { t.Errorf("got unexpected error: %v", err) return } - if reflect.DeepEqual(policy, registry.Policies[0]) { + if reflect.DeepEqual(policy, testPolicy) { t.Errorf("got unexpected policy: %v", policy) return } @@ -56,9 +51,8 @@ func TestListError(t *testing.T) { registry := test.PolicyRegistry{ Err: errors.New("Sample Error"), } - storage := REST{ - registry: ®istry, - } + storage := REST{registry: ®istry} + ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") _, err := storage.List(ctx, labels.Everything(), labels.Everything()) if err == nil { @@ -72,12 +66,9 @@ func TestListError(t *testing.T) { } func TestListEmpty(t *testing.T) { - registry := test.PolicyRegistry{ - Policies: make([]authorizationapi.Policy, 0), - } - storage := REST{ - registry: ®istry, - } + registry := test.NewPolicyRegistry([]authorizationapi.Policy{}, nil) + storage := REST{registry: registry} + ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") policies, err := storage.List(ctx, labels.Everything(), labels.Everything()) if err != registry.Err { @@ -96,15 +87,10 @@ func TestListEmpty(t *testing.T) { } func TestList(t *testing.T) { - registry := test.PolicyRegistry{ - Policies: append(make([]authorizationapi.Policy, 0), - authorizationapi.Policy{ - ObjectMeta: kapi.ObjectMeta{Name: authorizationapi.PolicyName, Namespace: "unittest"}, - }), - } - storage := REST{ - registry: ®istry, - } + testPolicy := authorizationapi.Policy{ObjectMeta: kapi.ObjectMeta{Name: authorizationapi.PolicyName, Namespace: "unittest"}} + registry := test.NewPolicyRegistry([]authorizationapi.Policy{testPolicy}, nil) + storage := REST{registry: registry} + ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") policies, err := storage.List(ctx, labels.Everything(), labels.Everything()) if err != registry.Err { @@ -138,13 +124,12 @@ func TestDeleteError(t *testing.T) { } func TestDeleteValid(t *testing.T) { - registry := test.PolicyRegistry{} - storage := REST{ - registry: ®istry, - } + testPolicy := authorizationapi.Policy{ObjectMeta: kapi.ObjectMeta{Name: authorizationapi.PolicyName, Namespace: "unittest"}} + registry := test.NewPolicyRegistry([]authorizationapi.Policy{testPolicy}, nil) + storage := REST{registry: registry} ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") - obj, err := storage.Delete(ctx, "foo") + obj, err := storage.Delete(ctx, authorizationapi.PolicyName) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -158,7 +143,7 @@ func TestDeleteValid(t *testing.T) { t.Errorf("Got back non-status result: %v", r) } - if registry.DeletedPolicyName != "foo" { - t.Error("Unexpected policy deleted: %s", registry.DeletedPolicyName) + if policy, _ := registry.GetPolicy(ctx, authorizationapi.PolicyName); policy != nil { + t.Error("Unexpected policy found: %v", policy) } } diff --git a/pkg/authorization/registry/policybinding/rest_test.go b/pkg/authorization/registry/policybinding/rest_test.go index 67437213ef2d..81f5b0283e0e 100644 --- a/pkg/authorization/registry/policybinding/rest_test.go +++ b/pkg/authorization/registry/policybinding/rest_test.go @@ -12,10 +12,9 @@ import ( ) func TestCreateValidationError(t *testing.T) { - registry := test.PolicyBindingRegistry{} - storage := REST{ - registry: ®istry, - } + registry := test.NewPolicyBindingRegistry([]authorizationapi.PolicyBinding{}, nil) + storage := REST{registry: registry} + policyBinding := &authorizationapi.PolicyBinding{ // ObjectMeta: kapi.ObjectMeta{Name: "authTokenName"}, // Missing required field } @@ -28,12 +27,9 @@ func TestCreateValidationError(t *testing.T) { } func TestCreateStorageError(t *testing.T) { - registry := test.PolicyBindingRegistry{ - Err: errors.New("Sample Error"), - } - storage := REST{ - registry: ®istry, - } + registry := test.NewPolicyBindingRegistry([]authorizationapi.PolicyBinding{}, nil) + storage := REST{registry: registry} + policyBinding := &authorizationapi.PolicyBinding{ ObjectMeta: kapi.ObjectMeta{Name: "master"}, PolicyRef: kapi.ObjectReference{Namespace: "master"}, @@ -47,10 +43,9 @@ func TestCreateStorageError(t *testing.T) { } func TestCreateValid(t *testing.T) { - registry := test.PolicyBindingRegistry{} - storage := REST{ - registry: ®istry, - } + registry := test.NewPolicyBindingRegistry([]authorizationapi.PolicyBinding{}, nil) + storage := REST{registry: registry} + policyBinding := &authorizationapi.PolicyBinding{ ObjectMeta: kapi.ObjectMeta{Name: "master", Namespace: "unittest"}, PolicyRef: kapi.ObjectReference{Namespace: "master"}, @@ -92,23 +87,20 @@ func TestGetError(t *testing.T) { } func TestGetValid(t *testing.T) { - registry := test.PolicyBindingRegistry{ - PolicyBindings: append(make([]authorizationapi.PolicyBinding, 0), - authorizationapi.PolicyBinding{ - ObjectMeta: kapi.ObjectMeta{Name: "master", Namespace: "unittest"}, - PolicyRef: kapi.ObjectReference{Namespace: "master"}, - }), - } - storage := REST{ - registry: ®istry, + testBinding := authorizationapi.PolicyBinding{ + ObjectMeta: kapi.ObjectMeta{Name: "master", Namespace: "unittest"}, + PolicyRef: kapi.ObjectReference{Namespace: "master"}, } + registry := test.NewPolicyBindingRegistry([]authorizationapi.PolicyBinding{testBinding}, nil) + storage := REST{registry: registry} + ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") policyBinding, err := storage.Get(ctx, "master") if err != nil { t.Errorf("got unexpected error: %v", err) return } - if reflect.DeepEqual(policyBinding, registry.PolicyBindings[0]) { + if reflect.DeepEqual(policyBinding, testBinding) { t.Errorf("got unexpected policyBinding: %v", policyBinding) return } @@ -134,12 +126,9 @@ func TestListError(t *testing.T) { } func TestListEmpty(t *testing.T) { - registry := test.PolicyBindingRegistry{ - PolicyBindings: make([]authorizationapi.PolicyBinding, 0), - } - storage := REST{ - registry: ®istry, - } + registry := test.NewPolicyBindingRegistry([]authorizationapi.PolicyBinding{}, nil) + storage := REST{registry: registry} + ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") policyBindings, err := storage.List(ctx, labels.Everything(), labels.Everything()) if err != registry.Err { @@ -158,15 +147,13 @@ func TestListEmpty(t *testing.T) { } func TestList(t *testing.T) { - registry := test.PolicyBindingRegistry{ - PolicyBindings: append(make([]authorizationapi.PolicyBinding, 0), - authorizationapi.PolicyBinding{ - ObjectMeta: kapi.ObjectMeta{Name: "master", Namespace: "unittest"}, - }), - } - storage := REST{ - registry: ®istry, + testBinding := authorizationapi.PolicyBinding{ + ObjectMeta: kapi.ObjectMeta{Name: "master", Namespace: "unittest"}, + PolicyRef: kapi.ObjectReference{Namespace: "master"}, } + registry := test.NewPolicyBindingRegistry([]authorizationapi.PolicyBinding{testBinding}, nil) + storage := REST{registry: registry} + ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") policyBindings, err := storage.List(ctx, labels.Everything(), labels.Everything()) if err != registry.Err { @@ -200,10 +187,12 @@ func TestDeleteError(t *testing.T) { } func TestDeleteValid(t *testing.T) { - registry := test.PolicyBindingRegistry{} - storage := REST{ - registry: ®istry, + testBinding := authorizationapi.PolicyBinding{ + ObjectMeta: kapi.ObjectMeta{Name: "foo", Namespace: "unittest"}, + PolicyRef: kapi.ObjectReference{Namespace: "master"}, } + registry := test.NewPolicyBindingRegistry([]authorizationapi.PolicyBinding{testBinding}, nil) + storage := REST{registry: registry} ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") obj, err := storage.Delete(ctx, "foo") @@ -220,7 +209,7 @@ func TestDeleteValid(t *testing.T) { t.Errorf("Got back non-status result: %v", r) } - if registry.DeletedPolicyBindingName != "foo" { - t.Error("Unexpected policyBinding deleted: %s", registry.DeletedPolicyBindingName) + if binding, _ := registry.GetPolicyBinding(ctx, "foo"); binding != nil { + t.Error("Unexpected binding found: %v", binding) } } diff --git a/pkg/authorization/registry/resourceaccessreview/rest.go b/pkg/authorization/registry/resourceaccessreview/rest.go index 673261f78294..983467fadf95 100644 --- a/pkg/authorization/registry/resourceaccessreview/rest.go +++ b/pkg/authorization/registry/resourceaccessreview/rest.go @@ -36,12 +36,11 @@ func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, err namespace := kapi.NamespaceValue(ctx) attributes := &authorizer.DefaultAuthorizationAttributes{ - Verb: resourceAccessReview.Verb, - Resource: resourceAccessReview.Resource, - Namespace: namespace, + Verb: resourceAccessReview.Verb, + Resource: resourceAccessReview.Resource, } - users, groups, err := r.authorizer.GetAllowedSubjects(attributes) + users, groups, err := r.authorizer.GetAllowedSubjects(ctx, attributes) if err != nil { return nil, err } diff --git a/pkg/authorization/registry/resourceaccessreview/rest_test.go b/pkg/authorization/registry/resourceaccessreview/rest_test.go index 0c203cfeca18..52a39e537103 100644 --- a/pkg/authorization/registry/resourceaccessreview/rest_test.go +++ b/pkg/authorization/registry/resourceaccessreview/rest_test.go @@ -25,10 +25,10 @@ type testAuthorizer struct { actualAttributes *authorizer.DefaultAuthorizationAttributes } -func (a *testAuthorizer) Authorize(attributes authorizer.AuthorizationAttributes) (allowed bool, reason string, err error) { +func (a *testAuthorizer) Authorize(ctx kapi.Context, attributes authorizer.AuthorizationAttributes) (allowed bool, reason string, err error) { return false, "", errors.New("unsupported") } -func (a *testAuthorizer) GetAllowedSubjects(passedAttributes authorizer.AuthorizationAttributes) ([]string, []string, error) { +func (a *testAuthorizer) GetAllowedSubjects(ctx kapi.Context, passedAttributes authorizer.AuthorizationAttributes) ([]string, []string, error) { attributes, ok := passedAttributes.(*authorizer.DefaultAuthorizationAttributes) if !ok { return nil, nil, errors.New("unexpected type for test") @@ -99,9 +99,8 @@ func (r *resourceAccessTest) runTest(t *testing.T) { } expectedAttributes := &authorizer.DefaultAuthorizationAttributes{ - Verb: r.reviewRequest.Verb, - Resource: r.reviewRequest.Resource, - Namespace: namespace, + Verb: r.reviewRequest.Verb, + Resource: r.reviewRequest.Resource, } ctx := kapi.WithNamespace(kapi.NewContext(), namespace) diff --git a/pkg/authorization/registry/role/rest_test.go b/pkg/authorization/registry/role/rest_test.go index b6af8828a635..96023d3579df 100644 --- a/pkg/authorization/registry/role/rest_test.go +++ b/pkg/authorization/registry/role/rest_test.go @@ -11,10 +11,9 @@ import ( ) func TestCreateValidationError(t *testing.T) { - registry := &test.PolicyRegistry{} - storage := REST{ - registry: registry, - } + registry := test.NewPolicyRegistry([]authorizationapi.Policy{}, nil) + storage := REST{registry: registry} + role := &authorizationapi.Role{} ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") @@ -25,12 +24,9 @@ func TestCreateValidationError(t *testing.T) { } func TestCreateStorageError(t *testing.T) { - registry := &test.PolicyRegistry{} - registry.Err = errors.New("Sample Error") + registry := test.NewPolicyRegistry([]authorizationapi.Policy{}, errors.New("Sample Error")) + storage := REST{registry: registry} - storage := REST{ - registry: registry, - } role := &authorizationapi.Role{ ObjectMeta: kapi.ObjectMeta{Name: "my-role"}, } @@ -47,14 +43,12 @@ func TestCreateStorageError(t *testing.T) { } func TestCreateValid(t *testing.T) { - registry := &test.PolicyRegistry{} - storage := REST{ - registry: registry, - } - registry.Policies = append(make([]authorizationapi.Policy, 0), - authorizationapi.Policy{ - ObjectMeta: kapi.ObjectMeta{Name: authorizationapi.PolicyName, Namespace: "unittest"}, - }) + registry := test.NewPolicyRegistry( + []authorizationapi.Policy{ + {ObjectMeta: kapi.ObjectMeta{Name: authorizationapi.PolicyName, Namespace: "unittest"}}, + }, + nil) + storage := REST{registry: registry} role := &authorizationapi.Role{ ObjectMeta: kapi.ObjectMeta{Name: "my-role"}, @@ -77,17 +71,17 @@ func TestCreateValid(t *testing.T) { } func TestUpdate(t *testing.T) { - registry := &test.PolicyRegistry{} - storage := REST{ - registry: registry, - } - registry.Policies = append(make([]authorizationapi.Policy, 0), - authorizationapi.Policy{ - ObjectMeta: kapi.ObjectMeta{Name: authorizationapi.PolicyName, Namespace: "unittest"}, - Roles: map[string]authorizationapi.Role{ - "my-role": {ObjectMeta: kapi.ObjectMeta{Name: "my-role"}}, + registry := test.NewPolicyRegistry( + []authorizationapi.Policy{ + { + ObjectMeta: kapi.ObjectMeta{Name: authorizationapi.PolicyName, Namespace: "unittest"}, + Roles: map[string]authorizationapi.Role{ + "my-role": {ObjectMeta: kapi.ObjectMeta{Name: "my-role"}}, + }, }, - }) + }, + nil) + storage := REST{registry: registry} role := &authorizationapi.Role{ ObjectMeta: kapi.ObjectMeta{Name: "my-role"}, @@ -114,14 +108,14 @@ func TestUpdate(t *testing.T) { } func TestUpdateError(t *testing.T) { - registry := &test.PolicyRegistry{} - storage := REST{ - registry: registry, - } - registry.Policies = append(make([]authorizationapi.Policy, 0), - authorizationapi.Policy{ - ObjectMeta: kapi.ObjectMeta{Name: authorizationapi.PolicyName, Namespace: "unittest"}, - }) + registry := test.NewPolicyRegistry( + []authorizationapi.Policy{ + { + ObjectMeta: kapi.ObjectMeta{Name: authorizationapi.PolicyName, Namespace: "unittest"}, + }, + }, + nil) + storage := REST{registry: registry} role := &authorizationapi.Role{ ObjectMeta: kapi.ObjectMeta{Name: "my-role"}, @@ -140,12 +134,8 @@ func TestUpdateError(t *testing.T) { } func TestDeleteError(t *testing.T) { - registry := &test.PolicyRegistry{} - - registry.Err = errors.New("Sample Error") - storage := REST{ - registry: registry, - } + registry := test.NewPolicyRegistry([]authorizationapi.Policy{}, errors.New("Sample Error")) + storage := REST{registry: registry} ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") _, err := storage.Delete(ctx, "foo") @@ -155,17 +145,17 @@ func TestDeleteError(t *testing.T) { } func TestDeleteValid(t *testing.T) { - registry := &test.PolicyRegistry{} - storage := REST{ - registry: registry, - } - registry.Policies = append(make([]authorizationapi.Policy, 0), - authorizationapi.Policy{ - ObjectMeta: kapi.ObjectMeta{Name: authorizationapi.PolicyName, Namespace: "unittest"}, - Roles: map[string]authorizationapi.Role{ - "foo": {ObjectMeta: kapi.ObjectMeta{Name: "foo"}}, + registry := test.NewPolicyRegistry( + []authorizationapi.Policy{ + { + ObjectMeta: kapi.ObjectMeta{Name: authorizationapi.PolicyName, Namespace: "unittest"}, + Roles: map[string]authorizationapi.Role{ + "foo": {ObjectMeta: kapi.ObjectMeta{Name: "foo"}}, + }, }, - }) + }, + nil) + storage := REST{registry: registry} ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") obj, err := storage.Delete(ctx, "foo") diff --git a/pkg/authorization/registry/rolebinding/registry.go b/pkg/authorization/registry/rolebinding/registry.go new file mode 100644 index 000000000000..cc6aaf924469 --- /dev/null +++ b/pkg/authorization/registry/rolebinding/registry.go @@ -0,0 +1,22 @@ +package rolebinding + +import ( + kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + klabels "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + + authorizationapi "github.com/openshift/origin/pkg/authorization/api" +) + +// Registry is an interface for things that know how to store RoleBindings. +type Registry interface { + // ListRoleBindings obtains list of policyRoleBindings that match a selector. + ListRoleBindings(ctx kapi.Context, labels, fields klabels.Selector) (*authorizationapi.RoleBindingList, error) + // GetRoleBinding retrieves a specific policyRoleBinding. + GetRoleBinding(ctx kapi.Context, id string) (*authorizationapi.RoleBinding, error) + // CreateRoleBinding creates a new policyRoleBinding. + CreateRoleBinding(ctx kapi.Context, policyRoleBinding *authorizationapi.RoleBinding) error + // UpdateRoleBinding updates a policyRoleBinding. + UpdateRoleBinding(ctx kapi.Context, policyRoleBinding *authorizationapi.RoleBinding) error + // DeleteRoleBinding deletes a policyRoleBinding. + DeleteRoleBinding(ctx kapi.Context, id string) error +} diff --git a/pkg/authorization/registry/rolebinding/rest.go b/pkg/authorization/registry/rolebinding/rest.go index 407d246a4750..936a37e47605 100644 --- a/pkg/authorization/registry/rolebinding/rest.go +++ b/pkg/authorization/registry/rolebinding/rest.go @@ -2,36 +2,26 @@ package rolebinding import ( "fmt" - "strings" kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" - klabels "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" authorizationapi "github.com/openshift/origin/pkg/authorization/api" "github.com/openshift/origin/pkg/authorization/api/validation" - policyregistry "github.com/openshift/origin/pkg/authorization/registry/policy" - policybindingregistry "github.com/openshift/origin/pkg/authorization/registry/policybinding" - userregistry "github.com/openshift/origin/pkg/user/registry/user" ) // TODO add get and list -// TODO prevent privilege escalation // REST implements the RESTStorage interface in terms of an Registry. type REST struct { - bindingRegistry policybindingregistry.Registry - policyRegistry policyregistry.Registry - userRegistry userregistry.Registry - masterAuthorizationNamespace string + registry Registry } // NewREST creates a new REST for policies. -func NewREST(bindingRegistry policybindingregistry.Registry, policyRegistry policyregistry.Registry, userRegistry userregistry.Registry, masterAuthorizationNamespace string) apiserver.RESTStorage { - return &REST{bindingRegistry, policyRegistry, userRegistry, masterAuthorizationNamespace} +func NewREST(registry Registry) apiserver.RESTStorage { + return &REST{registry} } // New creates a new RoleBinding object @@ -39,23 +29,12 @@ func (r *REST) New() runtime.Object { return &authorizationapi.RoleBinding{} } -// Delete asynchronously deletes the Policy specified by its id. -func (r *REST) Delete(ctx kapi.Context, id string) (runtime.Object, error) { - owningPolicyBinding, err := r.LocatePolicyBinding(ctx, id) - if err != nil { - return nil, err - } - if owningPolicyBinding == nil { - return nil, fmt.Errorf("roleBinding %v does not exist", id) - } - - delete(owningPolicyBinding.RoleBindings, id) - owningPolicyBinding.LastModified = util.Now() - - return &kapi.Status{Status: kapi.StatusSuccess}, r.bindingRegistry.UpdatePolicyBinding(ctx, owningPolicyBinding) +// Delete asynchronously deletes the PolicyBinding specified by its name. +func (r *REST) Delete(ctx kapi.Context, name string) (runtime.Object, error) { + return &kapi.Status{Status: kapi.StatusSuccess}, r.registry.DeleteRoleBinding(ctx, name) } -// Create registers a given new RoleBinding inside the Policy instance to r.bindingRegistry. +// Create registers a given new RoleBinding inside the PolicyBinding instance to r.bindingRegistry. func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) { roleBinding, ok := obj.(*authorizationapi.RoleBinding) if !ok { @@ -70,33 +49,14 @@ func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, err return nil, kerrors.NewInvalid("roleBinding", roleBinding.Name, errs) } - if err := r.validateReferentialIntegrity(ctx, roleBinding); err != nil { - return nil, err - } - if err := r.confirmRoleBindingNameUnique(ctx, roleBinding.Name); err != nil { - return nil, err - } - - policyBinding, err := r.GetPolicyBinding(ctx, roleBinding.RoleRef.Namespace) + err := r.registry.CreateRoleBinding(ctx, roleBinding) if err != nil { return nil, err } - - _, exists := policyBinding.RoleBindings[roleBinding.Name] - if exists { - return nil, fmt.Errorf("roleBinding %v already exists", roleBinding.Name) - } - - policyBinding.RoleBindings[roleBinding.Name] = *roleBinding - policyBinding.LastModified = util.Now() - - if err := r.bindingRegistry.UpdatePolicyBinding(ctx, policyBinding); err != nil { - return nil, err - } return roleBinding, nil } -// Update replaces a given RoleBinding inside the Policy instance with an existing instance in r.bindingRegistry. +// Update replaces a given RoleBinding inside the PolicyBinding instance with an existing instance in r.bindingRegistry. func (r *REST) Update(ctx kapi.Context, obj runtime.Object) (runtime.Object, bool, error) { roleBinding, ok := obj.(*authorizationapi.RoleBinding) if !ok { @@ -110,160 +70,9 @@ func (r *REST) Update(ctx kapi.Context, obj runtime.Object) (runtime.Object, boo return nil, false, kerrors.NewInvalid("roleBinding", roleBinding.Name, errs) } - if err := r.validateReferentialIntegrity(ctx, roleBinding); err != nil { - return nil, false, err - } - - existingRoleBinding, err := r.GetRoleBinding(ctx, roleBinding.Name) + err := r.registry.UpdateRoleBinding(ctx, roleBinding) if err != nil { return nil, false, err } - if existingRoleBinding == nil { - return nil, false, fmt.Errorf("roleBinding %v does not exist", roleBinding.Name) - } - if existingRoleBinding.RoleRef.Namespace != roleBinding.RoleRef.Namespace { - return nil, false, fmt.Errorf("cannot change roleBinding.RoleRef.Namespace from %v to %v", existingRoleBinding.RoleRef.Namespace, roleBinding.RoleRef.Namespace) - } - - policyBinding, err := r.GetPolicyBinding(ctx, roleBinding.RoleRef.Namespace) - if err != nil { - return nil, false, err - } - - _, exists := policyBinding.RoleBindings[roleBinding.Name] - if !exists { - return nil, false, fmt.Errorf("roleBinding %v does not exist", roleBinding.Name) - } - - policyBinding.RoleBindings[roleBinding.Name] = *roleBinding - policyBinding.LastModified = util.Now() - - if err := r.bindingRegistry.UpdatePolicyBinding(ctx, policyBinding); err != nil { - return nil, false, err - } return roleBinding, false, nil } - -func (r *REST) validateReferentialIntegrity(ctx kapi.Context, roleBinding *authorizationapi.RoleBinding) error { - if err := r.confirmRoleExists(roleBinding.RoleRef); err != nil { - return err - } - - return nil -} - -func (r *REST) confirmRoleBindingNameUnique(ctx kapi.Context, bindingName string) error { - policyBinding, err := r.LocatePolicyBinding(ctx, bindingName) - if err != nil { - return err - } - - if policyBinding != nil { - return fmt.Errorf("%v already exists", bindingName) - } - - return nil -} - -func (r *REST) confirmRoleExists(roleRef kapi.ObjectReference) error { - ctx := kapi.WithNamespace(kapi.NewContext(), roleRef.Namespace) - - policy, err := r.policyRegistry.GetPolicy(ctx, authorizationapi.PolicyName) - if err != nil { - return fmt.Errorf("policy %v not found: %v", roleRef.Namespace, err) - } - - if _, exists := policy.Roles[roleRef.Name]; !exists { - return fmt.Errorf("role %v not found", roleRef.Name) - } - - return nil -} - -func (r *REST) confirmUsersExist(userNames []string) error { - for _, userName := range userNames { - if _, err := r.userRegistry.GetUser(userName); err != nil { - return fmt.Errorf("user %v not found: %v", userName, err) - } - } - - return nil -} - -// EnsurePolicyBindingToMaster returns a PolicyBinding object that has a PolicyRef pointing to the Policy in the passed namespace. -func (r *REST) EnsurePolicyBindingToMaster(ctx kapi.Context) (*authorizationapi.PolicyBinding, error) { - policyBinding, err := r.bindingRegistry.GetPolicyBinding(ctx, r.masterAuthorizationNamespace) - if err != nil { - if !strings.Contains(err.Error(), "not found") { - return nil, err - } - - // if we have no policyBinding, go ahead and make one. creating one here collapses code paths below. We only take this hit once - policyBinding = policybindingregistry.NewEmptyPolicyBinding(kapi.NamespaceValue(ctx), r.masterAuthorizationNamespace) - if err := r.bindingRegistry.CreatePolicyBinding(ctx, policyBinding); err != nil { - return nil, err - } - - policyBinding, err = r.bindingRegistry.GetPolicyBinding(ctx, r.masterAuthorizationNamespace) - if err != nil { - return nil, err - } - } - - if policyBinding.RoleBindings == nil { - policyBinding.RoleBindings = make(map[string]authorizationapi.RoleBinding) - } - - return policyBinding, nil -} - -// Returns a PolicyBinding that points to the specified policyNamespace. It will autocreate ONLY if policyNamespace equals the master namespace -func (r *REST) GetPolicyBinding(ctx kapi.Context, policyNamespace string) (*authorizationapi.PolicyBinding, error) { - // we can autocreate a PolicyBinding object if the RoleBinding is for the master namespace - if policyNamespace == r.masterAuthorizationNamespace { - return r.EnsurePolicyBindingToMaster(ctx) - } - - policyBinding, err := r.bindingRegistry.GetPolicyBinding(ctx, policyNamespace) - if err != nil { - return nil, err - } - - if policyBinding.RoleBindings == nil { - policyBinding.RoleBindings = make(map[string]authorizationapi.RoleBinding) - } - - return policyBinding, nil -} - -func (r *REST) LocatePolicyBinding(ctx kapi.Context, roleBindingName string) (*authorizationapi.PolicyBinding, error) { - policyBindingList, err := r.bindingRegistry.ListPolicyBindings(ctx, klabels.Everything(), klabels.Everything()) - if err != nil { - return nil, err - } - - for _, policyBinding := range policyBindingList.Items { - _, exists := policyBinding.RoleBindings[roleBindingName] - if exists { - return &policyBinding, nil - } - } - - return nil, nil -} - -func (r *REST) GetRoleBinding(ctx kapi.Context, roleBindingName string) (*authorizationapi.RoleBinding, error) { - policyBindingList, err := r.bindingRegistry.ListPolicyBindings(ctx, klabels.Everything(), klabels.Everything()) - if err != nil { - return nil, err - } - - for _, policyBinding := range policyBindingList.Items { - roleBinding, exists := policyBinding.RoleBindings[roleBindingName] - if exists { - return &roleBinding, nil - } - } - - return nil, nil -} diff --git a/pkg/authorization/registry/rolebinding/rest_test.go b/pkg/authorization/registry/rolebinding/rest_test.go index 6b3938361446..4fd44725f581 100644 --- a/pkg/authorization/registry/rolebinding/rest_test.go +++ b/pkg/authorization/registry/rolebinding/rest_test.go @@ -6,61 +6,72 @@ import ( "testing" kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + authorizationapi "github.com/openshift/origin/pkg/authorization/api" "github.com/openshift/origin/pkg/authorization/registry/test" - usertest "github.com/openshift/origin/pkg/user/registry/test" ) -func makeSimpleStorage() (*REST, *test.PolicyBindingRegistry) { - bindingRegistry := &test.PolicyBindingRegistry{} - policyRegistry := &test.PolicyRegistry{} - policyRegistry.Policies = []authorizationapi.Policy{ +func testNewBaseBindings() []authorizationapi.PolicyBinding { + return []authorizationapi.PolicyBinding{ + { + ObjectMeta: kapi.ObjectMeta{Name: "master", Namespace: "master"}, + PolicyRef: kapi.ObjectReference{Namespace: "master"}, + RoleBindings: map[string]authorizationapi.RoleBinding{ + "cluster-admins": { + ObjectMeta: kapi.ObjectMeta{Name: "cluster-admins"}, + RoleRef: kapi.ObjectReference{Namespace: "master", Name: "cluster-admin"}, + Users: util.NewStringSet("system:admin"), + }, + }, + }, + } +} +func testNewBasePolicies() []authorizationapi.Policy { + return []authorizationapi.Policy{ { ObjectMeta: kapi.ObjectMeta{Name: authorizationapi.PolicyName, Namespace: "master"}, Roles: map[string]authorizationapi.Role{ - "admin": {ObjectMeta: kapi.ObjectMeta{Name: "admin"}}, + "cluster-admin": { + ObjectMeta: kapi.ObjectMeta{Name: "cluster-admin"}, + Rules: []authorizationapi.PolicyRule{{Verbs: util.NewStringSet("*"), Resources: util.NewStringSet("*")}}, + }, + "admin": { + ObjectMeta: kapi.ObjectMeta{Name: "admin"}, + Rules: []authorizationapi.PolicyRule{{Verbs: util.NewStringSet("*"), Resources: util.NewStringSet("*")}}, + }, }, - }} - userRegistry := &usertest.UserRegistry{} + }, + } +} + +func makeTestStorage() *REST { + bindingRegistry := test.NewPolicyBindingRegistry(testNewBaseBindings(), nil) + policyRegistry := test.NewPolicyRegistry(testNewBasePolicies(), nil) - return &REST{bindingRegistry, policyRegistry, userRegistry, "master"}, bindingRegistry + return &REST{NewVirtualRegistry(bindingRegistry, policyRegistry, "master")} } func TestCreateValidationError(t *testing.T) { - storage, _ := makeSimpleStorage() + storage := makeTestStorage() roleBinding := &authorizationapi.RoleBinding{} - ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") + ctx := kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "unittest"), &user.DefaultInfo{Name: "system:admin"}) _, err := storage.Create(ctx, roleBinding) if err == nil { t.Errorf("Expected validation error") } } -func TestCreateStorageError(t *testing.T) { - storage, registry := makeSimpleStorage() - registry.Err = errors.New("Sample Error") - - roleBinding := &authorizationapi.RoleBinding{ - ObjectMeta: kapi.ObjectMeta{Name: "my-roleBinding"}, - RoleRef: kapi.ObjectReference{Name: "admin", Namespace: "master"}, - } - - ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") - _, err := storage.Create(ctx, roleBinding) - if err != registry.Err { - t.Errorf("unexpected error: %v", err) - } -} - func TestCreateValidAutoCreateMasterPolicyBindings(t *testing.T) { - storage, _ := makeSimpleStorage() + storage := makeTestStorage() roleBinding := &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{Name: "my-roleBinding"}, RoleRef: kapi.ObjectReference{Name: "admin", Namespace: "master"}, } - ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") + ctx := kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "unittest"), &user.DefaultInfo{Name: "system:admin"}) obj, err := storage.Create(ctx, roleBinding) if err != nil { t.Errorf("unexpected error: %v", err) @@ -77,18 +88,18 @@ func TestCreateValidAutoCreateMasterPolicyBindings(t *testing.T) { } func TestCreateValid(t *testing.T) { - storage, registry := makeSimpleStorage() - registry.PolicyBindings = append(make([]authorizationapi.PolicyBinding, 0), - authorizationapi.PolicyBinding{ - ObjectMeta: kapi.ObjectMeta{Name: "master", Namespace: "unittest"}, - }) + ctx := kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "unittest"), &user.DefaultInfo{Name: "system:admin"}) + + storage := makeTestStorage() + storage.Create(ctx, &authorizationapi.PolicyBinding{ + ObjectMeta: kapi.ObjectMeta{Name: "master", Namespace: "unittest"}, + }) roleBinding := &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{Name: "my-roleBinding"}, RoleRef: kapi.ObjectReference{Name: "admin", Namespace: "master"}, } - ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") obj, err := storage.Create(ctx, roleBinding) if err != nil { t.Errorf("unexpected error: %v", err) @@ -105,24 +116,19 @@ func TestCreateValid(t *testing.T) { } func TestUpdate(t *testing.T) { - storage, registry := makeSimpleStorage() - registry.PolicyBindings = append(make([]authorizationapi.PolicyBinding, 0), - authorizationapi.PolicyBinding{ - ObjectMeta: kapi.ObjectMeta{Name: "master", Namespace: "unittest"}, - RoleBindings: map[string]authorizationapi.RoleBinding{ - "my-roleBinding": { - ObjectMeta: kapi.ObjectMeta{Name: "my-roleBinding"}, - RoleRef: kapi.ObjectReference{Name: "admin", Namespace: "master"}, - }, - }, - }) + ctx := kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "unittest"), &user.DefaultInfo{Name: "system:admin"}) + + storage := makeTestStorage() + storage.Create(ctx, &authorizationapi.RoleBinding{ + ObjectMeta: kapi.ObjectMeta{Name: "my-roleBinding"}, + RoleRef: kapi.ObjectReference{Name: "admin", Namespace: "master"}, + }) roleBinding := &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{Name: "my-roleBinding"}, RoleRef: kapi.ObjectReference{Name: "admin", Namespace: "master"}, } - ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") obj, created, err := storage.Update(ctx, roleBinding) if err != nil || created { t.Errorf("Unexpected error %v", err) @@ -143,24 +149,25 @@ func TestUpdate(t *testing.T) { } func TestUpdateError(t *testing.T) { - storage, registry := makeSimpleStorage() - registry.PolicyBindings = append(make([]authorizationapi.PolicyBinding, 0), - authorizationapi.PolicyBinding{ - ObjectMeta: kapi.ObjectMeta{Name: "master", Namespace: "unittest"}, - }) + ctx := kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "unittest"), &user.DefaultInfo{Name: "system:admin"}) + + storage := makeTestStorage() + storage.Create(ctx, &authorizationapi.RoleBinding{ + ObjectMeta: kapi.ObjectMeta{Name: "my-different"}, + RoleRef: kapi.ObjectReference{Name: "admin", Namespace: "master"}, + }) roleBinding := &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{Name: "my-roleBinding"}, RoleRef: kapi.ObjectReference{Name: "admin", Namespace: "master"}, } - ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") _, _, err := storage.Update(ctx, roleBinding) if err == nil { t.Errorf("Missing expected error") return } - expectedErr := "roleBinding my-roleBinding does not exist" + expectedErr := "RoleBinding my-roleBinding not found" if err.Error() != expectedErr { t.Errorf("Expected %v, got %v", expectedErr, err) } @@ -171,10 +178,9 @@ func TestDeleteError(t *testing.T) { registry.Err = errors.New("Sample Error") policyRegistry := &test.PolicyRegistry{} - userRegistry := &usertest.UserRegistry{} - storage := &REST{registry, policyRegistry, userRegistry, "master"} + storage := &REST{NewVirtualRegistry(registry, policyRegistry, "master")} - ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") + ctx := kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "unittest"), &user.DefaultInfo{Name: "system:admin"}) _, err := storage.Delete(ctx, "foo") if err != registry.Err { t.Errorf("unexpected error: %v", err) @@ -182,20 +188,10 @@ func TestDeleteError(t *testing.T) { } func TestDeleteValid(t *testing.T) { - registry := &test.PolicyBindingRegistry{} - policyRegistry := &test.PolicyRegistry{} - userRegistry := &usertest.UserRegistry{} - storage := &REST{registry, policyRegistry, userRegistry, "master"} - registry.PolicyBindings = append(make([]authorizationapi.PolicyBinding, 0), - authorizationapi.PolicyBinding{ - ObjectMeta: kapi.ObjectMeta{Name: "master", Namespace: "unittest"}, - RoleBindings: map[string]authorizationapi.RoleBinding{ - "foo": {ObjectMeta: kapi.ObjectMeta{Name: "foo"}}, - }, - }) + storage := makeTestStorage() - ctx := kapi.WithNamespace(kapi.NewContext(), "unittest") - obj, err := storage.Delete(ctx, "foo") + ctx := kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "master"), &user.DefaultInfo{Name: "system:admin"}) + obj, err := storage.Delete(ctx, "cluster-admins") if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/pkg/authorization/registry/rolebinding/virtual_registry.go b/pkg/authorization/registry/rolebinding/virtual_registry.go new file mode 100644 index 000000000000..1d271d142c4e --- /dev/null +++ b/pkg/authorization/registry/rolebinding/virtual_registry.go @@ -0,0 +1,252 @@ +package rolebinding + +import ( + "fmt" + "strings" + + kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + klabels "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + + authorizationapi "github.com/openshift/origin/pkg/authorization/api" + policyregistry "github.com/openshift/origin/pkg/authorization/registry/policy" + policybindingregistry "github.com/openshift/origin/pkg/authorization/registry/policybinding" + "github.com/openshift/origin/pkg/authorization/rulevalidation" +) + +type VirtualRegistry struct { + bindingRegistry policybindingregistry.Registry + policyRegistry policyregistry.Registry + masterAuthorizationNamespace string +} + +// NewVirtualRegistry creates a new REST for policies. +func NewVirtualRegistry(bindingRegistry policybindingregistry.Registry, policyRegistry policyregistry.Registry, masterAuthorizationNamespace string) Registry { + return &VirtualRegistry{bindingRegistry, policyRegistry, masterAuthorizationNamespace} +} + +func (m *VirtualRegistry) ListRoleBindings(ctx kapi.Context, labels, fields klabels.Selector) (*authorizationapi.RoleBindingList, error) { + policyBindingList, err := m.bindingRegistry.ListPolicyBindings(ctx, klabels.Everything(), klabels.Everything()) + if err != nil { + return nil, err + } + + roleBindingList := &authorizationapi.RoleBindingList{} + + for _, policyBinding := range policyBindingList.Items { + for _, roleBinding := range policyBinding.RoleBindings { + roleBindingList.Items = append(roleBindingList.Items, roleBinding) + } + } + + return roleBindingList, nil +} + +func (m *VirtualRegistry) GetRoleBinding(ctx kapi.Context, name string) (*authorizationapi.RoleBinding, error) { + policyBinding, err := m.getPolicyBindingOwningRoleBinding(ctx, name) + if err != nil { + return nil, err + } + if policyBinding == nil { + return nil, fmt.Errorf("RoleBinding %v not found", name) + } + + binding := policyBinding.RoleBindings[name] + return &binding, nil +} + +func (m *VirtualRegistry) DeleteRoleBinding(ctx kapi.Context, name string) error { + owningPolicyBinding, err := m.getPolicyBindingOwningRoleBinding(ctx, name) + if err != nil { + return err + } + if owningPolicyBinding == nil { + return fmt.Errorf("roleBinding %v does not exist", name) + } + + delete(owningPolicyBinding.RoleBindings, name) + owningPolicyBinding.LastModified = util.Now() + + return m.bindingRegistry.UpdatePolicyBinding(ctx, owningPolicyBinding) +} + +func (m *VirtualRegistry) CreateRoleBinding(ctx kapi.Context, roleBinding *authorizationapi.RoleBinding) error { + if err := m.validateReferentialIntegrity(ctx, roleBinding); err != nil { + return err + } + if err := m.confirmNoEscalaton(ctx, roleBinding); err != nil { + return err + } + + policyBinding, err := m.getPolicyBindingForPolicy(ctx, roleBinding.RoleRef.Namespace) + if err != nil { + return err + } + + _, exists := policyBinding.RoleBindings[roleBinding.Name] + if exists { + return fmt.Errorf("roleBinding %v already exists", roleBinding.Name) + } + + policyBinding.RoleBindings[roleBinding.Name] = *roleBinding + policyBinding.LastModified = util.Now() + + if err := m.bindingRegistry.UpdatePolicyBinding(ctx, policyBinding); err != nil { + return err + } + return nil +} + +func (m *VirtualRegistry) UpdateRoleBinding(ctx kapi.Context, roleBinding *authorizationapi.RoleBinding) error { + if err := m.validateReferentialIntegrity(ctx, roleBinding); err != nil { + return err + } + if err := m.confirmNoEscalaton(ctx, roleBinding); err != nil { + return err + } + + existingRoleBinding, err := m.GetRoleBinding(ctx, roleBinding.Name) + if err != nil { + return err + } + if existingRoleBinding == nil { + return fmt.Errorf("roleBinding %v does not exist", roleBinding.Name) + } + if existingRoleBinding.RoleRef.Namespace != roleBinding.RoleRef.Namespace { + return fmt.Errorf("cannot change roleBinding.RoleRef.Namespace from %v to %v", existingRoleBinding.RoleRef.Namespace, roleBinding.RoleRef.Namespace) + } + + policyBinding, err := m.getPolicyBindingForPolicy(ctx, roleBinding.RoleRef.Namespace) + if err != nil { + return err + } + + _, exists := policyBinding.RoleBindings[roleBinding.Name] + if !exists { + return fmt.Errorf("roleBinding %v does not exist", roleBinding.Name) + } + + policyBinding.RoleBindings[roleBinding.Name] = *roleBinding + policyBinding.LastModified = util.Now() + + if err := m.bindingRegistry.UpdatePolicyBinding(ctx, policyBinding); err != nil { + return err + } + return nil +} + +func (m *VirtualRegistry) validateReferentialIntegrity(ctx kapi.Context, roleBinding *authorizationapi.RoleBinding) error { + if _, err := m.getReferencedRole(roleBinding.RoleRef); err != nil { + return err + } + + return nil +} + +func (m *VirtualRegistry) getReferencedRole(roleRef kapi.ObjectReference) (*authorizationapi.Role, error) { + ctx := kapi.WithNamespace(kapi.NewContext(), roleRef.Namespace) + + policy, err := m.policyRegistry.GetPolicy(ctx, authorizationapi.PolicyName) + if err != nil { + return nil, fmt.Errorf("policy %v not found: %v", roleRef.Namespace, err) + } + + role, exists := policy.Roles[roleRef.Name] + if !exists { + return nil, fmt.Errorf("role %v not found", roleRef.Name) + } + + return &role, nil +} + +func (m *VirtualRegistry) confirmNoEscalaton(ctx kapi.Context, roleBinding *authorizationapi.RoleBinding) error { + modifyingRole, err := m.getReferencedRole(roleBinding.RoleRef) + if err != nil { + return err + } + + ruleResolver := rulevalidation.NewDefaultRuleResolver(m.policyRegistry, m.bindingRegistry) + ownerLocalRules, err := ruleResolver.GetEffectivePolicyRules(ctx) + if err != nil { + return err + } + masterContext := kapi.WithNamespace(ctx, m.masterAuthorizationNamespace) + ownerGlobalRules, err := ruleResolver.GetEffectivePolicyRules(masterContext) + if err != nil { + return err + } + + ownerRules := make([]authorizationapi.PolicyRule, 0, len(ownerGlobalRules)+len(ownerLocalRules)) + ownerRules = append(ownerRules, ownerLocalRules...) + ownerRules = append(ownerRules, ownerGlobalRules...) + + ownerRightsCover, missingRights := rulevalidation.Covers(ownerRules, modifyingRole.Rules) + if !ownerRightsCover { + user, _ := kapi.UserFrom(ctx) + return fmt.Errorf("attempt to grant extra privileges: %v\nuser=%v\nownerrules%v\n", missingRights, user, ownerRules) + } + + return nil +} + +// ensurePolicyBindingToMaster returns a PolicyBinding object that has a PolicyRef pointing to the Policy in the passed namespace. +func (m *VirtualRegistry) ensurePolicyBindingToMaster(ctx kapi.Context) (*authorizationapi.PolicyBinding, error) { + policyBinding, err := m.bindingRegistry.GetPolicyBinding(ctx, m.masterAuthorizationNamespace) + if err != nil { + if !strings.Contains(err.Error(), "not found") { + return nil, err + } + + // if we have no policyBinding, go ahead and make one. creating one here collapses code paths below. We only take this hit once + policyBinding = policybindingregistry.NewEmptyPolicyBinding(kapi.NamespaceValue(ctx), m.masterAuthorizationNamespace) + if err := m.bindingRegistry.CreatePolicyBinding(ctx, policyBinding); err != nil { + return nil, err + } + + policyBinding, err = m.bindingRegistry.GetPolicyBinding(ctx, m.masterAuthorizationNamespace) + if err != nil { + return nil, err + } + } + + if policyBinding.RoleBindings == nil { + policyBinding.RoleBindings = make(map[string]authorizationapi.RoleBinding) + } + + return policyBinding, nil +} + +// Returns a PolicyBinding that points to the specified policyNamespace. It will autocreate ONLY if policyNamespace equals the master namespace +func (m *VirtualRegistry) getPolicyBindingForPolicy(ctx kapi.Context, policyNamespace string) (*authorizationapi.PolicyBinding, error) { + // we can autocreate a PolicyBinding object if the RoleBinding is for the master namespace + if policyNamespace == m.masterAuthorizationNamespace { + return m.ensurePolicyBindingToMaster(ctx) + } + + policyBinding, err := m.bindingRegistry.GetPolicyBinding(ctx, policyNamespace) + if err != nil { + return nil, err + } + + if policyBinding.RoleBindings == nil { + policyBinding.RoleBindings = make(map[string]authorizationapi.RoleBinding) + } + + return policyBinding, nil +} + +func (m *VirtualRegistry) getPolicyBindingOwningRoleBinding(ctx kapi.Context, bindingName string) (*authorizationapi.PolicyBinding, error) { + policyBindingList, err := m.bindingRegistry.ListPolicyBindings(ctx, klabels.Everything(), klabels.Everything()) + if err != nil { + return nil, err + } + + for _, policyBinding := range policyBindingList.Items { + _, exists := policyBinding.RoleBindings[bindingName] + if exists { + return &policyBinding, nil + } + } + + return nil, nil +} diff --git a/pkg/authorization/registry/subjectaccessreview/rest.go b/pkg/authorization/registry/subjectaccessreview/rest.go index 54b8fbf81c7a..5b2590fa4c33 100644 --- a/pkg/authorization/registry/subjectaccessreview/rest.go +++ b/pkg/authorization/registry/subjectaccessreview/rest.go @@ -35,20 +35,18 @@ func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, err } namespace := kapi.NamespaceValue(ctx) - user := &user.DefaultInfo{ Name: subjectAccessReview.User, Groups: subjectAccessReview.Groups, } + requestContext := kapi.WithUser(kapi.WithNamespace(kapi.NewDefaultContext(), namespace), user) attributes := &authorizer.DefaultAuthorizationAttributes{ - User: user, - Verb: subjectAccessReview.Verb, - Resource: subjectAccessReview.Resource, - Namespace: namespace, + Verb: subjectAccessReview.Verb, + Resource: subjectAccessReview.Resource, } - allowed, reason, err := r.authorizer.Authorize(attributes) + allowed, reason, err := r.authorizer.Authorize(requestContext, attributes) if err != nil { return nil, err } diff --git a/pkg/authorization/registry/subjectaccessreview/rest_test.go b/pkg/authorization/registry/subjectaccessreview/rest_test.go index 748a35447469..9c70c6e06e25 100644 --- a/pkg/authorization/registry/subjectaccessreview/rest_test.go +++ b/pkg/authorization/registry/subjectaccessreview/rest_test.go @@ -6,7 +6,6 @@ import ( "testing" kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" authorizationapi "github.com/openshift/origin/pkg/authorization/api" @@ -26,7 +25,7 @@ type testAuthorizer struct { actualAttributes *authorizer.DefaultAuthorizationAttributes } -func (a *testAuthorizer) Authorize(passedAttributes authorizer.AuthorizationAttributes) (allowed bool, reason string, err error) { +func (a *testAuthorizer) Authorize(ctx kapi.Context, passedAttributes authorizer.AuthorizationAttributes) (allowed bool, reason string, err error) { attributes, ok := passedAttributes.(*authorizer.DefaultAuthorizationAttributes) if !ok { return false, "ERROR", errors.New("unexpected type for test") @@ -39,7 +38,7 @@ func (a *testAuthorizer) Authorize(passedAttributes authorizer.AuthorizationAttr } return a.allowed, a.reason, errors.New(a.err) } -func (a *testAuthorizer) GetAllowedSubjects(passedAttributes authorizer.AuthorizationAttributes) ([]string, []string, error) { +func (a *testAuthorizer) GetAllowedSubjects(ctx kapi.Context, passedAttributes authorizer.AuthorizationAttributes) ([]string, []string, error) { return nil, nil, nil } @@ -104,13 +103,8 @@ func (r *subjectAccessTest) runTest(t *testing.T) { } expectedAttributes := &authorizer.DefaultAuthorizationAttributes{ - User: &user.DefaultInfo{ - Name: r.reviewRequest.User, - Groups: r.reviewRequest.Groups, - }, - Verb: r.reviewRequest.Verb, - Resource: r.reviewRequest.Resource, - Namespace: namespace, + Verb: r.reviewRequest.Verb, + Resource: r.reviewRequest.Resource, } ctx := kapi.WithNamespace(kapi.NewContext(), namespace) diff --git a/pkg/authorization/registry/test/policy.go b/pkg/authorization/registry/test/policy.go index d9c88db83357..36e09ab2c49c 100644 --- a/pkg/authorization/registry/test/policy.go +++ b/pkg/authorization/registry/test/policy.go @@ -12,13 +12,22 @@ import ( ) type PolicyRegistry struct { - Err error - MasterNamespace string - Policies []authorizationapi.Policy - DeletedPolicyName string + // Policies is a of namespace->name->Policy + Policies map[string]map[string]authorizationapi.Policy + Err error } -// ListPolicies obtains list of policies that match a selector. +func NewPolicyRegistry(policies []authorizationapi.Policy, err error) *PolicyRegistry { + policyMap := make(map[string]map[string]authorizationapi.Policy) + + for _, policy := range policies { + addPolicy(policyMap, policy) + } + + return &PolicyRegistry{policyMap, err} +} + +// ListPolicies obtains list of ListPolicy that match a selector. func (r *PolicyRegistry) ListPolicies(ctx kapi.Context, labels, fields klabels.Selector) (*authorizationapi.PolicyList, error) { if r.Err != nil { return nil, r.Err @@ -30,16 +39,17 @@ func (r *PolicyRegistry) ListPolicies(ctx kapi.Context, labels, fields klabels.S } list := make([]authorizationapi.Policy, 0) - for _, curr := range r.Policies { - if curr.Namespace == namespace { + if namespacedPolicies, ok := r.Policies[namespace]; ok { + for _, curr := range namespacedPolicies { list = append(list, curr) } + } return &authorizationapi.PolicyList{ Items: list, }, - r.Err + nil } // GetPolicy retrieves a specific policy. @@ -53,9 +63,9 @@ func (r *PolicyRegistry) GetPolicy(ctx kapi.Context, id string) (*authorizationa return nil, errors.New("invalid request. Namespace parameter required.") } - for _, curr := range r.Policies { - if curr.Namespace == namespace && id == curr.Name { - return &curr, nil + if namespacedPolicies, ok := r.Policies[namespace]; ok { + if policy, ok := namespacedPolicies[id]; ok { + return &policy, nil } } @@ -64,20 +74,71 @@ func (r *PolicyRegistry) GetPolicy(ctx kapi.Context, id string) (*authorizationa // CreatePolicy creates a new policy. func (r *PolicyRegistry) CreatePolicy(ctx kapi.Context, policy *authorizationapi.Policy) error { - return r.Err + if r.Err != nil { + return r.Err + } + + namespace := kapi.NamespaceValue(ctx) + if len(namespace) == 0 { + return errors.New("invalid request. Namespace parameter required.") + } + if existing, _ := r.GetPolicy(ctx, policy.Name); existing != nil { + return fmt.Errorf("Policy %v::%v already exists", namespace, policy.Name) + } + + addPolicy(r.Policies, *policy) + + return nil } // UpdatePolicy updates a policy. func (r *PolicyRegistry) UpdatePolicy(ctx kapi.Context, policy *authorizationapi.Policy) error { - return r.Err + if r.Err != nil { + return r.Err + } + + namespace := kapi.NamespaceValue(ctx) + if len(namespace) == 0 { + return errors.New("invalid request. Namespace parameter required.") + } + if existing, _ := r.GetPolicy(ctx, policy.Name); existing == nil { + return fmt.Errorf("Policy %v::%v not found", namespace, policy.Name) + } + + addPolicy(r.Policies, *policy) + + return nil } // DeletePolicy deletes a policy. func (r *PolicyRegistry) DeletePolicy(ctx kapi.Context, id string) error { - r.DeletedPolicyName = id - return r.Err + if r.Err != nil { + return r.Err + } + + namespace := kapi.NamespaceValue(ctx) + if len(namespace) == 0 { + return errors.New("invalid request. Namespace parameter required.") + } + + namespacedPolicies, ok := r.Policies[namespace] + if ok { + delete(namespacedPolicies, id) + } + + return nil } func (r *PolicyRegistry) WatchPolicies(ctx kapi.Context, label, field klabels.Selector, resourceVersion string) (watch.Interface, error) { - return nil, r.Err + return nil, errors.New("unsupported") +} + +func addPolicy(policies map[string]map[string]authorizationapi.Policy, policy authorizationapi.Policy) { + namespacedPolicies, ok := policies[policy.Namespace] + if !ok { + namespacedPolicies = make(map[string]authorizationapi.Policy) + policies[policy.Namespace] = namespacedPolicies + } + + namespacedPolicies[policy.Name] = policy } diff --git a/pkg/authorization/registry/test/policybinding.go b/pkg/authorization/registry/test/policybinding.go index 1b883a5c4552..e8d308399555 100644 --- a/pkg/authorization/registry/test/policybinding.go +++ b/pkg/authorization/registry/test/policybinding.go @@ -12,10 +12,19 @@ import ( ) type PolicyBindingRegistry struct { - Err error - MasterNamespace string - PolicyBindings []authorizationapi.PolicyBinding - DeletedPolicyBindingName string + // PolicyBindings is a of namespace->name->PolicyBinding + PolicyBindings map[string]map[string]authorizationapi.PolicyBinding + Err error +} + +func NewPolicyBindingRegistry(bindings []authorizationapi.PolicyBinding, err error) *PolicyBindingRegistry { + bindingMap := make(map[string]map[string]authorizationapi.PolicyBinding) + + for _, binding := range bindings { + addPolicyBinding(bindingMap, binding) + } + + return &PolicyBindingRegistry{bindingMap, err} } // ListPolicies obtains list of ListPolicyBinding that match a selector. @@ -30,16 +39,17 @@ func (r *PolicyBindingRegistry) ListPolicyBindings(ctx kapi.Context, labels, fie } list := make([]authorizationapi.PolicyBinding, 0) - for _, curr := range r.PolicyBindings { - if curr.Namespace == namespace { + if namespacedBindings, ok := r.PolicyBindings[namespace]; ok { + for _, curr := range namespacedBindings { list = append(list, curr) } + } return &authorizationapi.PolicyBindingList{ Items: list, }, - r.Err + nil } // GetPolicyBinding retrieves a specific policyBinding. @@ -53,9 +63,9 @@ func (r *PolicyBindingRegistry) GetPolicyBinding(ctx kapi.Context, id string) (* return nil, errors.New("invalid request. Namespace parameter required.") } - for _, curr := range r.PolicyBindings { - if curr.Namespace == namespace && id == curr.Name { - return &curr, nil + if namespacedBindings, ok := r.PolicyBindings[namespace]; ok { + if binding, ok := namespacedBindings[id]; ok { + return &binding, nil } } @@ -64,23 +74,71 @@ func (r *PolicyBindingRegistry) GetPolicyBinding(ctx kapi.Context, id string) (* // CreatePolicyBinding creates a new policyBinding. func (r *PolicyBindingRegistry) CreatePolicyBinding(ctx kapi.Context, policyBinding *authorizationapi.PolicyBinding) error { - if r.Err == nil { - r.PolicyBindings = append(r.PolicyBindings, *policyBinding) + if r.Err != nil { + return r.Err + } + + namespace := kapi.NamespaceValue(ctx) + if len(namespace) == 0 { + return errors.New("invalid request. Namespace parameter required.") + } + if existing, _ := r.GetPolicyBinding(ctx, policyBinding.Name); existing != nil { + return fmt.Errorf("PolicyBinding %v::%v already exists", namespace, policyBinding.Name) } - return r.Err + + addPolicyBinding(r.PolicyBindings, *policyBinding) + + return nil } // UpdatePolicyBinding updates a policyBinding. func (r *PolicyBindingRegistry) UpdatePolicyBinding(ctx kapi.Context, policyBinding *authorizationapi.PolicyBinding) error { - return r.Err + if r.Err != nil { + return r.Err + } + + namespace := kapi.NamespaceValue(ctx) + if len(namespace) == 0 { + return errors.New("invalid request. Namespace parameter required.") + } + if existing, _ := r.GetPolicyBinding(ctx, policyBinding.Name); existing == nil { + return fmt.Errorf("PolicyBinding %v::%v not found", namespace, policyBinding.Name) + } + + addPolicyBinding(r.PolicyBindings, *policyBinding) + + return nil } // DeletePolicyBinding deletes a policyBinding. func (r *PolicyBindingRegistry) DeletePolicyBinding(ctx kapi.Context, id string) error { - r.DeletedPolicyBindingName = id - return r.Err + if r.Err != nil { + return r.Err + } + + namespace := kapi.NamespaceValue(ctx) + if len(namespace) == 0 { + return errors.New("invalid request. Namespace parameter required.") + } + + namespacedBindings, ok := r.PolicyBindings[namespace] + if ok { + delete(namespacedBindings, id) + } + + return nil } func (r *PolicyBindingRegistry) WatchPolicyBindings(ctx kapi.Context, label, field klabels.Selector, resourceVersion string) (watch.Interface, error) { - return nil, r.Err + return nil, errors.New("unsupported") +} + +func addPolicyBinding(bindings map[string]map[string]authorizationapi.PolicyBinding, binding authorizationapi.PolicyBinding) { + namespacedBindings, ok := bindings[binding.Namespace] + if !ok { + namespacedBindings = make(map[string]authorizationapi.PolicyBinding) + bindings[binding.Namespace] = namespacedBindings + } + + namespacedBindings[binding.Name] = binding } diff --git a/pkg/authorization/rulevalidation/find_rules.go b/pkg/authorization/rulevalidation/find_rules.go new file mode 100644 index 000000000000..e6e435a3de9e --- /dev/null +++ b/pkg/authorization/rulevalidation/find_rules.go @@ -0,0 +1,145 @@ +package rulevalidation + +import ( + "errors" + "fmt" + "strings" + + kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user" + klabels "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors" + + authorizationapi "github.com/openshift/origin/pkg/authorization/api" +) + +type DefaultRuleResolver struct { + policyGetter PolicyGetter + bindingLister BindingLister +} + +func NewDefaultRuleResolver(policyGetter PolicyGetter, bindingLister BindingLister) *DefaultRuleResolver { + return &DefaultRuleResolver{policyGetter, bindingLister} +} + +type AuthorizationRuleResolver interface { + GetRoleBindings(ctx kapi.Context) ([]authorizationapi.RoleBinding, error) + GetRole(roleBinding authorizationapi.RoleBinding) (*authorizationapi.Role, error) + // getEffectivePolicyRules returns the list of rules that apply to a given user in a given namespace and error. If an error is returned, the slice of + // PolicyRules may not be complete, but it contains all retrievable rules. This is done because policy rules are purely additive and policy determinations + // can be made on the basis of those rules that are found. + GetEffectivePolicyRules(ctx kapi.Context) ([]authorizationapi.PolicyRule, error) +} + +type PolicyGetter interface { + // GetPolicy retrieves a specific policy. + GetPolicy(ctx kapi.Context, id string) (*authorizationapi.Policy, error) +} + +type BindingLister interface { + // ListPolicyBindings obtains list of policyBindings that match a selector. + ListPolicyBindings(ctx kapi.Context, labels, fields klabels.Selector) (*authorizationapi.PolicyBindingList, error) +} + +// getPolicy provides a point for easy caching +func (a *DefaultRuleResolver) getPolicy(ctx kapi.Context) (*authorizationapi.Policy, error) { + policy, err := a.policyGetter.GetPolicy(ctx, authorizationapi.PolicyName) + if err != nil && !strings.Contains(err.Error(), "not found") { + return nil, err + } + + return policy, nil +} + +// getPolicyBindings provides a point for easy caching +func (a *DefaultRuleResolver) getPolicyBindings(ctx kapi.Context) ([]authorizationapi.PolicyBinding, error) { + policyBindingList, err := a.bindingLister.ListPolicyBindings(ctx, klabels.Everything(), klabels.Everything()) + if err != nil { + return nil, err + } + + return policyBindingList.Items, nil +} + +// getRoleBindings provides a point for easy caching +func (a *DefaultRuleResolver) GetRoleBindings(ctx kapi.Context) ([]authorizationapi.RoleBinding, error) { + policyBindings, err := a.getPolicyBindings(ctx) + if err != nil { + return nil, err + } + + ret := make([]authorizationapi.RoleBinding, 0, len(policyBindings)) + for _, policyBinding := range policyBindings { + for _, value := range policyBinding.RoleBindings { + ret = append(ret, value) + } + } + + return ret, nil +} + +func (a *DefaultRuleResolver) GetRole(roleBinding authorizationapi.RoleBinding) (*authorizationapi.Role, error) { + namespace := roleBinding.RoleRef.Namespace + name := roleBinding.RoleRef.Name + + ctx := kapi.WithNamespace(kapi.NewContext(), namespace) + policy, err := a.getPolicy(ctx) + if err != nil { + return nil, err + } + + role, exists := policy.Roles[name] + if !exists { + return nil, fmt.Errorf("role %#v not found", roleBinding.RoleRef) + } + + return &role, nil +} + +// getEffectivePolicyRules returns the list of rules that apply to a given user in a given namespace and error. If an error is returned, the slice of +// PolicyRules may not be complete, but it contains all retrievable rules. This is done because policy rules are purely additive and policy determinations +// can be made on the basis of those rules that are found. +func (a *DefaultRuleResolver) GetEffectivePolicyRules(ctx kapi.Context) ([]authorizationapi.PolicyRule, error) { + roleBindings, err := a.GetRoleBindings(ctx) + if err != nil { + return nil, err + } + + errs := []error{} + rules := make([]authorizationapi.PolicyRule, 0, len(roleBindings)) + for _, roleBinding := range roleBindings { + role, err := a.GetRole(roleBinding) + if err != nil { + errs = append(errs, err) + continue + } + + for _, curr := range role.Rules { + user, exists := kapi.UserFrom(ctx) + if !exists { + errs = append(errs, errors.New("user missing from context")) + } + + if doesApplyToUser(roleBinding.Users, roleBinding.Groups, user) { + rules = append(rules, curr) + } + } + } + + return rules, kerrors.NewAggregate(errs) +} + +func doesApplyToUser(ruleUsers, ruleGroups util.StringSet, user user.Info) bool { + if ruleUsers.Has(user.GetName()) { + return true + } + + for _, currGroup := range user.GetGroups() { + if ruleGroups.Has(currGroup) { + return true + } + } + + return false +} diff --git a/pkg/authorization/rulevalidation/policy_comparator.go b/pkg/authorization/rulevalidation/policy_comparator.go new file mode 100644 index 000000000000..1ff0d03dc838 --- /dev/null +++ b/pkg/authorization/rulevalidation/policy_comparator.go @@ -0,0 +1,81 @@ +package rulevalidation + +import ( + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + + authorizationapi "github.com/openshift/origin/pkg/authorization/api" +) + +// Covers determines whether or not the ownerRules cover the servantRules in terms of allowed actions. +// It returns whether or not the ownerRules cover and a list of the rules that the ownerRules do not cover. +func Covers(ownerRules, servantRules []authorizationapi.PolicyRule) (bool, []authorizationapi.PolicyRule) { + // 1. Break every servantRule into individual rule tuples: verb, resource, resourceName + // 2. Compare the mini-rules against each owner rule. Because the breakdown is down to the most atomic level, we're guaranteed that each mini-servant rule will be either fully covered or not covered by a single owner rule + // 3. Any left over mini-rules means that we are not covered and we have a nice list of them. + // TODO: it might be nice to collapse the list down into something more human readable + + subrules := []authorizationapi.PolicyRule{} + for _, servantRule := range servantRules { + subrules = append(subrules, breakdownRule(servantRule)...) + } + + // fmt.Printf("subrules: %v\n", subrules) + // fmt.Printf("ownerRules: %v\n", ownerRules) + + uncoveredRules := []authorizationapi.PolicyRule{} + for _, subrule := range subrules { + covered := false + for _, ownerRule := range ownerRules { + if ruleCovers(ownerRule, subrule) { + covered = true + break + } + } + + if !covered { + uncoveredRules = append(uncoveredRules, subrule) + } + } + + return (len(uncoveredRules) == 0), uncoveredRules +} + +// breadownRule takes a rule and builds an equivalent list of rules that each have at most one verb, one +// resource, and one resource name +func breakdownRule(rule authorizationapi.PolicyRule) []authorizationapi.PolicyRule { + subrules := []authorizationapi.PolicyRule{} + + for resource := range authorizationapi.ExpandResources(rule.Resources) { + for verb := range rule.Verbs { + if len(rule.ResourceNames) > 0 { + for _, resourceName := range rule.ResourceNames.List() { + subrules = append(subrules, authorizationapi.PolicyRule{Resources: util.NewStringSet(resource), Verbs: util.NewStringSet(verb), ResourceNames: util.NewStringSet(resourceName)}) + } + + } else { + subrules = append(subrules, authorizationapi.PolicyRule{Resources: util.NewStringSet(resource), Verbs: util.NewStringSet(verb)}) + } + + } + } + + return subrules +} + +// ruleCovers determines whether the ownerRule (which may have multiple verbs, resources, and resourceNames) covers +// the subrule (which may only contain at most one verb, resource, and resourceName) +func ruleCovers(ownerRule, subrule authorizationapi.PolicyRule) bool { + allResources := authorizationapi.ExpandResources(ownerRule.Resources) + + verbMatches := ownerRule.Verbs.Has("*") || ownerRule.Verbs.HasAll(subrule.Verbs.List()...) + resourceMatches := ownerRule.Resources.Has("*") || allResources.HasAll(subrule.Resources.List()...) + resourceNameMatches := false + + if len(subrule.ResourceNames) == 0 { + resourceNameMatches = (len(ownerRule.ResourceNames) == 0) + } else { + resourceNameMatches = (len(ownerRule.ResourceNames) == 0) || ownerRule.ResourceNames.HasAll(subrule.ResourceNames.List()...) + } + + return verbMatches && resourceMatches && resourceNameMatches +} diff --git a/pkg/authorization/rulevalidation/policy_comparator_test.go b/pkg/authorization/rulevalidation/policy_comparator_test.go new file mode 100644 index 000000000000..39a7ab8c4418 --- /dev/null +++ b/pkg/authorization/rulevalidation/policy_comparator_test.go @@ -0,0 +1,262 @@ +package rulevalidation + +import ( + "reflect" + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + + authorizationapi "github.com/openshift/origin/pkg/authorization/api" +) + +type escalationTest struct { + ownerRules []authorizationapi.PolicyRule + servantRules []authorizationapi.PolicyRule + + expectedCovered bool + expectedUncoveredRules []authorizationapi.PolicyRule +} + +func TestExactMatch(t *testing.T) { + escalationTest{ + ownerRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("builds")}, + }, + servantRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("builds")}, + }, + + expectedCovered: true, + expectedUncoveredRules: []authorizationapi.PolicyRule{}, + }.test(t) +} + +func TestMultipleRulesCoveringSingleRule(t *testing.T) { + escalationTest{ + ownerRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("delete"), Resources: util.NewStringSet("deployments")}, + {Verbs: util.NewStringSet("delete"), Resources: util.NewStringSet("builds")}, + {Verbs: util.NewStringSet("update"), Resources: util.NewStringSet("builds", "deployments")}, + }, + servantRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("delete", "update"), Resources: util.NewStringSet("builds", "deployments")}, + }, + + expectedCovered: true, + expectedUncoveredRules: []authorizationapi.PolicyRule{}, + }.test(t) + +} + +func TestMultipleRulesMissingSingleVerbResourceCombination(t *testing.T) { + escalationTest{ + ownerRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("delete", "update"), Resources: util.NewStringSet("builds", "deployments")}, + {Verbs: util.NewStringSet("delete"), Resources: util.NewStringSet("pods")}, + }, + servantRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("delete", "update"), Resources: util.NewStringSet("builds", "deployments", "pods")}, + }, + + expectedCovered: false, + expectedUncoveredRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("update"), Resources: util.NewStringSet("pods")}, + }, + }.test(t) +} + +func TestResourceGroupCoveringEnumerated(t *testing.T) { + escalationTest{ + ownerRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("create", "delete", "update"), Resources: util.NewStringSet("resourcegroup:builds")}, + }, + servantRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("delete", "update"), Resources: util.NewStringSet("builds", "buildconfigs")}, + }, + + expectedCovered: true, + expectedUncoveredRules: []authorizationapi.PolicyRule{}, + }.test(t) +} + +func TestEnumeratedCoveringResourceGroup(t *testing.T) { + escalationTest{ + ownerRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("delete", "update"), Resources: util.NewStringSet("builds", "buildconfigs", "buildlogs")}, + }, + servantRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("delete", "update"), Resources: util.NewStringSet("resourcegroup:builds")}, + }, + + expectedCovered: true, + expectedUncoveredRules: []authorizationapi.PolicyRule{}, + }.test(t) +} + +func TestEnumeratedMissingPartOfResourceGroup(t *testing.T) { + escalationTest{ + ownerRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("delete", "update"), Resources: util.NewStringSet("builds", "buildconfigs")}, + }, + servantRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("delete", "update"), Resources: util.NewStringSet("resourcegroup:builds")}, + }, + + expectedCovered: false, + expectedUncoveredRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("delete"), Resources: util.NewStringSet("buildlogs")}, + {Verbs: util.NewStringSet("update"), Resources: util.NewStringSet("buildlogs")}, + }, + }.test(t) +} + +func TestVerbStarCoveringMultiple(t *testing.T) { + escalationTest{ + ownerRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("*"), Resources: util.NewStringSet("roles")}, + }, + servantRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("watch", "list"), Resources: util.NewStringSet("roles")}, + }, + + expectedCovered: true, + expectedUncoveredRules: []authorizationapi.PolicyRule{}, + }.test(t) +} + +func TestEnumerationNotCoveringVerbStar(t *testing.T) { + escalationTest{ + ownerRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get", "list", "watch", "create", "update", "delete", "exec"), Resources: util.NewStringSet("roles")}, + }, + servantRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("*"), Resources: util.NewStringSet("roles")}, + }, + + expectedCovered: false, + expectedUncoveredRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("*"), Resources: util.NewStringSet("roles")}, + }, + }.test(t) +} + +func TestVerbStarCoveringStar(t *testing.T) { + escalationTest{ + ownerRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("*"), Resources: util.NewStringSet("roles")}, + }, + servantRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("*"), Resources: util.NewStringSet("roles")}, + }, + + expectedCovered: true, + expectedUncoveredRules: []authorizationapi.PolicyRule{}, + }.test(t) +} + +func TestResourceStarCoveringMultiple(t *testing.T) { + escalationTest{ + ownerRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("*")}, + }, + servantRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("resourcegroup:deployments")}, + }, + + expectedCovered: true, + expectedUncoveredRules: []authorizationapi.PolicyRule{}, + }.test(t) +} + +func TestEnumerationNotCoveringResourceStar(t *testing.T) { + escalationTest{ + ownerRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("roles", "resourcegroup:deployments")}, + }, + servantRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("*")}, + }, + + expectedCovered: false, + expectedUncoveredRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("*")}, + }, + }.test(t) +} + +func TestResourceStarCoveringStar(t *testing.T) { + escalationTest{ + ownerRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("*")}, + }, + servantRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("*")}, + }, + + expectedCovered: true, + expectedUncoveredRules: []authorizationapi.PolicyRule{}, + }.test(t) +} + +func TestResourceNameEmptyCoveringMultiple(t *testing.T) { + escalationTest{ + ownerRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("pods"), ResourceNames: util.NewStringSet()}, + }, + servantRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("pods"), ResourceNames: util.NewStringSet("foo", "bar")}, + }, + + expectedCovered: true, + expectedUncoveredRules: []authorizationapi.PolicyRule{}, + }.test(t) +} + +func TestEnumerationNotCoveringResourceNameEmpty(t *testing.T) { + escalationTest{ + ownerRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("pods"), ResourceNames: util.NewStringSet("foo", "bar")}, + }, + servantRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("pods"), ResourceNames: util.NewStringSet()}, + }, + + expectedCovered: false, + expectedUncoveredRules: []authorizationapi.PolicyRule{ + {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("pods")}, + }, + }.test(t) +} + +func (test escalationTest) test(t *testing.T) { + actualCovered, actualUncoveredRules := Covers(test.ownerRules, test.servantRules) + + if actualCovered != test.expectedCovered { + t.Errorf("expected %v, but got %v", test.expectedCovered, actualCovered) + } + + if !rulesMatch(test.expectedUncoveredRules, actualUncoveredRules) { + t.Errorf("expected %v, but got %v", test.expectedUncoveredRules, actualUncoveredRules) + } +} + +func rulesMatch(expectedRules, actualRules []authorizationapi.PolicyRule) bool { + if len(expectedRules) != len(actualRules) { + return false + } + + for _, expectedRule := range expectedRules { + found := false + for _, actualRule := range actualRules { + if reflect.DeepEqual(expectedRule, actualRule) { + found = true + } + } + + if !found { + return false + } + } + + return true +} diff --git a/pkg/cmd/cli/describe/describer.go b/pkg/cmd/cli/describe/describer.go index 7c781def0d55..336c48f40d67 100644 --- a/pkg/cmd/cli/describe/describer.go +++ b/pkg/cmd/cli/describe/describer.go @@ -334,8 +334,8 @@ func (d *PolicyBindingDescriber) Describe(namespace, name string) (string, error roleBinding := policyBinding.RoleBindings[key] formatString(out, "RoleBinding["+key+"]", " ") formatString(out, "\tRole", roleBinding.RoleRef.Name) - formatString(out, "\tUsers", roleBinding.UserNames) - formatString(out, "\tGroups", roleBinding.GroupNames) + formatString(out, "\tUsers", roleBinding.Users.List()) + formatString(out, "\tGroups", roleBinding.Groups.List()) } return nil diff --git a/pkg/cmd/experimental/policy/add_group.go b/pkg/cmd/experimental/policy/add_group.go index 1d85cf9c81e7..c314eb22482d 100644 --- a/pkg/cmd/experimental/policy/add_group.go +++ b/pkg/cmd/experimental/policy/add_group.go @@ -1,10 +1,11 @@ package policy import ( - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/golang/glog" "github.com/spf13/cobra" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + authorizationapi "github.com/openshift/origin/pkg/authorization/api" "github.com/openshift/origin/pkg/client" "github.com/openshift/origin/pkg/cmd/util/clientcmd" @@ -16,7 +17,7 @@ type addGroupOptions struct { bindingNamespace string client client.Interface - groupNames []string + groups []string } func NewCmdAddGroup(f *clientcmd.Factory) *cobra.Command { @@ -57,7 +58,7 @@ func (o *addGroupOptions) complete(cmd *cobra.Command) bool { } o.roleName = args[0] - o.groupNames = args[1:] + o.groups = args[1:] return true } @@ -71,10 +72,10 @@ func (o *addGroupOptions) run() error { return err } - roleBinding := (*authorizationapi.RoleBinding)(nil) + var roleBinding *authorizationapi.RoleBinding isUpdate := true if len(roleBindings) == 0 { - roleBinding = &authorizationapi.RoleBinding{} + roleBinding = &authorizationapi.RoleBinding{Groups: util.NewStringSet()} isUpdate = false } else { // only need to add the user or group to a single roleBinding on the role. Just choose the first one @@ -84,10 +85,7 @@ func (o *addGroupOptions) run() error { roleBinding.RoleRef.Namespace = o.roleNamespace roleBinding.RoleRef.Name = o.roleName - groups := util.StringSet{} - groups.Insert(roleBinding.GroupNames...) - groups.Insert(o.groupNames...) - roleBinding.GroupNames = groups.List() + roleBinding.Groups.Insert(o.groups...) if isUpdate { _, err = o.client.RoleBindings(o.bindingNamespace).Update(roleBinding) diff --git a/pkg/cmd/experimental/policy/add_user.go b/pkg/cmd/experimental/policy/add_user.go index 6fb2a509a488..38cb2a6c4d33 100644 --- a/pkg/cmd/experimental/policy/add_user.go +++ b/pkg/cmd/experimental/policy/add_user.go @@ -1,10 +1,11 @@ package policy import ( - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/golang/glog" "github.com/spf13/cobra" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + authorizationapi "github.com/openshift/origin/pkg/authorization/api" "github.com/openshift/origin/pkg/client" "github.com/openshift/origin/pkg/cmd/util/clientcmd" @@ -16,7 +17,7 @@ type addUserOptions struct { bindingNamespace string client client.Interface - userNames []string + users []string } func NewCmdAddUser(f *clientcmd.Factory) *cobra.Command { @@ -57,7 +58,7 @@ func (o *addUserOptions) complete(cmd *cobra.Command) bool { } o.roleName = args[0] - o.userNames = args[1:] + o.users = args[1:] return true } @@ -71,10 +72,10 @@ func (o *addUserOptions) run() error { return err } - roleBinding := (*authorizationapi.RoleBinding)(nil) + var roleBinding *authorizationapi.RoleBinding isUpdate := true if len(roleBindings) == 0 { - roleBinding = &authorizationapi.RoleBinding{} + roleBinding = &authorizationapi.RoleBinding{Users: util.NewStringSet()} isUpdate = false } else { // only need to add the user or group to a single roleBinding on the role. Just choose the first one @@ -84,10 +85,7 @@ func (o *addUserOptions) run() error { roleBinding.RoleRef.Namespace = o.roleNamespace roleBinding.RoleRef.Name = o.roleName - users := util.StringSet{} - users.Insert(roleBinding.UserNames...) - users.Insert(o.userNames...) - roleBinding.UserNames = users.List() + roleBinding.Users.Insert(o.users...) if isUpdate { _, err = o.client.RoleBindings(o.bindingNamespace).Update(roleBinding) diff --git a/pkg/cmd/experimental/policy/remove_group.go b/pkg/cmd/experimental/policy/remove_group.go index 9d4b7ce237a1..6f39482b2ecb 100644 --- a/pkg/cmd/experimental/policy/remove_group.go +++ b/pkg/cmd/experimental/policy/remove_group.go @@ -3,7 +3,6 @@ package policy import ( "fmt" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/golang/glog" "github.com/spf13/cobra" @@ -17,7 +16,7 @@ type removeGroupOptions struct { bindingNamespace string client client.Interface - groupNames []string + groups []string } func NewCmdRemoveGroup(f *clientcmd.Factory) *cobra.Command { @@ -58,7 +57,7 @@ func (o *removeGroupOptions) complete(cmd *cobra.Command) bool { } o.roleName = args[0] - o.groupNames = args[1:] + o.groups = args[1:] return true } @@ -72,10 +71,7 @@ func (o *removeGroupOptions) run() error { } for _, roleBinding := range roleBindings { - groups := util.StringSet{} - groups.Insert(roleBinding.GroupNames...) - groups.Delete(o.groupNames...) - roleBinding.GroupNames = groups.List() + roleBinding.Groups.Delete(o.groups...) _, err = o.client.RoleBindings(o.bindingNamespace).Update(roleBinding) if err != nil { diff --git a/pkg/cmd/experimental/policy/remove_group_from_project.go b/pkg/cmd/experimental/policy/remove_group_from_project.go index cdc3ee3e4af4..7075495ec349 100644 --- a/pkg/cmd/experimental/policy/remove_group_from_project.go +++ b/pkg/cmd/experimental/policy/remove_group_from_project.go @@ -1,11 +1,11 @@ package policy import ( - labels "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/golang/glog" "github.com/spf13/cobra" + labels "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/openshift/origin/pkg/client" "github.com/openshift/origin/pkg/cmd/util/clientcmd" ) @@ -14,7 +14,7 @@ type removeGroupFromProjectOptions struct { bindingNamespace string client client.Interface - groupNames []string + groups []string } func NewCmdRemoveGroupFromProject(f *clientcmd.Factory) *cobra.Command { @@ -53,7 +53,7 @@ func (o *removeGroupFromProjectOptions) complete(cmd *cobra.Command) bool { return false } - o.groupNames = args + o.groups = args return true } @@ -65,11 +65,7 @@ func (o *removeGroupFromProjectOptions) run() error { for _, currBindings := range bindingList.Items { for _, currBinding := range currBindings.RoleBindings { - groupsForBinding := util.StringSet{} - groupsForBinding.Insert(currBinding.GroupNames...) - groupsForBinding.Delete(o.groupNames...) - - currBinding.GroupNames = groupsForBinding.List() + currBinding.Groups.Delete(o.groups...) _, err = o.client.RoleBindings(o.bindingNamespace).Update(&currBinding) if err != nil { diff --git a/pkg/cmd/experimental/policy/remove_user.go b/pkg/cmd/experimental/policy/remove_user.go index 9983bfa4408c..1fc3a74ea157 100644 --- a/pkg/cmd/experimental/policy/remove_user.go +++ b/pkg/cmd/experimental/policy/remove_user.go @@ -3,7 +3,6 @@ package policy import ( "fmt" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/golang/glog" "github.com/spf13/cobra" @@ -17,7 +16,7 @@ type removeUserOptions struct { bindingNamespace string client client.Interface - userNames []string + users []string } func NewCmdRemoveUser(f *clientcmd.Factory) *cobra.Command { @@ -58,7 +57,7 @@ func (o *removeUserOptions) complete(cmd *cobra.Command) bool { } o.roleName = args[0] - o.userNames = args[1:] + o.users = args[1:] return true } @@ -72,10 +71,7 @@ func (o *removeUserOptions) run() error { } for _, roleBinding := range roleBindings { - users := util.StringSet{} - users.Insert(roleBinding.UserNames...) - users.Delete(o.userNames...) - roleBinding.UserNames = users.List() + roleBinding.Users.Delete(o.users...) _, err = o.client.RoleBindings(o.bindingNamespace).Update(roleBinding) if err != nil { diff --git a/pkg/cmd/experimental/policy/remove_user_from_project.go b/pkg/cmd/experimental/policy/remove_user_from_project.go index 10f57a684aec..16a932a0bfa3 100644 --- a/pkg/cmd/experimental/policy/remove_user_from_project.go +++ b/pkg/cmd/experimental/policy/remove_user_from_project.go @@ -1,11 +1,11 @@ package policy import ( - "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/golang/glog" "github.com/spf13/cobra" + "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/openshift/origin/pkg/client" "github.com/openshift/origin/pkg/cmd/util/clientcmd" ) @@ -14,7 +14,7 @@ type removeUserFromProjectOptions struct { bindingNamespace string client client.Interface - userNames []string + users []string } func NewCmdRemoveUserFromProject(f *clientcmd.Factory) *cobra.Command { @@ -52,7 +52,7 @@ func (o *removeUserFromProjectOptions) complete(cmd *cobra.Command) bool { return false } - o.userNames = args + o.users = args return true } @@ -64,11 +64,7 @@ func (o *removeUserFromProjectOptions) run() error { for _, currBindings := range bindingList.Items { for _, currBinding := range currBindings.RoleBindings { - usersForBinding := util.StringSet{} - usersForBinding.Insert(currBinding.UserNames...) - usersForBinding.Delete(o.userNames...) - - currBinding.UserNames = usersForBinding.List() + currBinding.Users.Delete(o.users...) _, err = o.client.RoleBindings(o.bindingNamespace).Update(&currBinding) if err != nil { diff --git a/pkg/cmd/experimental/project/new_project.go b/pkg/cmd/experimental/project/new_project.go index 3081ee4bba16..08f2fef2cf91 100644 --- a/pkg/cmd/experimental/project/new_project.go +++ b/pkg/cmd/experimental/project/new_project.go @@ -3,10 +3,12 @@ package project import ( "fmt" - kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/golang/glog" "github.com/spf13/cobra" + kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + authorizationapi "github.com/openshift/origin/pkg/authorization/api" "github.com/openshift/origin/pkg/client" "github.com/openshift/origin/pkg/cmd/util/clientcmd" @@ -94,7 +96,7 @@ func (o *newProjectOptions) run() error { adminRoleBinding.Name = "admins" adminRoleBinding.RoleRef.Namespace = o.masterPolicyNamespace adminRoleBinding.RoleRef.Name = o.adminRole - adminRoleBinding.UserNames = []string{o.adminUser} + adminRoleBinding.Users = util.NewStringSet(o.adminUser) _, err := o.client.RoleBindings(project.Name).Create(adminRoleBinding) if err != nil { diff --git a/pkg/cmd/server/origin/master.go b/pkg/cmd/server/origin/master.go index db578218bc38..073b44d5cd1e 100644 --- a/pkg/cmd/server/origin/master.go +++ b/pkg/cmd/server/origin/master.go @@ -301,7 +301,7 @@ func (c *MasterConfig) InstallProtectedAPI(container *restful.Container) []strin "policies": policyregistry.NewREST(authorizationEtcd), "policyBindings": policybindingregistry.NewREST(authorizationEtcd), "roles": roleregistry.NewREST(authorizationEtcd), - "roleBindings": rolebindingregistry.NewREST(authorizationEtcd, authorizationEtcd, userEtcd, c.MasterAuthorizationNamespace), + "roleBindings": rolebindingregistry.NewREST(rolebindingregistry.NewVirtualRegistry(authorizationEtcd, authorizationEtcd, c.MasterAuthorizationNamespace)), "resourceAccessReviews": resourceaccessreviewregistry.NewREST(c.Authorizer), "subjectAccessReviews": subjectaccessreviewregistry.NewREST(c.Authorizer), } @@ -485,19 +485,22 @@ func (c *MasterConfig) authorizationFilter(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { attributes, err := c.AuthorizationAttributeBuilder.GetAttributes(req) if err != nil { - // fail forbidden(err.Error(), w, req) return } if attributes == nil { - // fail forbidden("No attributes", w, req) return } - allowed, reason, err := c.Authorizer.Authorize(attributes) + ctx, exists := c.RequestContextMapper.Get(req) + if !exists { + forbidden("context not found", w, req) + return + } + + allowed, reason, err := c.Authorizer.Authorize(ctx, attributes) if err != nil { - // fail forbidden(err.Error(), w, req) return } @@ -512,7 +515,6 @@ func (c *MasterConfig) authorizationFilter(handler http.Handler) http.Handler { // forbidden renders a simple forbidden error func forbidden(reason string, w http.ResponseWriter, req *http.Request) { - glog.V(1).Infof("!!!!!!!!!!!! FORBIDDING because %v!\n", reason) w.WriteHeader(http.StatusForbidden) fmt.Fprintf(w, "Forbidden: %q %s", req.RequestURI, reason) } diff --git a/pkg/cmd/server/start.go b/pkg/cmd/server/start.go index 8170a000d510..f5f1283aa0bd 100644 --- a/pkg/cmd/server/start.go +++ b/pkg/cmd/server/start.go @@ -39,6 +39,7 @@ import ( "github.com/openshift/origin/pkg/auth/authenticator/request/x509request" "github.com/openshift/origin/pkg/authorization/authorizer" authorizationetcd "github.com/openshift/origin/pkg/authorization/registry/etcd" + "github.com/openshift/origin/pkg/authorization/rulevalidation" "github.com/openshift/origin/pkg/auth/group" "github.com/openshift/origin/pkg/cmd/flagtypes" @@ -646,7 +647,7 @@ func start(cfg *config, args []string) error { func newAuthorizer(etcdHelper tools.EtcdHelper, masterAuthorizationNamespace string) authorizer.Authorizer { authorizationEtcd := authorizationetcd.New(etcdHelper) - authorizer := authorizer.NewAuthorizer(masterAuthorizationNamespace, authorizationEtcd, authorizationEtcd) + authorizer := authorizer.NewAuthorizer(masterAuthorizationNamespace, rulevalidation.NewDefaultRuleResolver(authorizationEtcd, authorizationEtcd)) return authorizer }