github.com/polarismesh/polaris@v1.17.8/store/boltdb/group.go (about)

     1  /**
     2   * Tencent is pleased to support the open source community by making Polaris available.
     3   *
     4   * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
     5   *
     6   * Licensed under the BSD 3-Clause License (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   * https://opensource.org/licenses/BSD-3-Clause
    11   *
    12   * Unless required by applicable law or agreed to in writing, software distributed
    13   * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    14   * CONDITIONS OF ANY KIND, either express or implied. See the License for the
    15   * specific language governing permissions and limitations under the License.
    16   */
    17  
    18  package boltdb
    19  
    20  import (
    21  	"errors"
    22  	"fmt"
    23  	"sort"
    24  	"strings"
    25  	"time"
    26  
    27  	bolt "go.etcd.io/bbolt"
    28  	"go.uber.org/zap"
    29  
    30  	"github.com/polarismesh/polaris/common/model"
    31  	"github.com/polarismesh/polaris/common/utils"
    32  	"github.com/polarismesh/polaris/store"
    33  )
    34  
    35  var (
    36  	// ErrorMultipleGroupFound is returned when multiple groups are found.
    37  	ErrorMultipleGroupFound error = errors.New("multiple group found")
    38  	// ErrorGroupNotFound is returned when a group is not found.
    39  	ErrorGroupNotFound error = errors.New("usergroup not found")
    40  )
    41  
    42  const (
    43  	tblGroup string = "group"
    44  
    45  	GroupFieldID          string = "ID"
    46  	GroupFieldName        string = "Name"
    47  	GroupFieldOwner       string = "Owner"
    48  	GroupFieldToken       string = "Token"
    49  	GroupFieldTokenEnable string = "TokenEnable"
    50  	GroupFieldValid       string = "Valid"
    51  	GroupFieldComment     string = "Comment"
    52  	GroupFieldCreateTime  string = "CreateTime"
    53  	GroupFieldModifyTime  string = "ModifyTime"
    54  	GroupFieldUserIds     string = "UserIds"
    55  )
    56  
    57  type groupForStore struct {
    58  	ID          string
    59  	Name        string
    60  	Owner       string
    61  	Token       string
    62  	TokenEnable bool
    63  	Valid       bool
    64  	Comment     string
    65  	CreateTime  time.Time
    66  	ModifyTime  time.Time
    67  	UserIds     map[string]string
    68  }
    69  
    70  // groupStore
    71  type groupStore struct {
    72  	handler BoltHandler
    73  }
    74  
    75  // AddGroup add a group
    76  func (gs *groupStore) AddGroup(group *model.UserGroupDetail) error {
    77  	if group.ID == "" || group.Name == "" || group.Token == "" {
    78  		return store.NewStatusError(store.EmptyParamsErr, fmt.Sprintf(
    79  			"add usergroup missing some params, groupId is %s, name is %s", group.ID, group.Name))
    80  	}
    81  
    82  	proxy, err := gs.handler.StartTx()
    83  	if err != nil {
    84  		return err
    85  	}
    86  	tx := proxy.GetDelegateTx().(*bolt.Tx)
    87  
    88  	defer func() {
    89  		_ = tx.Rollback()
    90  	}()
    91  
    92  	if err := gs.cleanInValidGroup(tx, group.Name, group.Owner); err != nil {
    93  		log.Error("[Store][Group] clean invalid usergroup", zap.Error(err),
    94  			zap.String("name", group.Name), zap.String("owner", group.Owner))
    95  		return err
    96  	}
    97  
    98  	return gs.addGroup(tx, group)
    99  }
   100  
   101  // addGroup to boltdb
   102  func (gs *groupStore) addGroup(tx *bolt.Tx, group *model.UserGroupDetail) error {
   103  
   104  	group.Valid = true
   105  	group.CreateTime = time.Now()
   106  	group.ModifyTime = group.CreateTime
   107  
   108  	data := convertForGroupStore(group)
   109  
   110  	if err := saveValue(tx, tblGroup, data.ID, data); err != nil {
   111  		log.Error("[Store][Group] save usergroup", zap.Error(err),
   112  			zap.String("name", group.Name), zap.String("owner", group.Owner))
   113  
   114  		return err
   115  	}
   116  
   117  	if err := createDefaultStrategy(tx, model.PrincipalGroup, data.ID, data.Name,
   118  		data.Owner); err != nil {
   119  		log.Error("[Store][Group] add usergroup default strategy", zap.Error(err),
   120  			zap.String("name", group.Name), zap.String("owner", group.Owner))
   121  
   122  		return err
   123  	}
   124  
   125  	if err := tx.Commit(); err != nil {
   126  		log.Error("[Store][Group] add usergroup tx commit", zap.Error(err),
   127  			zap.String("name", group.Name), zap.String("owner", group.Owner))
   128  		return err
   129  	}
   130  
   131  	return nil
   132  }
   133  
   134  // UpdateGroup update a group
   135  func (gs *groupStore) UpdateGroup(group *model.ModifyUserGroup) error {
   136  	if group.ID == "" {
   137  		return store.NewStatusError(store.EmptyParamsErr, fmt.Sprintf(
   138  			"update usergroup missing some params, groupId is %s", group.ID))
   139  	}
   140  
   141  	return gs.updateGroup(group)
   142  }
   143  
   144  func (gs *groupStore) updateGroup(group *model.ModifyUserGroup) error {
   145  	proxy, err := gs.handler.StartTx()
   146  	if err != nil {
   147  		return err
   148  	}
   149  	tx := proxy.GetDelegateTx().(*bolt.Tx)
   150  
   151  	defer func() {
   152  		_ = tx.Rollback()
   153  	}()
   154  
   155  	values := make(map[string]interface{})
   156  
   157  	if err := loadValues(tx, tblGroup, []string{group.ID}, &groupForStore{}, values); err != nil {
   158  		log.Error("[Store][Group] get usergroup by id", zap.Error(err), zap.String("id", group.ID))
   159  	}
   160  
   161  	if len(values) == 0 {
   162  		return ErrorGroupNotFound
   163  	}
   164  
   165  	if len(values) > 1 {
   166  		return ErrorMultipleGroupFound
   167  	}
   168  
   169  	var ret *model.UserGroupDetail
   170  	for _, v := range values {
   171  		ret = convertForGroupDetail(v.(*groupForStore))
   172  		break
   173  	}
   174  
   175  	ret.Comment = group.Comment
   176  	ret.Token = group.Token
   177  	ret.TokenEnable = group.TokenEnable
   178  	ret.ModifyTime = time.Now()
   179  
   180  	updateGroupRelation(ret, group)
   181  
   182  	if err := saveValue(tx, tblGroup, ret.ID, convertForGroupStore(ret)); err != nil {
   183  		log.Error("[Store][Group] update usergroup", zap.Error(err), zap.String("id", ret.ID))
   184  		return err
   185  	}
   186  
   187  	if err := tx.Commit(); err != nil {
   188  		log.Error("[Store][Group] update usergroup tx commit",
   189  			zap.Error(err), zap.String("id", ret.ID))
   190  		return err
   191  	}
   192  	return nil
   193  }
   194  
   195  // updateGroupRelation 更新用户组的关联关系数据
   196  func updateGroupRelation(group *model.UserGroupDetail, modify *model.ModifyUserGroup) {
   197  	for i := range modify.AddUserIds {
   198  		group.UserIds[modify.AddUserIds[i]] = struct{}{}
   199  	}
   200  
   201  	for i := range modify.RemoveUserIds {
   202  		delete(group.UserIds, modify.RemoveUserIds[i])
   203  	}
   204  }
   205  
   206  // DeleteGroup 删除用户组
   207  func (gs *groupStore) DeleteGroup(group *model.UserGroupDetail) error {
   208  	if group.ID == "" {
   209  		return store.NewStatusError(store.EmptyParamsErr, fmt.Sprintf(
   210  			"delete usergroup missing some params, groupId is %s", group.ID))
   211  	}
   212  
   213  	return gs.deleteGroup(group)
   214  }
   215  
   216  func (gs *groupStore) deleteGroup(group *model.UserGroupDetail) error {
   217  	proxy, err := gs.handler.StartTx()
   218  	if err != nil {
   219  		return err
   220  	}
   221  	tx := proxy.GetDelegateTx().(*bolt.Tx)
   222  
   223  	defer func() {
   224  		_ = tx.Rollback()
   225  	}()
   226  
   227  	properties := make(map[string]interface{})
   228  	properties[GroupFieldValid] = false
   229  	properties[GroupFieldModifyTime] = time.Now()
   230  
   231  	if err := updateValue(tx, tblGroup, group.ID, properties); err != nil {
   232  		log.Error("[Store][Group] remove usergroup", zap.Error(err), zap.String("id", group.ID))
   233  		return err
   234  	}
   235  
   236  	if err := cleanLinkStrategy(tx, model.PrincipalGroup, group.ID, group.Owner); err != nil {
   237  		log.Error("[Store][Group] clean usergroup default strategy",
   238  			zap.Error(err), zap.String("id", group.ID))
   239  		return err
   240  	}
   241  
   242  	if err := tx.Commit(); err != nil {
   243  		log.Error("[Store][Group] delete usergroupr tx commit",
   244  			zap.Error(err), zap.String("id", group.ID))
   245  		return err
   246  	}
   247  
   248  	return nil
   249  }
   250  
   251  // GetGroup get a group
   252  func (gs *groupStore) GetGroup(groupID string) (*model.UserGroupDetail, error) {
   253  	if groupID == "" {
   254  		return nil, store.NewStatusError(store.EmptyParamsErr, fmt.Sprintf(
   255  			"get usergroup missing some params, groupID is %s", groupID))
   256  	}
   257  
   258  	values, err := gs.handler.LoadValues(tblGroup, []string{groupID}, &groupForStore{})
   259  	if err != nil {
   260  		log.Error("[Store][Group] get usergroup by id", zap.Error(err), zap.String("id", groupID))
   261  		return nil, err
   262  	}
   263  
   264  	if len(values) == 0 {
   265  		return nil, nil
   266  	}
   267  
   268  	if len(values) > 1 {
   269  		return nil, ErrorMultipleGroupFound
   270  	}
   271  
   272  	var ret *model.UserGroupDetail
   273  	for _, v := range values {
   274  		ret = convertForGroupDetail(v.(*groupForStore))
   275  		break
   276  	}
   277  
   278  	if ret.Valid {
   279  		return ret, nil
   280  	}
   281  
   282  	return nil, nil
   283  }
   284  
   285  // GetGroupByName get a group by name
   286  func (gs *groupStore) GetGroupByName(name, owner string) (*model.UserGroup, error) {
   287  	if name == "" || owner == "" {
   288  		return nil, store.NewStatusError(store.EmptyParamsErr, fmt.Sprintf(
   289  			"get usergroup missing some params, name=%s, owner=%s", name, owner))
   290  	}
   291  
   292  	fields := []string{GroupFieldName, GroupFieldOwner, GroupFieldValid}
   293  	values, err := gs.handler.LoadValuesByFilter(tblGroup, fields, &groupForStore{},
   294  		func(m map[string]interface{}) bool {
   295  			valid, ok := m[GroupFieldValid].(bool)
   296  			if ok && !valid {
   297  				return false
   298  			}
   299  
   300  			saveName := m[GroupFieldName]
   301  			saveOwner := m[GroupFieldOwner]
   302  
   303  			return saveName == name && saveOwner == owner
   304  		})
   305  
   306  	if err != nil {
   307  		return nil, err
   308  	}
   309  
   310  	if len(values) == 0 {
   311  		return nil, nil
   312  	}
   313  
   314  	if len(values) > 1 {
   315  		return nil, ErrorMultipleGroupFound
   316  	}
   317  	var ret *model.UserGroupDetail
   318  	for _, v := range values {
   319  		ret = convertForGroupDetail(v.(*groupForStore))
   320  		break
   321  	}
   322  	return ret.UserGroup, nil
   323  }
   324  
   325  // GetGroups get groups
   326  func (gs *groupStore) GetGroups(filters map[string]string, offset uint32,
   327  	limit uint32) (uint32, []*model.UserGroup, error) {
   328  
   329  	// 如果本次请求参数携带了 user_id,那么就是查询这个用户所关联的所有用户组
   330  	if _, ok := filters["user_id"]; ok {
   331  		return gs.listGroupByUser(filters, offset, limit)
   332  	}
   333  	// 正常查询用户组信息
   334  	return gs.listSimpleGroups(filters, offset, limit)
   335  }
   336  
   337  // listSimpleGroups Normal user group query
   338  func (gs *groupStore) listSimpleGroups(filters map[string]string, offset uint32, limit uint32) (uint32,
   339  	[]*model.UserGroup, error) {
   340  	fields := []string{GroupFieldID, GroupFieldOwner, GroupFieldName, GroupFieldValid}
   341  	values, err := gs.handler.LoadValuesByFilter(tblGroup, fields, &groupForStore{},
   342  		func(m map[string]interface{}) bool {
   343  			valid, ok := m[GroupFieldValid].(bool)
   344  			if ok && !valid {
   345  				return false
   346  			}
   347  
   348  			saveId, _ := m[GroupFieldID].(string)
   349  			saveName, _ := m[GroupFieldName].(string)
   350  			saveOwner, _ := m[GroupFieldOwner].(string)
   351  
   352  			if sId, ok := filters["id"]; ok && sId != saveId {
   353  				return false
   354  			}
   355  			if sName, ok := filters["name"]; ok {
   356  				if utils.IsPrefixWildName(sName) {
   357  					sName = sName[:len(sName)-1]
   358  				}
   359  				if !strings.Contains(saveName, sName) {
   360  					return false
   361  				}
   362  			}
   363  
   364  			if sOwner, ok := filters["owner"]; ok && sOwner != saveOwner {
   365  				return false
   366  			}
   367  
   368  			return true
   369  		})
   370  
   371  	if err != nil {
   372  		return 0, nil, err
   373  	}
   374  
   375  	total := uint32(len(values))
   376  
   377  	return total, doGroupPage(values, offset, limit), nil
   378  }
   379  
   380  // listGroupByUser 查询某个用户下所关联的用户组信息
   381  func (gs *groupStore) listGroupByUser(filters map[string]string, offset uint32,
   382  	limit uint32) (uint32, []*model.UserGroup, error) {
   383  
   384  	var (
   385  		userID            = filters["user_id"]
   386  		owner, existOwner = filters["owner"]
   387  		fields            = []string{GroupFieldUserIds, GroupFieldOwner, GroupFieldValid}
   388  	)
   389  
   390  	values, err := gs.handler.LoadValuesByFilter(tblGroup, fields, &groupForStore{},
   391  		func(m map[string]interface{}) bool {
   392  			valid, ok := m[GroupFieldValid].(bool)
   393  			if ok && !valid {
   394  				return false
   395  			}
   396  			saveOwner := m[GroupFieldOwner]
   397  			if existOwner && saveOwner != owner {
   398  				return false
   399  			}
   400  
   401  			if sName, ok := filters["name"]; ok {
   402  				saveName, _ := m[GroupFieldName].(string)
   403  				if utils.IsPrefixWildName(sName) {
   404  					sName = sName[:len(sName)-1]
   405  				}
   406  				if !strings.Contains(saveName, sName) {
   407  					return false
   408  				}
   409  			}
   410  
   411  			saveVal, ok := m[GroupFieldUserIds]
   412  			if !ok {
   413  				return false
   414  			}
   415  
   416  			saveUserIds := saveVal.(map[string]string)
   417  			_, exist := saveUserIds[userID]
   418  			return exist
   419  		})
   420  
   421  	if err != nil {
   422  		return 0, nil, err
   423  	}
   424  
   425  	total := uint32(len(values))
   426  
   427  	return total, doGroupPage(values, offset, limit), nil
   428  }
   429  
   430  func doGroupPage(ret map[string]interface{}, offset uint32, limit uint32) []*model.UserGroup {
   431  
   432  	groups := make([]*model.UserGroup, 0, len(ret))
   433  
   434  	beginIndex := offset
   435  	endIndex := beginIndex + limit
   436  	totalCount := uint32(len(ret))
   437  
   438  	if totalCount == 0 {
   439  		return groups
   440  	}
   441  	if beginIndex >= endIndex {
   442  		return groups
   443  	}
   444  	if beginIndex >= totalCount {
   445  		return groups
   446  	}
   447  	if endIndex > totalCount {
   448  		endIndex = totalCount
   449  	}
   450  	for k := range ret {
   451  		groups = append(groups, convertForGroupDetail(ret[k].(*groupForStore)).UserGroup)
   452  	}
   453  
   454  	sort.Slice(groups, func(i, j int) bool {
   455  		return groups[i].ModifyTime.After(groups[j].ModifyTime)
   456  	})
   457  
   458  	return groups[beginIndex:endIndex]
   459  }
   460  
   461  // GetGroupsForCache 查询用户分组数据,主要用于Cache更新
   462  func (gs *groupStore) GetGroupsForCache(mtime time.Time, firstUpdate bool) ([]*model.UserGroupDetail, error) {
   463  	ret, err := gs.handler.LoadValuesByFilter(tblGroup, []string{GroupFieldModifyTime}, &groupForStore{},
   464  		func(m map[string]interface{}) bool {
   465  			mt := m[GroupFieldModifyTime].(time.Time)
   466  			isAfter := mt.After(mtime)
   467  			return isAfter
   468  		})
   469  	if err != nil {
   470  		return nil, err
   471  	}
   472  
   473  	groups := make([]*model.UserGroupDetail, 0, len(ret))
   474  
   475  	for k := range ret {
   476  		val := ret[k]
   477  		groups = append(groups, convertForGroupDetail(val.(*groupForStore)))
   478  	}
   479  
   480  	return groups, nil
   481  }
   482  
   483  // cleanInValidGroup 清理无效的用户组数据
   484  func (gs *groupStore) cleanInValidGroup(tx *bolt.Tx, name, owner string) error {
   485  	log.Infof("[Store][User] clean usergroup(%s)", name)
   486  
   487  	fields := []string{GroupFieldName, GroupFieldValid, GroupFieldOwner}
   488  
   489  	values := make(map[string]interface{})
   490  
   491  	err := loadValuesByFilter(tx, tblGroup, fields, &groupForStore{},
   492  		func(m map[string]interface{}) bool {
   493  			valid, ok := m[GroupFieldValid].(bool)
   494  			// 如果数据是 valid 的,则不能被清理
   495  			if ok && valid {
   496  				return false
   497  			}
   498  
   499  			saveName := m[GroupFieldName]
   500  			saveOwner := m[GroupFieldOwner]
   501  
   502  			return saveName == name && saveOwner == owner
   503  		}, values)
   504  
   505  	if err != nil {
   506  		return err
   507  	}
   508  
   509  	if len(values) == 0 {
   510  		return nil
   511  	}
   512  
   513  	keys := make([]string, 0, len(values))
   514  	for k := range values {
   515  		keys = append(keys, k)
   516  	}
   517  
   518  	return deleteValues(tx, tblGroup, keys)
   519  }
   520  
   521  func convertForGroupStore(group *model.UserGroupDetail) *groupForStore {
   522  
   523  	userIds := make(map[string]string, len(group.UserIds))
   524  
   525  	for id := range group.UserIds {
   526  		userIds[id] = ""
   527  	}
   528  
   529  	return &groupForStore{
   530  		ID:          group.ID,
   531  		Name:        group.Name,
   532  		Owner:       group.Owner,
   533  		Token:       group.Token,
   534  		TokenEnable: group.TokenEnable,
   535  		Valid:       group.Valid,
   536  		Comment:     group.Comment,
   537  		CreateTime:  group.CreateTime,
   538  		ModifyTime:  group.ModifyTime,
   539  		UserIds:     userIds,
   540  	}
   541  }
   542  
   543  func convertForGroupDetail(group *groupForStore) *model.UserGroupDetail {
   544  	userIds := make(map[string]struct{}, len(group.UserIds))
   545  	for id := range group.UserIds {
   546  		userIds[id] = struct{}{}
   547  	}
   548  
   549  	return &model.UserGroupDetail{
   550  		UserGroup: &model.UserGroup{
   551  			ID:          group.ID,
   552  			Name:        group.Name,
   553  			Owner:       group.Owner,
   554  			Token:       group.Token,
   555  			TokenEnable: group.TokenEnable,
   556  			Valid:       group.Valid,
   557  			Comment:     group.Comment,
   558  			CreateTime:  group.CreateTime,
   559  			ModifyTime:  group.ModifyTime,
   560  		},
   561  		UserIds: userIds,
   562  	}
   563  }