package rulevalidation
import (
kapi "k8s.io/kubernetes/pkg/api"
kapierror "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/auth/user"
kerrors "k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/util/sets"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
authorizationinterfaces "github.com/openshift/origin/pkg/authorization/interfaces"
"github.com/openshift/origin/pkg/client"
)
type DefaultRuleResolver struct {
policyGetter client.PoliciesListerNamespacer
bindingLister client.PolicyBindingsListerNamespacer
clusterPolicyGetter client.ClusterPolicyLister
clusterBindingLister client.ClusterPolicyBindingLister
}
func NewDefaultRuleResolver(policyGetter client.PoliciesListerNamespacer, bindingLister client.PolicyBindingsListerNamespacer, clusterPolicyGetter client.ClusterPolicyLister, clusterBindingLister client.ClusterPolicyBindingLister) *DefaultRuleResolver {
return &DefaultRuleResolver{policyGetter, bindingLister, clusterPolicyGetter, clusterBindingLister}
}
type AuthorizationRuleResolver interface {
GetRoleBindings(namespace string) ([]authorizationinterfaces.RoleBinding, error)
GetRole(roleBinding authorizationinterfaces.RoleBinding) (authorizationinterfaces.Role, error)
// RulesFor 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.
RulesFor(info user.Info, namespace string) ([]authorizationapi.PolicyRule, error)
}
func (a *DefaultRuleResolver) GetRoleBindings(namespace string) ([]authorizationinterfaces.RoleBinding, error) {
clusterBindings, clusterErr := a.clusterBindingLister.List(kapi.ListOptions{})
var namespaceBindings *authorizationapi.PolicyBindingList
var namespaceErr error
if a.bindingLister != nil && len(namespace) > 0 {
namespaceBindings, namespaceErr = a.bindingLister.PolicyBindings(namespace).List(kapi.ListOptions{})
}
// return all loaded bindings
expect := 0
if clusterBindings != nil {
expect += len(clusterBindings.Items)
}
if namespaceBindings != nil {
expect += len(namespaceBindings.Items)
}
bindings := make([]authorizationinterfaces.RoleBinding, 0, expect)
if clusterBindings != nil {
for _, policyBinding := range clusterBindings.Items {
for _, value := range policyBinding.RoleBindings {
bindings = append(bindings, authorizationinterfaces.NewClusterRoleBindingAdapter(value))
}
}
}
if namespaceBindings != nil {
for _, policyBinding := range namespaceBindings.Items {
for _, value := range policyBinding.RoleBindings {
bindings = append(bindings, authorizationinterfaces.NewLocalRoleBindingAdapter(value))
}
}
}
// return all errors
var errs []error
if clusterErr != nil {
errs = append(errs, clusterErr)
}
if namespaceErr != nil {
errs = append(errs, namespaceErr)
}
return bindings, kerrors.NewAggregate(errs)
}
func (a *DefaultRuleResolver) GetRole(roleBinding authorizationinterfaces.RoleBinding) (authorizationinterfaces.Role, error) {
namespace := roleBinding.RoleRef().Namespace
name := roleBinding.RoleRef().Name
if len(namespace) == 0 {
policy, err := a.clusterPolicyGetter.Get(authorizationapi.PolicyName)
if kapierror.IsNotFound(err) {
return nil, kapierror.NewNotFound(authorizationapi.Resource("role"), name)
}
if err != nil {
return nil, err
}
role, exists := policy.Roles[name]
if !exists {
return nil, kapierror.NewNotFound(authorizationapi.Resource("role"), name)
}
return authorizationinterfaces.NewClusterRoleAdapter(role), nil
}
if a.policyGetter == nil {
return nil, kapierror.NewNotFound(authorizationapi.Resource("role"), name)
}
policy, err := a.policyGetter.Policies(namespace).Get(authorizationapi.PolicyName)
if kapierror.IsNotFound(err) {
return nil, kapierror.NewNotFound(authorizationapi.Resource("role"), name)
}
if err != nil {
return nil, err
}
role, exists := policy.Roles[name]
if !exists {
return nil, kapierror.NewNotFound(authorizationapi.Resource("role"), name)
}
return authorizationinterfaces.NewLocalRoleAdapter(role), nil
}
// RulesFor 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) RulesFor(user user.Info, namespace string) ([]authorizationapi.PolicyRule, error) {
var errs []error
roleBindings, err := a.GetRoleBindings(namespace)
if err != nil {
errs = append(errs, err)
}
rules := make([]authorizationapi.PolicyRule, 0, len(roleBindings))
for _, roleBinding := range roleBindings {
if !roleBinding.AppliesToUser(user) {
continue
}
role, err := a.GetRole(roleBinding)
if err != nil {
errs = append(errs, err)
continue
}
for _, curr := range role.Rules() {
rules = append(rules, curr)
}
}
return rules, kerrors.NewAggregate(errs)
}
func appliesToUser(ruleUsers, ruleGroups sets.String, user user.Info) bool {
if ruleUsers.Has(user.GetName()) {
return true
}
for _, currGroup := range user.GetGroups() {
if ruleGroups.Has(currGroup) {
return true
}
}
return false
}