Files
kubesphere/pkg/models/iam/am.go
hongming 93ad572e19 refine tenant api
Signed-off-by: hongming <talonwan@yunify.com>
2019-04-08 20:48:31 +08:00

855 lines
24 KiB
Go

/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iam
import (
"encoding/json"
"errors"
"fmt"
"github.com/golang/glog"
"io/ioutil"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"net/http"
"sort"
"strings"
"github.com/go-ldap/ldap"
"k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam/policy"
)
const ClusterRoleKind = "ClusterRole"
func GetUserDevopsSimpleRules(username, projectId string) ([]models.SimpleRule, error) {
role, err := GetUserDevopsRole(projectId, username)
if err != nil {
return nil, err
}
return GetDevopsRoleSimpleRules(role), nil
}
func GetDevopsRoleSimpleRules(role string) []models.SimpleRule {
var rules []models.SimpleRule
switch role {
case "developer":
rules = []models.SimpleRule{
{Name: "pipelines", Actions: []string{"view", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
{Name: "devops", Actions: []string{"view"}},
}
break
case "owner":
rules = []models.SimpleRule{
{Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"create", "edit", "view", "delete"}},
{Name: "credentials", Actions: []string{"create", "edit", "view", "delete"}},
{Name: "devops", Actions: []string{"edit", "view", "delete"}},
}
break
case "maintainer":
rules = []models.SimpleRule{
{Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
{Name: "credentials", Actions: []string{"create", "edit", "view", "delete"}},
{Name: "devops", Actions: []string{"view"}},
}
break
case "reporter":
fallthrough
default:
rules = []models.SimpleRule{
{Name: "pipelines", Actions: []string{"view"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
{Name: "devops", Actions: []string{"view"}},
}
break
}
return rules
}
func GetUserDevopsRole(projectId string, username string) (string, error) {
//Hard fix
if username == "admin" {
return "owner", nil
}
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/api/v1alpha/projects/%s/members", constants.DevopsAPIServer, projectId), nil)
if err != nil {
return "", err
}
req.Header.Set(constants.UserNameHeader, username)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode > 200 {
return "", errors.New(string(data))
}
var result []map[string]string
err = json.Unmarshal(data, &result)
if err != nil {
return "", err
}
for _, item := range result {
if item["username"] == username {
return item["role"], nil
}
}
return "", nil
}
// Get user roles in namespace
func GetUserRoles(namespace, username string) ([]*v1.Role, error) {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister()
roleLister := informers.SharedInformerFactory().Rbac().V1().Roles().Lister()
roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
roles := make([]*v1.Role, 0)
for _, roleBinding := range roleBindings {
if k8sutil.ContainsUser(roleBinding.Subjects, username) {
if roleBinding.RoleRef.Kind == ClusterRoleKind {
clusterRole, err := clusterRoleLister.Get(roleBinding.RoleRef.Name)
if err != nil {
if apierrors.IsNotFound(err) {
glog.Warningf("cluster role %s not found but bind user %s in namespace %s", roleBinding.RoleRef.Name, username, namespace)
continue
} else {
return nil, err
}
}
role := v1.Role{}
role.TypeMeta = clusterRole.TypeMeta
role.ObjectMeta = clusterRole.ObjectMeta
role.Rules = clusterRole.Rules
role.Namespace = roleBinding.Namespace
roles = append(roles, &role)
} else {
role, err := roleLister.Roles(roleBinding.Namespace).Get(roleBinding.RoleRef.Name)
if err != nil {
if apierrors.IsNotFound(err) {
glog.Warningf("role %s not found but bind user %s in namespace %s", roleBinding.RoleRef.Name, username, namespace)
continue
} else {
return nil, err
}
}
roles = append(roles, role)
}
}
}
return roles, nil
}
func GetUserClusterRoles(username string) (*v1.ClusterRole, []*v1.ClusterRole, error) {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
if err != nil {
return nil, nil, err
}
clusterRoles := make([]*v1.ClusterRole, 0)
userFacingClusterRole := &v1.ClusterRole{}
for _, clusterRoleBinding := range clusterRoleBindings {
if k8sutil.ContainsUser(clusterRoleBinding.Subjects, username) {
clusterRole, err := clusterRoleLister.Get(clusterRoleBinding.RoleRef.Name)
if err != nil {
if apierrors.IsNotFound(err) {
glog.Warningf("cluster role %s not found but bind user %s", clusterRoleBinding.RoleRef.Name, username)
continue
} else {
return nil, nil, err
}
}
if clusterRoleBinding.Name == username {
userFacingClusterRole = clusterRole
}
clusterRoles = append(clusterRoles, clusterRole)
}
}
return userFacingClusterRole, clusterRoles, nil
}
func GetUserClusterRole(username string) (*v1.ClusterRole, error) {
userFacingClusterRole, _, err := GetUserClusterRoles(username)
if err != nil {
return nil, err
}
return userFacingClusterRole, nil
}
func GetUserClusterRules(username string) ([]v1.PolicyRule, error) {
_, clusterRoles, err := GetUserClusterRoles(username)
if err != nil {
return nil, err
}
rules := make([]v1.PolicyRule, 0)
for _, clusterRole := range clusterRoles {
rules = append(rules, clusterRole.Rules...)
}
return rules, nil
}
func GetUserRules(namespace, username string) ([]v1.PolicyRule, error) {
roles, err := GetUserRoles(namespace, username)
if err != nil {
return nil, err
}
rules := make([]v1.PolicyRule, 0)
for _, role := range roles {
rules = append(rules, role.Rules...)
}
return rules, nil
}
func isUserFacingClusterRole(role *v1.ClusterRole) bool {
if role.Labels[constants.CreatorLabelKey] != "" {
return true
}
return false
}
func GetWorkspaceRoleBindings(workspace string) ([]*v1.ClusterRoleBinding, error) {
clusterRoleBindings, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*v1.ClusterRoleBinding, 0)
for _, roleBinding := range clusterRoleBindings {
if k8sutil.IsControlledBy(roleBinding.OwnerReferences, "Workspace", workspace) {
result = append(result, roleBinding)
}
}
return result, nil
}
func GetWorkspaceRole(workspace, role string) (*v1.ClusterRole, error) {
if !sliceutil.HasString(constants.WorkSpaceRoles, role) {
return nil, apierrors.NewNotFound(schema.GroupResource{Resource: "workspace role"}, role)
}
role = fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-"))
return informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().Get(role)
}
func GetUserWorkspaceRoleMap(username string) (map[string]string, error) {
clusterRoleBindings, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything())
if err != nil {
return nil, err
}
result := make(map[string]string, 0)
for _, roleBinding := range clusterRoleBindings {
if workspace := k8sutil.GetControlledWorkspace(roleBinding.OwnerReferences); workspace != "" &&
k8sutil.ContainsUser(roleBinding.Subjects, username) {
result[workspace] = roleBinding.RoleRef.Name
}
}
return result, nil
}
func GetUserWorkspaceRole(workspace, username string) (*v1.ClusterRole, error) {
workspaceRoleMap, err := GetUserWorkspaceRoleMap(username)
if err != nil {
return nil, err
}
if workspaceRole := workspaceRoleMap[workspace]; workspaceRole != "" {
return informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().Get(workspaceRole)
}
return nil, apierrors.NewNotFound(schema.GroupResource{Resource: "workspace user"}, username)
}
func GetRoleBindings(namespace string, roleName string) ([]*v1.RoleBinding, error) {
roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister()
roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
items := make([]*v1.RoleBinding, 0)
for _, roleBinding := range roleBindings {
if roleName == "" {
items = append(items, roleBinding)
} else if roleBinding.RoleRef.Name == roleName {
items = append(items, roleBinding)
}
}
return items, nil
}
func GetClusterRoleBindings(clusterRoleName string) ([]*v1.ClusterRoleBinding, error) {
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
roleBindings, err := clusterRoleBindingLister.List(labels.Everything())
if err != nil {
return nil, err
}
items := make([]*v1.ClusterRoleBinding, 0)
for _, roleBinding := range roleBindings {
if roleBinding.RoleRef.Name == clusterRoleName {
items = append(items, roleBinding)
}
}
return items, nil
}
func ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
roleBindings, err := GetClusterRoleBindings(clusterRoleName)
if err != nil {
return nil, err
}
users := make([]*models.User, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := DescribeUser(subject.Name)
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
}
if err != nil {
return nil, err
}
users = append(users, user)
}
}
}
// order & reverse
sort.Slice(users, func(i, j int) bool {
if reverse {
tmp := i
i = j
j = tmp
}
switch orderBy {
default:
fallthrough
case "name":
return strings.Compare(users[i].Username, users[j].Username) <= 0
}
})
result := make([]interface{}, 0)
for i, d := range users {
if i >= offset && (limit == -1 || len(result) < limit) {
result = append(result, d)
}
}
return &models.PageableResponse{Items: result, TotalCount: len(users)}, nil
}
func RoleUsers(namespace string, roleName string) ([]*models.User, error) {
roleBindings, err := GetRoleBindings(namespace, roleName)
if err != nil {
return nil, err
}
users := make([]*models.User, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := DescribeUser(subject.Name)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
}
return nil, err
}
user.Role = roleBinding.RoleRef.Name
users = append(users, user)
}
}
}
return users, nil
}
func ListRoles(namespace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
return resources.ListResources(namespace, resources.Roles, conditions, orderBy, reverse, limit, offset)
}
func ListWorkspaceRoles(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
conditions.Match["ownerName"] = workspace
conditions.Match["ownerKind"] = "Workspace"
result, err := resources.ListResources("", resources.ClusterRoles, conditions, orderBy, reverse, limit, offset)
if err != nil {
return nil, err
}
for i, item := range result.Items {
if role, ok := item.(*v1.ClusterRole); ok {
role = role.DeepCopy()
role.Name = role.Labels[constants.DisplayNameLabelKey]
result.Items[i] = role
}
}
return result, nil
}
func ListClusterRoles(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
return resources.ListResources("", resources.ClusterRoles, conditions, orderBy, reverse, limit, offset)
}
func NamespaceUsers(namespaceName string) ([]*models.User, error) {
roleBindings, err := GetRoleBindings(namespaceName, "")
if err != nil {
return nil, err
}
users := make([]*models.User, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := DescribeUser(subject.Name)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
}
return nil, err
}
user.Role = roleBinding.RoleRef.Name
user.RoleBindTime = &roleBinding.CreationTimestamp.Time
user.RoleBinding = roleBinding.Name
users = append(users, user)
}
}
}
return users, nil
}
func GetUserWorkspaceSimpleRules(workspace, username string) ([]models.SimpleRule, error) {
clusterRules, err := GetUserClusterRules(username)
if err != nil {
return nil, err
}
if workspacesManager, err := policy.GetClusterAction("workspaces", "edit"); err == nil {
if rulesMatchesAction(clusterRules, workspacesManager) {
return GetWorkspaceRoleSimpleRules(workspace, constants.WorkspaceAdmin), nil
}
}
workspaceRole, err := GetUserWorkspaceRole(workspace, username)
if err != nil {
return nil, err
}
return GetWorkspaceRoleSimpleRules(workspace, workspaceRole.Labels[constants.DisplayNameLabelKey]), nil
}
func GetWorkspaceRoleSimpleRules(workspace, roleName string) []models.SimpleRule {
workspaceRules := make([]models.SimpleRule, 0)
switch roleName {
case constants.WorkspaceAdmin:
workspaceRules = []models.SimpleRule{
{Name: "workspaces", Actions: []string{"edit", "delete", "view"}},
{Name: "members", Actions: []string{"edit", "delete", "create", "view"}},
{Name: "devops", Actions: []string{"edit", "delete", "create", "view"}},
{Name: "projects", Actions: []string{"edit", "delete", "create", "view"}},
{Name: "organizations", Actions: []string{"edit", "delete", "create", "view"}},
{Name: "roles", Actions: []string{"view"}},
}
case constants.WorkspaceRegular:
workspaceRules = []models.SimpleRule{
{Name: "workspaces", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
{Name: "devops", Actions: []string{"create"}},
{Name: "projects", Actions: []string{"create"}},
{Name: "organizations", Actions: []string{"view"}},
}
case constants.WorkspaceViewer:
workspaceRules = []models.SimpleRule{
{Name: "workspaces", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
{Name: "devops", Actions: []string{"view"}},
{Name: "projects", Actions: []string{"view"}},
{Name: "organizations", Actions: []string{"view"}},
{Name: "roles", Actions: []string{"view"}},
}
}
return workspaceRules
}
// Convert cluster role to rules
func GetClusterRoleSimpleRules(clusterRoleName string) ([]models.SimpleRule, error) {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
clusterRole, err := clusterRoleLister.Get(clusterRoleName)
if err != nil {
return nil, err
}
return getClusterSimpleRule(clusterRole.Rules), nil
}
func GetUserClusterSimpleRules(username string) ([]models.SimpleRule, error) {
clusterRules, err := GetUserClusterRules(username)
if err != nil {
return nil, err
}
return getClusterSimpleRule(clusterRules), nil
}
func GetUserNamespaceSimpleRules(namespace, username string) ([]models.SimpleRule, error) {
clusterRules, err := GetUserClusterRules(username)
if err != nil {
return nil, err
}
rules, err := GetUserRules(namespace, username)
if err != nil {
return nil, err
}
rules = append(rules, clusterRules...)
return getSimpleRule(rules), nil
}
// Convert roles to rules
func GetRoleSimpleRules(namespace string, roleName string) ([]models.SimpleRule, error) {
roleLister := informers.SharedInformerFactory().Rbac().V1().Roles().Lister()
role, err := roleLister.Roles(namespace).Get(roleName)
if err != nil {
return nil, err
}
return getSimpleRule(role.Rules), nil
}
func getClusterSimpleRule(policyRules []v1.PolicyRule) []models.SimpleRule {
rules := make([]models.SimpleRule, 0)
for i := 0; i < len(policy.ClusterRoleRuleMapping); i++ {
validActions := make([]string, 0)
for j := 0; j < (len(policy.ClusterRoleRuleMapping[i].Actions)); j++ {
if rulesMatchesAction(policyRules, policy.ClusterRoleRuleMapping[i].Actions[j]) {
validActions = append(validActions, policy.ClusterRoleRuleMapping[i].Actions[j].Name)
}
}
if len(validActions) > 0 {
rules = append(rules, models.SimpleRule{Name: policy.ClusterRoleRuleMapping[i].Name, Actions: validActions})
}
}
return rules
}
func getSimpleRule(policyRules []v1.PolicyRule) []models.SimpleRule {
simpleRules := make([]models.SimpleRule, 0)
for i := 0; i < len(policy.RoleRuleMapping); i++ {
rule := models.SimpleRule{Name: policy.RoleRuleMapping[i].Name}
rule.Actions = make([]string, 0)
for j := 0; j < len(policy.RoleRuleMapping[i].Actions); j++ {
if rulesMatchesAction(policyRules, policy.RoleRuleMapping[i].Actions[j]) {
rule.Actions = append(rule.Actions, policy.RoleRuleMapping[i].Actions[j].Name)
}
}
if len(rule.Actions) > 0 {
simpleRules = append(simpleRules, rule)
}
}
return simpleRules
}
func CreateClusterRoleBinding(username string, clusterRoleName string) error {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
_, err := clusterRoleLister.Get(clusterRoleName)
if err != nil {
return err
}
clusterRoleBinding := &v1.ClusterRoleBinding{}
clusterRoleBinding.Name = username
clusterRoleBinding.RoleRef = v1.RoleRef{Name: clusterRoleName, Kind: ClusterRoleKind}
clusterRoleBinding.Subjects = []v1.Subject{{Kind: v1.UserKind, Name: username}}
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
found, err := clusterRoleBindingLister.Get(username)
if apierrors.IsNotFound(err) {
_, err = k8s.Client().RbacV1().ClusterRoleBindings().Create(clusterRoleBinding)
return err
} else if err != nil {
return err
}
// cluster role changed
if found.RoleRef.Name != clusterRoleBinding.RoleRef.Name {
deletePolicy := metav1.DeletePropagationForeground
deleteOption := &metav1.DeleteOptions{PropagationPolicy: &deletePolicy}
err := k8s.Client().RbacV1().ClusterRoleBindings().Delete(clusterRoleBinding.Name, deleteOption)
if err != nil {
return err
}
_, err = k8s.Client().RbacV1().ClusterRoleBindings().Create(clusterRoleBinding)
return err
}
if !k8sutil.ContainsUser(found.Subjects, username) {
found.Subjects = clusterRoleBinding.Subjects
_, err = k8s.Client().RbacV1().ClusterRoleBindings().Update(found)
return err
}
return nil
}
func RulesMatchesRequired(rules []v1.PolicyRule, required v1.PolicyRule) bool {
for _, rule := range rules {
if ruleMatchesRequired(rule, required) {
return true
}
}
return false
}
func rulesMatchesAction(rules []v1.PolicyRule, action models.Action) bool {
for _, required := range action.Rules {
if !RulesMatchesRequired(rules, required) {
return false
}
}
return true
}
func ruleMatchesRequired(rule v1.PolicyRule, required v1.PolicyRule) bool {
if len(required.NonResourceURLs) == 0 {
for _, apiGroup := range required.APIGroups {
for _, resource := range required.Resources {
resources := strings.Split(resource, "/")
resource = resources[0]
var subsource string
if len(resources) > 1 {
subsource = resources[1]
}
if len(required.ResourceNames) == 0 {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, "", verb) {
return false
}
}
} else {
for _, resourceName := range required.ResourceNames {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, resourceName, verb) {
return false
}
}
}
}
}
}
} else {
for _, apiGroup := range required.APIGroups {
for _, nonResourceURL := range required.NonResourceURLs {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, nonResourceURL, "", "", "", verb) {
return false
}
}
}
}
}
return true
}
func ruleMatchesResources(rule v1.PolicyRule, apiGroup string, resource string, subresource string, resourceName string) bool {
if resource == "" {
return false
}
if !hasString(rule.APIGroups, apiGroup) && !hasString(rule.APIGroups, v1.ResourceAll) {
return false
}
if len(rule.ResourceNames) > 0 && !hasString(rule.ResourceNames, resourceName) {
return false
}
combinedResource := resource
if subresource != "" {
combinedResource = combinedResource + "/" + subresource
}
for _, res := range rule.Resources {
// match "*"
if res == v1.ResourceAll || res == combinedResource {
return true
}
// match "*/subresource"
if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") {
return true
}
// match "resource/*"
if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") {
return true
}
}
return false
}
func ruleMatchesRequest(rule v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool {
if !hasString(rule.Verbs, verb) && !hasString(rule.Verbs, v1.VerbAll) {
return false
}
if nonResourceURL == "" {
return ruleMatchesResources(rule, apiGroup, resource, subresource, resourceName)
} else {
return ruleMatchesNonResource(rule, nonResourceURL)
}
}
func ruleMatchesNonResource(rule v1.PolicyRule, nonResourceURL string) bool {
if nonResourceURL == "" {
return false
}
for _, spec := range rule.NonResourceURLs {
if pathMatches(nonResourceURL, spec) {
return true
}
}
return false
}
func pathMatches(path, spec string) bool {
// Allow wildcard match
if spec == "*" {
return true
}
// Allow exact match
if spec == path {
return true
}
// Allow a trailing * subpath match
if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) {
return true
}
return false
}
func hasString(slice []string, value string) bool {
for _, s := range slice {
if s == value {
return true
}
}
return false
}