37 Star 407 Fork 75

GVPrancher/rancher

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
usermanager.go 16.95 KB
一键复制 编辑 原始数据 按行查看 历史
Dan Ramich 提交于 2019-06-12 13:38 . goimport linting changes
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
package common
import (
"crypto/sha256"
"encoding/base32"
"encoding/json"
"fmt"
"reflect"
"strings"
"time"
"github.com/pkg/errors"
"github.com/rancher/norman/types"
"github.com/rancher/norman/types/slice"
"github.com/rancher/rancher/pkg/auth/tokens"
"github.com/rancher/rancher/pkg/randomtoken"
v3 "github.com/rancher/types/apis/management.cattle.io/v3"
rbacv1 "github.com/rancher/types/apis/rbac.authorization.k8s.io/v1"
"github.com/rancher/types/config"
"github.com/rancher/types/user"
"github.com/sirupsen/logrus"
k8srbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
apitypes "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/cache"
)
const (
userAuthHeader = "Impersonate-User"
userByPrincipalIndex = "auth.management.cattle.io/userByPrincipal"
crtbsByPrincipalAndUserIndex = "auth.management.cattle.io/crtbByPrincipalAndUser"
prtbsByPrincipalAndUserIndex = "auth.management.cattle.io/prtbByPrincipalAndUser"
grbByUserIndex = "auth.management.cattle.io/grbByUser"
roleTemplatesRequired = "authz.management.cattle.io/creator-role-bindings"
)
func NewUserManager(scaledContext *config.ScaledContext) (user.Manager, error) {
userInformer := scaledContext.Management.Users("").Controller().Informer()
userIndexers := map[string]cache.IndexFunc{
userByPrincipalIndex: userByPrincipal,
}
if err := userInformer.AddIndexers(userIndexers); err != nil {
return nil, err
}
crtbInformer := scaledContext.Management.ClusterRoleTemplateBindings("").Controller().Informer()
crtbIndexers := map[string]cache.IndexFunc{
crtbsByPrincipalAndUserIndex: crtbsByPrincipalAndUser,
}
if err := crtbInformer.AddIndexers(crtbIndexers); err != nil {
return nil, err
}
prtbInformer := scaledContext.Management.ProjectRoleTemplateBindings("").Controller().Informer()
prtbIndexers := map[string]cache.IndexFunc{
prtbsByPrincipalAndUserIndex: prtbsByPrincipalAndUser,
}
if err := prtbInformer.AddIndexers(prtbIndexers); err != nil {
return nil, err
}
grbInformer := scaledContext.Management.GlobalRoleBindings("").Controller().Informer()
grbIndexers := map[string]cache.IndexFunc{
grbByUserIndex: grbByUser,
}
if err := grbInformer.AddIndexers(grbIndexers); err != nil {
return nil, err
}
return &userManager{
users: scaledContext.Management.Users(""),
userIndexer: userInformer.GetIndexer(),
crtbIndexer: crtbInformer.GetIndexer(),
prtbIndexer: prtbInformer.GetIndexer(),
tokens: scaledContext.Management.Tokens(""),
tokenLister: scaledContext.Management.Tokens("").Controller().Lister(),
globalRoleBindings: scaledContext.Management.GlobalRoleBindings(""),
globalRoleLister: scaledContext.Management.GlobalRoles("").Controller().Lister(),
grbIndexer: grbInformer.GetIndexer(),
clusterRoleLister: scaledContext.RBAC.ClusterRoles("").Controller().Lister(),
clusterRoleBindingLister: scaledContext.RBAC.ClusterRoleBindings("").Controller().Lister(),
rbacClient: scaledContext.RBAC,
}, nil
}
type userManager struct {
users v3.UserInterface
globalRoleBindings v3.GlobalRoleBindingInterface
globalRoleLister v3.GlobalRoleLister
grbIndexer cache.Indexer
userIndexer cache.Indexer
crtbIndexer cache.Indexer
prtbIndexer cache.Indexer
tokenLister v3.TokenLister
tokens v3.TokenInterface
clusterRoleLister rbacv1.ClusterRoleLister
clusterRoleBindingLister rbacv1.ClusterRoleBindingLister
rbacClient rbacv1.Interface
}
func (m *userManager) SetPrincipalOnCurrentUser(apiContext *types.APIContext, principal v3.Principal) (*v3.User, error) {
userID := m.GetUser(apiContext)
if userID == "" {
return nil, errors.New("user not provided")
}
return m.SetPrincipalOnCurrentUserByUserID(userID, principal)
}
func (m *userManager) SetPrincipalOnCurrentUserByUserID(userID string, principal v3.Principal) (*v3.User, error) {
user, err := m.users.Get(userID, v1.GetOptions{})
if err != nil {
return nil, err
}
if providerExists(user.PrincipalIDs, principal.Provider) {
var principalIDs []string
for _, id := range user.PrincipalIDs {
if !strings.Contains(id, principal.Provider) {
principalIDs = append(principalIDs, id)
}
}
user.PrincipalIDs = principalIDs
}
if !slice.ContainsString(user.PrincipalIDs, principal.Name) {
user.PrincipalIDs = append(user.PrincipalIDs, principal.Name)
logrus.Infof("Updating user %v. Adding principal", user.Name)
return m.users.Update(user)
}
return user, nil
}
func (m *userManager) GetUser(apiContext *types.APIContext) string {
return apiContext.Request.Header.Get(userAuthHeader)
}
// checkis if the supplied principal can login based on the accessMode and allowed principals
func (m *userManager) CheckAccess(accessMode string, allowedPrincipalIDs []string, userPrincipalID string, groups []v3.Principal) (bool, error) {
if accessMode == "unrestricted" || accessMode == "" {
return true, nil
}
if accessMode == "required" || accessMode == "restricted" {
user, err := m.checkCache(userPrincipalID)
if err != nil {
return false, err
}
userPrincipals := []string{userPrincipalID}
if user != nil {
for _, p := range user.PrincipalIDs {
if userPrincipalID != p {
userPrincipals = append(userPrincipals, p)
}
}
}
for _, p := range userPrincipals {
if slice.ContainsString(allowedPrincipalIDs, p) {
return true, nil
}
}
for _, g := range groups {
if slice.ContainsString(allowedPrincipalIDs, g.Name) {
return true, nil
}
}
if accessMode == "restricted" {
// check if any of the user's principals are in a project or cluster
var userNameAndPrincipals []string
for _, g := range groups {
userNameAndPrincipals = append(userNameAndPrincipals, g.Name)
}
if user != nil {
userNameAndPrincipals = append(userNameAndPrincipals, user.Name)
userNameAndPrincipals = append(userNameAndPrincipals, userPrincipals...)
}
return m.userExistsInClusterOrProject(userNameAndPrincipals)
}
return false, nil
}
return false, errors.Errorf("Unsupported accessMode: %v", accessMode)
}
func (m *userManager) EnsureToken(tokenName, description, userName string) (string, error) {
return m.EnsureClusterToken("", tokenName, description, userName)
}
func (m *userManager) EnsureClusterToken(clusterName, tokenName, description, userName string) (string, error) {
if strings.HasPrefix(tokenName, "token-") {
return "", errors.New("token names can't start with token-")
}
token, err := m.tokenLister.Get("", tokenName)
if err != nil && !apierrors.IsNotFound(err) {
return "", err
}
if token == nil {
key, err := randomtoken.Generate()
if err != nil {
return "", fmt.Errorf("failed to generate token key")
}
token = &v3.Token{
ObjectMeta: v1.ObjectMeta{
Name: tokenName,
Labels: map[string]string{
tokens.UserIDLabel: userName,
},
},
TTLMillis: 0,
Description: description,
UserID: userName,
AuthProvider: "local",
IsDerived: true,
Token: key,
ClusterName: clusterName,
}
logrus.Infof("Creating token for user %v", userName)
createdToken, err := m.tokens.Create(token)
if err != nil {
if !apierrors.IsAlreadyExists(err) {
return "", err
}
token, err = m.tokens.Get(tokenName, v1.GetOptions{})
if err != nil {
return "", err
}
} else {
token = createdToken
}
}
return token.Name + ":" + token.Token, nil
}
func (m *userManager) EnsureUser(principalName, displayName string) (*v3.User, error) {
var user *v3.User
var err error
var labelSet labels.Set
// First check the local cache
user, err = m.checkCache(principalName)
if err != nil {
return nil, err
}
if user == nil {
// Not in cache, query API by label
user, labelSet, err = m.checkLabels(principalName)
if err != nil {
return nil, err
}
}
if user != nil {
// If the user does not have the annotation it indicates the user was created
// through the UI or from a previous rancher version so don't add the
// default bindings.
if _, ok := user.Annotations[roleTemplatesRequired]; !ok {
return user, nil
}
if v3.UserConditionInitialRolesPopulated.IsTrue(user) {
// The users global role bindings were already created. They can differ
// from what is in the annotation if they were updated manually.
return user, nil
}
} else {
// User doesn't exist, create user
logrus.Infof("Creating user for principal %v", principalName)
// Create a hash of the principalName to use as the name for the user,
// this lets k8s tell us if there are duplicate users with the same name
// thus avoiding a race.
hasher := sha256.New()
hasher.Write([]byte(principalName))
sha := base32.StdEncoding.WithPadding(-1).EncodeToString(hasher.Sum(nil))[:10]
annotations, err := m.createUsersRoleAnnotation()
if err != nil {
return nil, err
}
user = &v3.User{
ObjectMeta: v1.ObjectMeta{
Name: "u-" + strings.ToLower(sha),
Labels: labelSet,
Annotations: annotations,
},
DisplayName: displayName,
PrincipalIDs: []string{principalName},
}
user, err = m.users.Create(user)
if err != nil {
return nil, err
}
err = m.CreateNewUserClusterRoleBinding(user.Name, user.UID)
if err != nil {
return nil, err
}
}
logrus.Infof("Creating globalRoleBindings for %v", user.Name)
err = m.createUsersBindings(user)
if err != nil {
return nil, err
}
return user, nil
}
func (m *userManager) CreateNewUserClusterRoleBinding(userName string, userUID apitypes.UID) error {
roleName := userName + "-view"
bindingName := "grb-" + roleName
ownerReference := v1.OwnerReference{
APIVersion: "management.cattle.io/v3",
Kind: "User",
Name: userName,
UID: userUID,
}
cr, err := m.clusterRoleLister.Get("", roleName)
if err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// ClusterRole doesn't exist yet, create it.
rule := k8srbacv1.PolicyRule{
Verbs: []string{"get"},
APIGroups: []string{"management.cattle.io"},
Resources: []string{"users"},
ResourceNames: []string{userName},
}
role := &k8srbacv1.ClusterRole{
ObjectMeta: v1.ObjectMeta{
Name: roleName,
OwnerReferences: []v1.OwnerReference{ownerReference},
},
Rules: []k8srbacv1.PolicyRule{rule},
}
cr, err = m.rbacClient.ClusterRoles("").Create(role)
if err != nil {
if !apierrors.IsAlreadyExists(err) {
return err
}
}
}
_, err = m.clusterRoleBindingLister.Get("", bindingName)
if err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// ClusterRoleBinding doesn't exit yet, create it.
crb := &k8srbacv1.ClusterRoleBinding{
ObjectMeta: v1.ObjectMeta{
Name: bindingName,
OwnerReferences: []v1.OwnerReference{ownerReference},
},
Subjects: []k8srbacv1.Subject{
k8srbacv1.Subject{
Kind: "User",
Name: userName,
},
},
RoleRef: k8srbacv1.RoleRef{
Kind: "ClusterRole",
Name: cr.Name,
},
}
_, err = m.rbacClient.ClusterRoleBindings("").Create(crb)
if err != nil {
if !apierrors.IsAlreadyExists(err) {
return err
}
}
}
return nil
}
func (m *userManager) createUsersBindings(user *v3.User) error {
roleMap := make(map[string][]string)
err := json.Unmarshal([]byte(user.Annotations[roleTemplatesRequired]), &roleMap)
if err != nil {
return err
}
// Collect the users existing globalRoleBindings
var existingGRB []string
grbs, err := m.grbIndexer.ByIndex(grbByUserIndex, user.Name)
if err != nil {
return err
}
for _, grb := range grbs {
binding, ok := grb.(*v3.GlobalRoleBinding)
if !ok {
continue
}
existingGRB = append(existingGRB, binding.GlobalRoleName)
}
var createdRoles []string
for _, role := range roleMap["required"] {
if !slice.ContainsString(existingGRB, role) {
_, err := m.globalRoleBindings.Create(&v3.GlobalRoleBinding{
ObjectMeta: v1.ObjectMeta{
GenerateName: "grb-",
},
UserName: user.Name,
GlobalRoleName: role,
})
if err != nil {
return err
}
}
createdRoles = append(createdRoles, role)
}
roleMap["created"] = createdRoles
d, err := json.Marshal(roleMap)
if err != nil {
return err
}
rtr := string(d)
sleepTime := 100
// The user needs updated so keep trying if there is a conflict
for i := 0; i <= 3; i++ {
user, err = m.users.Get(user.Name, v1.GetOptions{})
if err != nil {
return err
}
user.Annotations[roleTemplatesRequired] = rtr
if reflect.DeepEqual(roleMap["required"], createdRoles) {
v3.UserConditionInitialRolesPopulated.True(user)
}
_, err = m.users.Update(user)
if err != nil {
if apierrors.IsConflict(err) {
// Conflict on the user, sleep and try again
time.Sleep(time.Duration(sleepTime) * time.Millisecond)
sleepTime *= 2
continue
}
return err
}
break
}
return nil
}
func (m *userManager) createUsersRoleAnnotation() (map[string]string, error) {
roleMap := make(map[string][]string)
roles, err := m.globalRoleLister.List("", labels.NewSelector())
if err != nil {
return nil, err
}
for _, gr := range roles {
if gr.NewUserDefault {
roleMap["required"] = append(roleMap["required"], gr.Name)
}
}
d, err := json.Marshal(roleMap)
if err != nil {
return nil, err
}
annotations := make(map[string]string)
annotations[roleTemplatesRequired] = string(d)
return annotations, nil
}
func (m *userManager) GetUserByPrincipalID(principalName string) (*v3.User, error) {
user, err := m.checkCache(principalName)
if err != nil {
return nil, err
}
if user == nil {
// Not in cache, query API by label
user, _, err = m.checkLabels(principalName)
if err != nil {
return nil, err
}
}
return user, nil
}
func (m *userManager) checkCache(principalName string) (*v3.User, error) {
users, err := m.userIndexer.ByIndex(userByPrincipalIndex, principalName)
if err != nil {
return nil, err
}
if len(users) > 1 {
return nil, errors.Errorf("can't find unique user for principal %v", principalName)
}
if len(users) == 1 {
u := users[0].(*v3.User)
return u.DeepCopy(), nil
}
return nil, nil
}
func (m *userManager) userExistsInClusterOrProject(userNameAndPrincipals []string) (bool, error) {
for _, principal := range userNameAndPrincipals {
crtbs, err := m.crtbIndexer.ByIndex(crtbsByPrincipalAndUserIndex, principal)
if err != nil {
return false, err
}
if len(crtbs) > 0 {
return true, nil
}
prtbs, err := m.prtbIndexer.ByIndex(prtbsByPrincipalAndUserIndex, principal)
if err != nil {
return false, err
}
if len(prtbs) > 0 {
return true, nil
}
}
return false, nil
}
func (m *userManager) checkLabels(principalName string) (*v3.User, labels.Set, error) {
encodedPrincipalID := base32.HexEncoding.WithPadding(base32.NoPadding).EncodeToString([]byte(principalName))
if len(encodedPrincipalID) > 63 {
encodedPrincipalID = encodedPrincipalID[:63]
}
set := labels.Set(map[string]string{encodedPrincipalID: "hashed-principal-name"})
users, err := m.users.List(v1.ListOptions{LabelSelector: set.String()})
if err != nil {
return nil, nil, err
}
if len(users.Items) == 0 {
return nil, set, nil
}
var match *v3.User
for _, u := range users.Items {
if slice.ContainsString(u.PrincipalIDs, principalName) {
if match != nil {
// error out on duplicates
return nil, nil, errors.Errorf("can't find unique user for principal %v", principalName)
}
match = &u
}
}
return match, set, nil
}
func userByPrincipal(obj interface{}) ([]string, error) {
u, ok := obj.(*v3.User)
if !ok {
return []string{}, nil
}
return u.PrincipalIDs, nil
}
func crtbsByPrincipalAndUser(obj interface{}) ([]string, error) {
var principals []string
b, ok := obj.(*v3.ClusterRoleTemplateBinding)
if !ok {
return []string{}, nil
}
if b.GroupPrincipalName != "" {
principals = append(principals, b.GroupPrincipalName)
}
if b.UserPrincipalName != "" {
principals = append(principals, b.UserPrincipalName)
}
if b.UserName != "" {
principals = append(principals, b.UserName)
}
return principals, nil
}
func prtbsByPrincipalAndUser(obj interface{}) ([]string, error) {
var principals []string
b, ok := obj.(*v3.ProjectRoleTemplateBinding)
if !ok {
return []string{}, nil
}
if b.GroupPrincipalName != "" {
principals = append(principals, b.GroupPrincipalName)
}
if b.UserPrincipalName != "" {
principals = append(principals, b.UserPrincipalName)
}
if b.UserName != "" {
principals = append(principals, b.UserName)
}
return principals, nil
}
func grbByUser(obj interface{}) ([]string, error) {
grb, ok := obj.(*v3.GlobalRoleBinding)
if !ok {
return []string{}, nil
}
return []string{grb.UserName}, nil
}
func providerExists(principalIDs []string, provider string) bool {
for _, id := range principalIDs {
splitID := strings.Split(id, ":")[0]
if strings.Contains(splitID, provider) {
return true
}
}
return false
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/rancher/rancher.git
git@gitee.com:rancher/rancher.git
rancher
rancher
rancher
v2.2.13-rc2

搜索帮助