github.com/polarismesh/polaris@v1.17.8/auth/defaultauth/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 defaultauth
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"time"
    24  
    25  	"github.com/gogo/protobuf/jsonpb"
    26  	apimodel "github.com/polarismesh/specification/source/go/api/v1/model"
    27  	apisecurity "github.com/polarismesh/specification/source/go/api/v1/security"
    28  	apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage"
    29  	"go.uber.org/zap"
    30  
    31  	api "github.com/polarismesh/polaris/common/api/v1"
    32  	"github.com/polarismesh/polaris/common/model"
    33  	authcommon "github.com/polarismesh/polaris/common/model/auth"
    34  	commonstore "github.com/polarismesh/polaris/common/store"
    35  	commontime "github.com/polarismesh/polaris/common/time"
    36  	"github.com/polarismesh/polaris/common/utils"
    37  )
    38  
    39  type (
    40  	// UserGroup2Api is the user group to api
    41  	UserGroup2Api func(user *model.UserGroup) *apisecurity.UserGroup
    42  )
    43  
    44  var (
    45  	// UserLinkGroupAttributes is the user link group attributes
    46  	UserLinkGroupAttributes = map[string]bool{
    47  		"id":        true,
    48  		"user_id":   true,
    49  		"user_name": true,
    50  		"group_id":  true,
    51  		"name":      true,
    52  		"offset":    true,
    53  		"limit":     true,
    54  	}
    55  )
    56  
    57  // CreateGroup create a group
    58  func (svr *Server) CreateGroup(ctx context.Context, req *apisecurity.UserGroup) *apiservice.Response {
    59  	var (
    60  		requestID  = utils.ParseRequestID(ctx)
    61  		platformID = utils.ParsePlatformID(ctx)
    62  		ownerID    = utils.ParseOwnerID(ctx)
    63  	)
    64  
    65  	req.Owner = utils.NewStringValue(ownerID)
    66  	if checkErrResp := svr.checkCreateGroup(ctx, req); checkErrResp != nil {
    67  		return checkErrResp
    68  	}
    69  
    70  	// 根据 owner + groupname 确定唯一的用户组信息
    71  	group, err := svr.storage.GetGroupByName(req.Name.GetValue(), ownerID)
    72  	if err != nil {
    73  		log.Error("get group when create", utils.ZapRequestID(requestID),
    74  			utils.ZapPlatformID(platformID), zap.Error(err))
    75  		return api.NewGroupResponse(commonstore.StoreCode2APICode(err), req)
    76  	}
    77  
    78  	if group != nil {
    79  		return api.NewGroupResponse(apimodel.Code_UserGroupExisted, req)
    80  	}
    81  
    82  	data, err := createGroupModel(req)
    83  	if err != nil {
    84  		log.Error("create group model", utils.ZapRequestID(requestID),
    85  			utils.ZapPlatformID(platformID), zap.Error(err))
    86  		return api.NewAuthResponseWithMsg(apimodel.Code_ExecuteException, err.Error())
    87  	}
    88  
    89  	if err := svr.storage.AddGroup(data); err != nil {
    90  		log.Error(err.Error(), utils.ZapRequestID(requestID), utils.ZapPlatformID(platformID))
    91  		return api.NewAuthResponseWithMsg(commonstore.StoreCode2APICode(err), err.Error())
    92  	}
    93  
    94  	log.Info("create group", zap.String("name", req.Name.GetValue()), utils.ZapRequestID(requestID),
    95  		utils.ZapPlatformID(platformID))
    96  	svr.RecordHistory(userGroupRecordEntry(ctx, req, data.UserGroup, model.OCreate))
    97  
    98  	req.Id = utils.NewStringValue(data.ID)
    99  
   100  	return api.NewGroupResponse(apimodel.Code_ExecuteSuccess, req)
   101  }
   102  
   103  // UpdateGroups 批量修改用户组
   104  func (svr *Server) UpdateGroups(
   105  	ctx context.Context, groups []*apisecurity.ModifyUserGroup) *apiservice.BatchWriteResponse {
   106  	resp := api.NewAuthBatchWriteResponse(apimodel.Code_ExecuteSuccess)
   107  	for index := range groups {
   108  		req := groups[index]
   109  		ret := svr.UpdateGroup(ctx, req)
   110  		api.Collect(resp, ret)
   111  	}
   112  
   113  	return resp
   114  }
   115  
   116  // UpdateGroup 更新用户组
   117  func (svr *Server) UpdateGroup(ctx context.Context, req *apisecurity.ModifyUserGroup) *apiservice.Response {
   118  	var (
   119  		requestID  = utils.ParseRequestID(ctx)
   120  		platformID = utils.ParsePlatformID(ctx)
   121  	)
   122  
   123  	if checkErrResp := svr.checkUpdateGroup(ctx, req); checkErrResp != nil {
   124  		return checkErrResp
   125  	}
   126  
   127  	data, errResp := svr.getGroupFromDB(req.Id.GetValue())
   128  	if errResp != nil {
   129  		return errResp
   130  	}
   131  
   132  	modifyReq, needUpdate := updateGroupAttribute(ctx, data.UserGroup, req)
   133  	if !needUpdate {
   134  		log.Info("update group data no change, no need update",
   135  			utils.ZapRequestID(requestID), utils.ZapPlatformID(platformID), zap.String("group", req.String()))
   136  		return api.NewModifyGroupResponse(apimodel.Code_NoNeedUpdate, req)
   137  	}
   138  
   139  	if err := svr.storage.UpdateGroup(modifyReq); err != nil {
   140  		log.Error(err.Error(), utils.ZapRequestID(requestID), utils.ZapPlatformID(platformID))
   141  		return api.NewAuthResponseWithMsg(commonstore.StoreCode2APICode(err), err.Error())
   142  	}
   143  
   144  	log.Info("update group", zap.String("name", data.Name), utils.ZapRequestID(requestID),
   145  		utils.ZapPlatformID(platformID))
   146  	svr.RecordHistory(modifyUserGroupRecordEntry(ctx, req, data.UserGroup, model.OUpdateGroup))
   147  
   148  	return api.NewModifyGroupResponse(apimodel.Code_ExecuteSuccess, req)
   149  }
   150  
   151  // DeleteGroups 批量删除用户组
   152  func (svr *Server) DeleteGroups(ctx context.Context, reqs []*apisecurity.UserGroup) *apiservice.BatchWriteResponse {
   153  	resp := api.NewAuthBatchWriteResponse(apimodel.Code_ExecuteSuccess)
   154  	for index := range reqs {
   155  		ret := svr.DeleteGroup(ctx, reqs[index])
   156  		api.Collect(resp, ret)
   157  	}
   158  
   159  	return resp
   160  }
   161  
   162  // DeleteGroup 删除用户组
   163  func (svr *Server) DeleteGroup(ctx context.Context, req *apisecurity.UserGroup) *apiservice.Response {
   164  	var (
   165  		requestID = utils.ParseRequestID(ctx)
   166  		userID    = utils.ParseUserID(ctx)
   167  	)
   168  
   169  	group, err := svr.storage.GetGroup(req.GetId().GetValue())
   170  	if err != nil {
   171  		log.Error("get group from store", utils.ZapRequestID(requestID), zap.Error(err))
   172  		return api.NewGroupResponse(commonstore.StoreCode2APICode(err), req)
   173  	}
   174  	if group == nil {
   175  		return api.NewGroupResponse(apimodel.Code_ExecuteSuccess, req)
   176  	}
   177  
   178  	if authcommon.ParseUserRole(ctx) != model.AdminUserRole {
   179  		if group.Owner != userID {
   180  			return api.NewAuthResponse(apimodel.Code_NotAllowedAccess)
   181  		}
   182  	}
   183  
   184  	if err := svr.storage.DeleteGroup(group); err != nil {
   185  		log.Error("delete group from store", utils.ZapRequestID(requestID), zap.Error(err))
   186  		return api.NewAuthResponseWithMsg(commonstore.StoreCode2APICode(err), err.Error())
   187  	}
   188  
   189  	log.Info("delete group", utils.ZapRequestID(requestID), zap.String("name", req.Name.GetValue()))
   190  	svr.RecordHistory(userGroupRecordEntry(ctx, req, group.UserGroup, model.ODelete))
   191  
   192  	return api.NewGroupResponse(apimodel.Code_ExecuteSuccess, req)
   193  }
   194  
   195  // GetGroups 查看用户组
   196  func (svr *Server) GetGroups(ctx context.Context, query map[string]string) *apiservice.BatchQueryResponse {
   197  	requestID := utils.ParseRequestID(ctx)
   198  
   199  	log.Info("[Auth][Group] origin get groups query params",
   200  		utils.ZapRequestID(requestID), zap.Any("query", query))
   201  
   202  	var (
   203  		offset, limit uint32
   204  		err           error
   205  	)
   206  
   207  	offset, limit, err = utils.ParseOffsetAndLimit(query)
   208  	if err != nil {
   209  		return api.NewAuthBatchQueryResponse(apimodel.Code_InvalidParameter)
   210  	}
   211  
   212  	searchFilters, errResp := parseGroupSearchArgs(ctx, query)
   213  	if errResp != nil {
   214  		return errResp
   215  	}
   216  
   217  	total, groups, err := svr.storage.GetGroups(searchFilters, offset, limit)
   218  	if err != nil {
   219  		log.Errorf("[Auth][Group] get groups req(%+v) store err: %s", query, err.Error())
   220  		return api.NewAuthBatchQueryResponse(commonstore.StoreCode2APICode(err))
   221  	}
   222  
   223  	resp := api.NewAuthBatchQueryResponse(apimodel.Code_ExecuteSuccess)
   224  	resp.Amount = utils.NewUInt32Value(total)
   225  	resp.Size = utils.NewUInt32Value(uint32(len(groups)))
   226  	resp.UserGroups = enhancedGroups2Api(groups, userGroup2Api)
   227  
   228  	svr.fillGroupUserCount(resp.UserGroups)
   229  
   230  	return resp
   231  }
   232  
   233  func parseGroupSearchArgs(
   234  	ctx context.Context, query map[string]string) (map[string]string, *apiservice.BatchQueryResponse) {
   235  	searchFilters := make(map[string]string, len(query))
   236  	for key, value := range query {
   237  		if _, ok := UserLinkGroupAttributes[key]; !ok {
   238  			log.Errorf("[Auth][Group] get groups attribute(%s) it not allowed", key)
   239  			return nil, api.NewAuthBatchQueryResponseWithMsg(apimodel.Code_InvalidParameter, key+" is not allowed")
   240  		}
   241  
   242  		searchFilters[key] = value
   243  	}
   244  
   245  	if authcommon.ParseUserRole(ctx) != model.AdminUserRole {
   246  		// step 1: 设置 owner 信息,只能查看归属主帐户下的用户组
   247  		searchFilters["owner"] = utils.ParseOwnerID(ctx)
   248  		if authcommon.ParseUserRole(ctx) != model.OwnerUserRole {
   249  			// step 2: 非主帐户,只能查看自己所在的用户组
   250  			if _, ok := searchFilters["user_id"]; !ok {
   251  				searchFilters["user_id"] = utils.ParseUserID(ctx)
   252  			}
   253  		}
   254  	}
   255  
   256  	return searchFilters, nil
   257  }
   258  
   259  // GetGroup 查看对应用户组下的用户信息
   260  func (svr *Server) GetGroup(ctx context.Context, req *apisecurity.UserGroup) *apiservice.Response {
   261  	if req.GetId().GetValue() == "" {
   262  		return api.NewAuthResponse(apimodel.Code_InvalidUserGroupID)
   263  	}
   264  
   265  	group, errResp := svr.getGroupFromDB(req.Id.Value)
   266  	if errResp != nil {
   267  		return errResp
   268  	}
   269  
   270  	if authcommon.ParseUserRole(ctx) != model.AdminUserRole {
   271  		userID := utils.ParseUserID(ctx)
   272  		isGroupOwner := group.Owner == userID
   273  		_, find := group.UserIds[userID]
   274  		if !isGroupOwner && !find {
   275  			log.Error("can't see group info", zap.String("user", userID),
   276  				zap.String("group", req.GetId().GetValue()), zap.Bool("group-owner", isGroupOwner),
   277  				zap.Bool("in-group", find))
   278  			return api.NewAuthResponse(apimodel.Code_NotAllowedAccess)
   279  		}
   280  	}
   281  
   282  	return api.NewGroupResponse(apimodel.Code_ExecuteSuccess, svr.userGroupDetail2Api(group))
   283  }
   284  
   285  // GetGroupToken 查看用户组的token
   286  func (svr *Server) GetGroupToken(ctx context.Context, req *apisecurity.UserGroup) *apiservice.Response {
   287  	if req.GetId().GetValue() == "" {
   288  		return api.NewAuthResponse(apimodel.Code_InvalidUserGroupID)
   289  	}
   290  
   291  	groupCache, errResp := svr.getGroupFromCache(req)
   292  	if errResp != nil {
   293  		return errResp
   294  	}
   295  
   296  	if authcommon.ParseUserRole(ctx) != model.AdminUserRole {
   297  		userID := utils.ParseUserID(ctx)
   298  		isGroupOwner := groupCache.Owner == userID
   299  		_, find := groupCache.UserIds[userID]
   300  		if !isGroupOwner && !find {
   301  			log.Error("can't see group token", zap.String("user", userID),
   302  				zap.String("group", req.GetId().GetValue()), zap.Bool("group-owner", isGroupOwner),
   303  				zap.Bool("in-group", find))
   304  			return api.NewAuthResponse(apimodel.Code_NotAllowedAccess)
   305  		}
   306  	}
   307  
   308  	req.AuthToken = utils.NewStringValue(groupCache.Token)
   309  	req.TokenEnable = utils.NewBoolValue(groupCache.TokenEnable)
   310  
   311  	return api.NewGroupResponse(apimodel.Code_ExecuteSuccess, req)
   312  }
   313  
   314  // UpdateGroupToken 调整用户组 token 的使用状态 (禁用|开启)
   315  func (svr *Server) UpdateGroupToken(ctx context.Context, req *apisecurity.UserGroup) *apiservice.Response {
   316  	var (
   317  		requestID      = utils.ParseRequestID(ctx)
   318  		platformID     = utils.ParsePlatformID(ctx)
   319  		group, errResp = svr.getGroupFromDB(req.Id.GetValue())
   320  	)
   321  
   322  	if errResp != nil {
   323  		return errResp
   324  	}
   325  
   326  	if authcommon.ParseUserRole(ctx) != model.AdminUserRole {
   327  		userID := utils.ParseUserID(ctx)
   328  		if group.Owner != userID {
   329  			return api.NewAuthResponse(apimodel.Code_NotAllowedAccess)
   330  		}
   331  	}
   332  
   333  	group.TokenEnable = req.TokenEnable.GetValue()
   334  
   335  	modifyReq := &model.ModifyUserGroup{
   336  		ID:          group.ID,
   337  		Owner:       group.Owner,
   338  		Token:       group.Token,
   339  		TokenEnable: group.TokenEnable,
   340  		Comment:     group.Comment,
   341  	}
   342  
   343  	if err := svr.storage.UpdateGroup(modifyReq); err != nil {
   344  		log.Error(err.Error(), utils.ZapRequestID(requestID), utils.ZapPlatformID(platformID))
   345  		return api.NewAuthResponseWithMsg(commonstore.StoreCode2APICode(err), err.Error())
   346  	}
   347  
   348  	log.Info("update group token", zap.String("id", req.Id.GetValue()),
   349  		zap.Bool("enable", group.TokenEnable), utils.ZapRequestID(requestID), utils.ZapPlatformID(platformID))
   350  	svr.RecordHistory(userGroupRecordEntry(ctx, req, group.UserGroup, model.OUpdateToken))
   351  
   352  	return api.NewGroupResponse(apimodel.Code_ExecuteSuccess, req)
   353  }
   354  
   355  // ResetGroupToken 刷新用户组的token
   356  func (svr *Server) ResetGroupToken(ctx context.Context, req *apisecurity.UserGroup) *apiservice.Response {
   357  	var (
   358  		requestID      = utils.ParseRequestID(ctx)
   359  		platformID     = utils.ParsePlatformID(ctx)
   360  		group, errResp = svr.getGroupFromDB(req.Id.GetValue())
   361  	)
   362  
   363  	if errResp != nil {
   364  		return errResp
   365  	}
   366  
   367  	if !utils.ParseIsOwner(ctx) || (group.Owner != utils.ParseUserID(ctx)) {
   368  		return api.NewAuthResponse(apimodel.Code_NotAllowedAccess)
   369  	}
   370  
   371  	newToken, err := createGroupToken(group.ID)
   372  	if err != nil {
   373  		log.Error("reset group token", utils.ZapRequestID(requestID),
   374  			utils.ZapPlatformID(platformID), zap.Error(err))
   375  		return api.NewAuthResponseWithMsg(apimodel.Code_ExecuteException, err.Error())
   376  	}
   377  
   378  	group.Token = newToken
   379  	modifyReq := &model.ModifyUserGroup{
   380  		ID:          group.ID,
   381  		Owner:       group.Owner,
   382  		Token:       group.Token,
   383  		TokenEnable: group.TokenEnable,
   384  		Comment:     group.Comment,
   385  	}
   386  
   387  	if err := svr.storage.UpdateGroup(modifyReq); err != nil {
   388  		log.Error(err.Error(), utils.ZapRequestID(requestID), utils.ZapPlatformID(platformID))
   389  		return api.NewAuthResponseWithMsg(commonstore.StoreCode2APICode(err), err.Error())
   390  	}
   391  
   392  	log.Info("reset group token", zap.String("group-id", req.Id.GetValue()),
   393  		utils.ZapRequestID(requestID), utils.ZapPlatformID(platformID))
   394  	svr.RecordHistory(userGroupRecordEntry(ctx, req, group.UserGroup, model.OUpdate))
   395  
   396  	req.AuthToken = utils.NewStringValue(newToken)
   397  
   398  	return api.NewGroupResponse(apimodel.Code_ExecuteSuccess, req)
   399  }
   400  
   401  // getGroupFromDB 获取用户组
   402  func (svr *Server) getGroupFromDB(id string) (*model.UserGroupDetail, *apiservice.Response) {
   403  	group, err := svr.storage.GetGroup(id)
   404  	if err != nil {
   405  		log.Error("get group from store", zap.Error(err))
   406  		return nil, api.NewAuthResponseWithMsg(commonstore.StoreCode2APICode(err), err.Error())
   407  	}
   408  	if group == nil {
   409  		return nil, api.NewAuthResponse(apimodel.Code_NotFoundUserGroup)
   410  	}
   411  
   412  	return group, nil
   413  }
   414  
   415  // getGroupFromCache 从缓存中获取用户组信息数据
   416  func (svr *Server) getGroupFromCache(req *apisecurity.UserGroup) (*model.UserGroupDetail, *apiservice.Response) {
   417  	group := svr.cacheMgn.User().GetGroup(req.Id.GetValue())
   418  	if group == nil {
   419  		return nil, api.NewGroupResponse(apimodel.Code_NotFoundUserGroup, req)
   420  	}
   421  
   422  	return group, nil
   423  }
   424  
   425  // preCheckGroupRelation 检查用户-用户组关联关系中,对应的用户信息是否存在,即不能添加一个不存在的用户到用户组
   426  func (svr *Server) preCheckGroupRelation(groupID string, req *apisecurity.UserGroupRelation) (*model.UserGroupDetail,
   427  	*apiservice.Response) {
   428  	group := svr.cacheMgn.User().GetGroup(groupID)
   429  	if group == nil {
   430  		return nil, api.NewAuthResponse(apimodel.Code_NotFoundUserGroup)
   431  	}
   432  
   433  	// 检查该关系中所有的用户是否存在
   434  	uIDs := make([]string, len(req.GetUsers()))
   435  	for i := range req.GetUsers() {
   436  		uIDs[i] = req.GetUsers()[i].GetId().GetValue()
   437  	}
   438  
   439  	uIDs = utils.StringSliceDeDuplication(uIDs)
   440  	for i := range uIDs {
   441  		user := svr.cacheMgn.User().GetUserByID(uIDs[i])
   442  		if user == nil {
   443  			return group, api.NewGroupRelationResponse(apimodel.Code_NotFoundUser, req)
   444  		}
   445  	}
   446  
   447  	return group, nil
   448  }
   449  
   450  // checkCreateGroup 检查创建用户组的请求
   451  func (svr *Server) checkCreateGroup(_ context.Context, req *apisecurity.UserGroup) *apiservice.Response {
   452  	if req == nil {
   453  		return api.NewGroupResponse(apimodel.Code_EmptyRequest, req)
   454  	}
   455  
   456  	users := req.GetRelation().GetUsers()
   457  	for i := range users {
   458  		user := svr.cacheMgn.User().GetUserByID(users[i].GetId().GetValue())
   459  		if user == nil {
   460  			return api.NewGroupRelationResponse(apimodel.Code_NotFoundUser, req.GetRelation())
   461  		}
   462  	}
   463  
   464  	return nil
   465  }
   466  
   467  // checkUpdateGroup 检查用户组的更新请求
   468  func (svr *Server) checkUpdateGroup(ctx context.Context, req *apisecurity.ModifyUserGroup) *apiservice.Response {
   469  	userID := utils.ParseUserID(ctx)
   470  	isOwner := utils.ParseIsOwner(ctx)
   471  
   472  	if req == nil {
   473  		return api.NewModifyGroupResponse(apimodel.Code_EmptyRequest, req)
   474  	}
   475  
   476  	if req.Id == nil || req.Id.GetValue() == "" {
   477  		return api.NewModifyGroupResponse(apimodel.Code_InvalidUserGroupID, req)
   478  	}
   479  
   480  	group, checkErrResp := svr.preCheckGroupRelation(req.GetId().GetValue(), req.GetAddRelations())
   481  	if checkErrResp != nil {
   482  		return checkErrResp
   483  	}
   484  
   485  	// 满足以下情况才可以进行操作
   486  	// 1.管理员
   487  	// 2.自己在这个用户组里面
   488  	// 3.自己是这个用户组的owner角色
   489  	if authcommon.ParseUserRole(ctx) != model.AdminUserRole {
   490  		_, inGroup := group.UserIds[userID]
   491  		if !inGroup && group.Owner != userID {
   492  			return api.NewAuthResponse(apimodel.Code_NotAllowedAccess)
   493  		}
   494  
   495  		// 如果当前用户只是在这个组里面,但不是该用户组的owner,那只能添加用户,不能删除用户
   496  		if inGroup && !isOwner && len(req.GetRemoveRelations().GetUsers()) != 0 {
   497  			return api.NewAuthResponseWithMsg(
   498  				apimodel.Code_NotAllowedAccess, "only main account can remove user from usergroup")
   499  		}
   500  	}
   501  	return nil
   502  }
   503  
   504  func (svr *Server) fillGroupUserCount(groups []*apisecurity.UserGroup) {
   505  	groupCache := svr.cacheMgn.User()
   506  
   507  	for index := range groups {
   508  		group := groups[index]
   509  		cacheVal := groupCache.GetGroup(group.Id.Value)
   510  		if cacheVal == nil {
   511  			group.UserCount = utils.NewUInt32Value(0)
   512  		} else {
   513  			group.UserCount = utils.NewUInt32Value(uint32(len(cacheVal.UserIds)))
   514  		}
   515  	}
   516  }
   517  
   518  // updateGroupAttribute 更新计算用户组更新时的结构体数据,并判断是否需要执行更新操作
   519  func updateGroupAttribute(ctx context.Context, old *model.UserGroup, newUser *apisecurity.ModifyUserGroup) (
   520  	*model.ModifyUserGroup, bool) {
   521  	var (
   522  		needUpdate bool
   523  		ret        = &model.ModifyUserGroup{
   524  			ID:          old.ID,
   525  			Token:       old.Token,
   526  			TokenEnable: old.TokenEnable,
   527  			Comment:     old.Comment,
   528  		}
   529  	)
   530  
   531  	// 只有 owner 可以修改这个属性
   532  	if utils.ParseIsOwner(ctx) {
   533  		if newUser.Comment.GetValue() != "" && old.Comment != newUser.Comment.GetValue() {
   534  			needUpdate = true
   535  			ret.Comment = newUser.Comment.GetValue()
   536  		}
   537  	}
   538  
   539  	// 用户组成员变更计算
   540  	if len(newUser.GetAddRelations().GetUsers()) != 0 {
   541  		needUpdate = true
   542  		ids := make([]string, 0, len(newUser.GetAddRelations().GetUsers()))
   543  		for index := range newUser.GetAddRelations().GetUsers() {
   544  			ids = append(ids, newUser.GetAddRelations().GetUsers()[index].GetId().GetValue())
   545  		}
   546  		ret.AddUserIds = ids
   547  	}
   548  
   549  	if len(newUser.GetRemoveRelations().GetUsers()) != 0 {
   550  		needUpdate = true
   551  		ids := make([]string, 0, len(newUser.GetRemoveRelations().GetUsers()))
   552  		for index := range newUser.GetRemoveRelations().GetUsers() {
   553  			ids = append(ids, newUser.GetRemoveRelations().GetUsers()[index].GetId().GetValue())
   554  		}
   555  		ret.RemoveUserIds = ids
   556  	}
   557  
   558  	return ret, needUpdate
   559  }
   560  
   561  // enhancedGroups2Api 数组专为 []*apisecurity.UserGroup
   562  func enhancedGroups2Api(groups []*model.UserGroup, handler UserGroup2Api) []*apisecurity.UserGroup {
   563  	out := make([]*apisecurity.UserGroup, 0, len(groups))
   564  	for k := range groups {
   565  		out = append(out, handler(groups[k]))
   566  	}
   567  
   568  	return out
   569  }
   570  
   571  // createGroupModel 创建用户组的存储模型
   572  func createGroupModel(req *apisecurity.UserGroup) (group *model.UserGroupDetail, err error) {
   573  	ids := make(map[string]struct{}, len(req.GetRelation().GetUsers()))
   574  	for index := range req.GetRelation().GetUsers() {
   575  		ids[req.GetRelation().GetUsers()[index].GetId().GetValue()] = struct{}{}
   576  	}
   577  
   578  	group = &model.UserGroupDetail{
   579  		UserGroup: &model.UserGroup{
   580  			ID:          utils.NewUUID(),
   581  			Name:        req.GetName().GetValue(),
   582  			Owner:       req.GetOwner().GetValue(),
   583  			TokenEnable: true,
   584  			Valid:       true,
   585  			Comment:     req.GetComment().GetValue(),
   586  			CreateTime:  time.Now(),
   587  			ModifyTime:  time.Now(),
   588  		},
   589  		UserIds: ids,
   590  	}
   591  
   592  	if group.Token, err = createGroupToken(group.ID); err != nil {
   593  		return nil, err
   594  	}
   595  	return group, nil
   596  }
   597  
   598  // model.UserGroup 转为 api.UserGroup
   599  func userGroup2Api(group *model.UserGroup) *apisecurity.UserGroup {
   600  	if group == nil {
   601  		return nil
   602  	}
   603  
   604  	// note: 不包括token,token比较特殊
   605  	out := &apisecurity.UserGroup{
   606  		Id:          utils.NewStringValue(group.ID),
   607  		Name:        utils.NewStringValue(group.Name),
   608  		Owner:       utils.NewStringValue(group.Owner),
   609  		TokenEnable: utils.NewBoolValue(group.TokenEnable),
   610  		Comment:     utils.NewStringValue(group.Comment),
   611  		Ctime:       utils.NewStringValue(commontime.Time2String(group.CreateTime)),
   612  		Mtime:       utils.NewStringValue(commontime.Time2String(group.ModifyTime)),
   613  	}
   614  
   615  	return out
   616  }
   617  
   618  // model.UserGroupDetail 转为 api.UserGroup,并且主动填充 user 的信息数据
   619  func (svr *Server) userGroupDetail2Api(group *model.UserGroupDetail) *apisecurity.UserGroup {
   620  	if group == nil {
   621  		return nil
   622  	}
   623  
   624  	users := make([]*apisecurity.User, 0, len(group.UserIds))
   625  	for id := range group.UserIds {
   626  		user := svr.cacheMgn.User().GetUserByID(id)
   627  		users = append(users, &apisecurity.User{
   628  			Id:          utils.NewStringValue(user.ID),
   629  			Name:        utils.NewStringValue(user.Name),
   630  			Source:      utils.NewStringValue(user.Source),
   631  			Comment:     utils.NewStringValue(user.Comment),
   632  			TokenEnable: utils.NewBoolValue(user.TokenEnable),
   633  			Ctime:       utils.NewStringValue(commontime.Time2String(user.CreateTime)),
   634  			Mtime:       utils.NewStringValue(commontime.Time2String(user.ModifyTime)),
   635  		})
   636  	}
   637  
   638  	// note: 不包括token,token比较特殊
   639  	out := &apisecurity.UserGroup{
   640  		Id:          utils.NewStringValue(group.ID),
   641  		Name:        utils.NewStringValue(group.Name),
   642  		Owner:       utils.NewStringValue(group.Owner),
   643  		TokenEnable: utils.NewBoolValue(group.TokenEnable),
   644  		Comment:     utils.NewStringValue(group.Comment),
   645  		Ctime:       utils.NewStringValue(commontime.Time2String(group.CreateTime)),
   646  		Mtime:       utils.NewStringValue(commontime.Time2String(group.ModifyTime)),
   647  		Relation: &apisecurity.UserGroupRelation{
   648  			Users: users,
   649  		},
   650  		UserCount: utils.NewUInt32Value(uint32(len(users))),
   651  	}
   652  
   653  	return out
   654  }
   655  
   656  // userGroupRecordEntry 生成用户组的记录entry
   657  func userGroupRecordEntry(ctx context.Context, req *apisecurity.UserGroup, md *model.UserGroup,
   658  	operationType model.OperationType) *model.RecordEntry {
   659  
   660  	marshaler := jsonpb.Marshaler{}
   661  	datail, _ := marshaler.MarshalToString(req)
   662  
   663  	entry := &model.RecordEntry{
   664  		ResourceType:  model.RUserGroup,
   665  		ResourceName:  fmt.Sprintf("%s(%s)", md.Name, md.ID),
   666  		OperationType: operationType,
   667  		Operator:      utils.ParseOperator(ctx),
   668  		Detail:        datail,
   669  		HappenTime:    time.Now(),
   670  	}
   671  
   672  	return entry
   673  }
   674  
   675  // 生成修改用户组的记录entry
   676  func modifyUserGroupRecordEntry(ctx context.Context, req *apisecurity.ModifyUserGroup, md *model.UserGroup,
   677  	operationType model.OperationType) *model.RecordEntry {
   678  
   679  	marshaler := jsonpb.Marshaler{}
   680  	detail, _ := marshaler.MarshalToString(req)
   681  
   682  	entry := &model.RecordEntry{
   683  		ResourceType:  model.RUserGroup,
   684  		ResourceName:  fmt.Sprintf("%s(%s)", md.Name, md.ID),
   685  		OperationType: operationType,
   686  		Operator:      utils.ParseOperator(ctx),
   687  		Detail:        detail,
   688  		HappenTime:    time.Now(),
   689  	}
   690  
   691  	return entry
   692  }
   693  
   694  // 生成用户-用户组关联关系的记录entry
   695  func userRelationRecordEntry(ctx context.Context, req *apisecurity.UserGroupRelation, md *model.UserGroup,
   696  	operationType model.OperationType) *model.RecordEntry {
   697  
   698  	marshaler := jsonpb.Marshaler{}
   699  	detail, _ := marshaler.MarshalToString(req)
   700  
   701  	entry := &model.RecordEntry{
   702  		ResourceType:  model.RUserGroupRelation,
   703  		ResourceName:  fmt.Sprintf("%s(%s)", md.Name, md.ID),
   704  		OperationType: operationType,
   705  		Operator:      utils.ParseOperator(ctx),
   706  		Detail:        detail,
   707  		HappenTime:    time.Now(),
   708  	}
   709  
   710  	return entry
   711  }