github.com/oam-dev/kubevela@v1.9.11/pkg/auth/identity.go (about)

     1  /*
     2  Copyright 2022 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package auth
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  
    23  	corev1 "k8s.io/api/core/v1"
    24  	rbacv1 "k8s.io/api/rbac/v1"
    25  	"k8s.io/apiserver/pkg/authentication/serviceaccount"
    26  	"k8s.io/utils/strings/slices"
    27  )
    28  
    29  // Identity the kubernetes identity
    30  type Identity struct {
    31  	User                    string
    32  	Groups                  []string
    33  	ServiceAccount          string
    34  	ServiceAccountNamespace string
    35  }
    36  
    37  // String .
    38  func (identity *Identity) String() string {
    39  	var tokens []string
    40  	if identity.User != "" {
    41  		tokens = append(tokens, "User="+identity.User)
    42  	}
    43  	if len(identity.Groups) > 0 {
    44  		tokens = append(tokens, "Groups="+strings.Join(identity.Groups, ","))
    45  	}
    46  	if identity.ServiceAccount != "" {
    47  		tokens = append(tokens, "SA="+serviceaccount.MakeUsername(identity.ServiceAccountNamespace, identity.ServiceAccount))
    48  	}
    49  	return strings.Join(tokens, " ")
    50  }
    51  
    52  // Match validate if identity matches rbac subject
    53  func (identity *Identity) Match(subject rbacv1.Subject) bool {
    54  	switch subject.Kind {
    55  	case rbacv1.UserKind:
    56  		return subject.Name == identity.User
    57  	case rbacv1.GroupKind:
    58  		return slices.Contains(identity.Groups, subject.Name)
    59  	case rbacv1.ServiceAccountKind:
    60  		return serviceaccount.MatchesUsername(subject.Namespace, subject.Name,
    61  			serviceaccount.MakeUsername(identity.ServiceAccountNamespace, identity.ServiceAccount))
    62  	default:
    63  		return false
    64  	}
    65  }
    66  
    67  // MatchAny validate if identity matches any one of the rbac subjects
    68  func (identity *Identity) MatchAny(subjects []rbacv1.Subject) bool {
    69  	for _, subject := range subjects {
    70  		if identity.Match(subject) {
    71  			return true
    72  		}
    73  	}
    74  	return false
    75  }
    76  
    77  // Regularize clean up input info
    78  func (identity *Identity) Regularize() {
    79  	identity.User = strings.TrimSpace(identity.User)
    80  	groupMap := map[string]struct{}{}
    81  	var groups []string
    82  	for _, group := range identity.Groups {
    83  		group = strings.TrimSpace(group)
    84  		if _, found := groupMap[group]; !found {
    85  			groupMap[group] = struct{}{}
    86  			groups = append(groups, group)
    87  		}
    88  	}
    89  	identity.Groups = groups
    90  	identity.ServiceAccount = strings.TrimSpace(identity.ServiceAccount)
    91  	if identity.ServiceAccount != "" {
    92  		if identity.ServiceAccountNamespace == "" {
    93  			identity.ServiceAccountNamespace = corev1.NamespaceDefault
    94  		}
    95  	}
    96  }
    97  
    98  // Validate check if identity is valid
    99  func (identity *Identity) Validate() error {
   100  	if identity.User == "" && identity.ServiceAccount == "" {
   101  		return fmt.Errorf("either `user` or `serviceaccount` should be set")
   102  	}
   103  	if identity.User != "" && identity.ServiceAccount != "" {
   104  		return fmt.Errorf("cannot set `user` and `serviceaccount` at the same time")
   105  	}
   106  	if len(identity.Groups) > 0 && identity.ServiceAccount != "" {
   107  		return fmt.Errorf("cannot set `group` and `serviceaccount` at the same time")
   108  	}
   109  	if identity.ServiceAccount == "" && identity.ServiceAccountNamespace != "" {
   110  		return fmt.Errorf("cannot set serviceaccount namespace when serviceaccount is not set")
   111  	}
   112  	return nil
   113  }
   114  
   115  // Subjects return rbac subjects
   116  func (identity *Identity) Subjects() []rbacv1.Subject {
   117  	var subs []rbacv1.Subject
   118  	if identity.User != "" {
   119  		subs = append(subs, rbacv1.Subject{Kind: rbacv1.UserKind, APIGroup: rbacv1.GroupName, Name: identity.User})
   120  	}
   121  	for _, group := range identity.Groups {
   122  		subs = append(subs, rbacv1.Subject{Kind: rbacv1.GroupKind, APIGroup: rbacv1.GroupName, Name: group})
   123  	}
   124  	if identity.ServiceAccount != "" {
   125  		subs = append(subs, rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Name: identity.ServiceAccount, Namespace: identity.ServiceAccountNamespace})
   126  	}
   127  	return subs
   128  }