github.com/polarismesh/polaris@v1.17.8/auth/defaultauth/strategy.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 "strconv" 24 "strings" 25 "time" 26 27 "github.com/gogo/protobuf/jsonpb" 28 "github.com/golang/protobuf/ptypes/wrappers" 29 apimodel "github.com/polarismesh/specification/source/go/api/v1/model" 30 apisecurity "github.com/polarismesh/specification/source/go/api/v1/security" 31 apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage" 32 "go.uber.org/zap" 33 34 api "github.com/polarismesh/polaris/common/api/v1" 35 "github.com/polarismesh/polaris/common/model" 36 authcommon "github.com/polarismesh/polaris/common/model/auth" 37 commonstore "github.com/polarismesh/polaris/common/store" 38 commontime "github.com/polarismesh/polaris/common/time" 39 "github.com/polarismesh/polaris/common/utils" 40 ) 41 42 type ( 43 // StrategyDetail2Api strategy detail to *apisecurity.AuthStrategy func 44 StrategyDetail2Api func(user *model.StrategyDetail) *apisecurity.AuthStrategy 45 ) 46 47 var ( 48 // StrategyFilterAttributes strategy filter attributes 49 StrategyFilterAttributes = map[string]bool{ 50 "id": true, 51 "name": true, 52 "owner": true, 53 "offset": true, 54 "limit": true, 55 "principal_id": true, 56 "principal_type": true, 57 "res_id": true, 58 "res_type": true, 59 "default": true, 60 "show_detail": true, 61 } 62 ) 63 64 // CreateStrategy 创建鉴权策略 65 func (svr *Server) CreateStrategy(ctx context.Context, req *apisecurity.AuthStrategy) *apiservice.Response { 66 requestID := utils.ParseRequestID(ctx) 67 ownerId := utils.ParseOwnerID(ctx) 68 req.Owner = utils.NewStringValue(ownerId) 69 70 if checkErrResp := svr.checkCreateStrategy(req); checkErrResp != nil { 71 return checkErrResp 72 } 73 74 req.Resources = svr.normalizeResource(req.Resources) 75 76 data := svr.createAuthStrategyModel(req) 77 if err := svr.storage.AddStrategy(data); err != nil { 78 log.Error("[Auth][Strategy] create strategy into store", utils.ZapRequestID(requestID), 79 zap.Error(err)) 80 return api.NewAuthResponse(commonstore.StoreCode2APICode(err)) 81 } 82 83 log.Info("[Auth][Strategy] create strategy", utils.ZapRequestID(requestID), 84 zap.String("name", req.Name.GetValue())) 85 svr.RecordHistory(authStrategyRecordEntry(ctx, req, data, model.OCreate)) 86 87 return api.NewAuthStrategyResponse(apimodel.Code_ExecuteSuccess, req) 88 } 89 90 // UpdateStrategies 批量修改鉴权 91 func (svr *Server) UpdateStrategies( 92 ctx context.Context, reqs []*apisecurity.ModifyAuthStrategy) *apiservice.BatchWriteResponse { 93 resp := api.NewAuthBatchWriteResponse(apimodel.Code_ExecuteSuccess) 94 95 for index := range reqs { 96 ret := svr.UpdateStrategy(ctx, reqs[index]) 97 api.Collect(resp, ret) 98 } 99 100 return resp 101 } 102 103 // UpdateStrategy 实现鉴权策略的变更 104 // Case 1. 修改的是默认鉴权策略的话,只能修改资源,不能添加、删除用户 or 用户组 105 // Case 2. 鉴权策略只能被自己的 owner 对应的用户修改 106 // Case 3. 主账户的默认策略不得修改 107 func (svr *Server) UpdateStrategy(ctx context.Context, req *apisecurity.ModifyAuthStrategy) *apiservice.Response { 108 requestID := utils.ParseRequestID(ctx) 109 110 strategy, err := svr.storage.GetStrategyDetail(req.GetId().GetValue()) 111 if err != nil { 112 log.Error("[Auth][Strategy] get strategy from store", utils.ZapRequestID(requestID), 113 zap.Error(err)) 114 return api.NewModifyAuthStrategyResponse(commonstore.StoreCode2APICode(err), req) 115 } 116 if strategy == nil { 117 return api.NewModifyAuthStrategyResponse(apimodel.Code_NotFoundAuthStrategyRule, req) 118 } 119 120 if checkErrResp := svr.checkUpdateStrategy(ctx, req, strategy); checkErrResp != nil { 121 return checkErrResp 122 } 123 124 req.AddResources = svr.normalizeResource(req.AddResources) 125 data, needUpdate := svr.updateAuthStrategyAttribute(ctx, req, strategy) 126 if !needUpdate { 127 return api.NewModifyAuthStrategyResponse(apimodel.Code_NoNeedUpdate, req) 128 } 129 130 if err := svr.storage.UpdateStrategy(data); err != nil { 131 log.Error("[Auth][Strategy] update strategy into store", 132 utils.ZapRequestID(requestID), zap.Error(err)) 133 return api.NewAuthResponseWithMsg(commonstore.StoreCode2APICode(err), err.Error()) 134 } 135 136 log.Info("[Auth][Strategy] update strategy into store", utils.ZapRequestID(requestID), 137 zap.String("name", strategy.Name)) 138 svr.RecordHistory(authModifyStrategyRecordEntry(ctx, req, data, model.OUpdate)) 139 140 return api.NewModifyAuthStrategyResponse(apimodel.Code_ExecuteSuccess, req) 141 } 142 143 // DeleteStrategies 批量删除鉴权策略 144 func (svr *Server) DeleteStrategies( 145 ctx context.Context, reqs []*apisecurity.AuthStrategy) *apiservice.BatchWriteResponse { 146 resp := api.NewAuthBatchWriteResponse(apimodel.Code_ExecuteSuccess) 147 for index := range reqs { 148 ret := svr.DeleteStrategy(ctx, reqs[index]) 149 api.Collect(resp, ret) 150 } 151 152 return resp 153 } 154 155 // DeleteStrategy 删除鉴权策略 156 // Case 1. 只有该策略的 owner 账户可以删除策略 157 // Case 2. 默认策略不能被删除,默认策略只能随着账户的删除而被清理 158 func (svr *Server) DeleteStrategy(ctx context.Context, req *apisecurity.AuthStrategy) *apiservice.Response { 159 requestID := utils.ParseRequestID(ctx) 160 161 strategy, err := svr.storage.GetStrategyDetail(req.GetId().GetValue()) 162 if err != nil { 163 log.Error("[Auth][Strategy] get strategy from store", utils.ZapRequestID(requestID), 164 zap.Error(err)) 165 return api.NewAuthStrategyResponse(commonstore.StoreCode2APICode(err), req) 166 } 167 168 if strategy == nil { 169 return api.NewAuthStrategyResponse(apimodel.Code_ExecuteSuccess, req) 170 } 171 172 if strategy.Default { 173 log.Error("[Auth][Strategy] delete default strategy is denied", utils.ZapRequestID(requestID)) 174 return api.NewAuthStrategyResponseWithMsg(apimodel.Code_BadRequest, "default strategy can't delete", req) 175 } 176 177 if strategy.Owner != utils.ParseUserID(ctx) { 178 return api.NewAuthStrategyResponse(apimodel.Code_NotAllowedAccess, req) 179 } 180 181 if err := svr.storage.DeleteStrategy(req.GetId().GetValue()); err != nil { 182 log.Error("[Auth][Strategy] delete strategy from store", 183 utils.ZapRequestID(requestID), zap.Error(err)) 184 return api.NewAuthResponse(commonstore.StoreCode2APICode(err)) 185 } 186 187 log.Info("[Auth][Strategy] delete strategy from store", utils.ZapRequestID(requestID), 188 zap.String("name", req.Name.GetValue())) 189 svr.RecordHistory(authStrategyRecordEntry(ctx, req, strategy, model.ODelete)) 190 191 return api.NewAuthStrategyResponse(apimodel.Code_ExecuteSuccess, req) 192 } 193 194 // GetStrategies 查询鉴权策略列表 195 // Case 1. 如果是以资源视角来查询鉴权策略,那么就会忽略自动根据账户类型进行数据查看的限制 196 // 197 // eg. 比如当前子账户A想要查看资源R的相关的策略,那么不在会自动注入 principal_id 以及 principal_type 的查询条件 198 // 199 // Case 2. 如果是以用户视角来查询鉴权策略,如果没有带上 principal_id,那么就会根据账户类型自动注入 principal_id 以 200 // 201 // 及 principal_type 的查询条件,从而限制该账户的数据查看 202 // eg. 203 // a. 如果当前是超级管理账户,则按照传入的 query 进行查询即可 204 // b. 如果当前是主账户,则自动注入 owner 字段,即只能查看策略的 owner 是自己的策略 205 // c. 如果当前是子账户,则自动注入 principal_id 以及 principal_type 字段,即稚嫩查询与自己有关的策略 206 func (svr *Server) GetStrategies(ctx context.Context, query map[string]string) *apiservice.BatchQueryResponse { 207 requestID := utils.ParseRequestID(ctx) 208 platformID := utils.ParsePlatformID(ctx) 209 210 log.Debug("[Auth][Strategy] origin get strategies query params", utils.ZapRequestID(requestID), 211 utils.ZapPlatformID(platformID), zap.Any("query", query)) 212 213 showDetail := query["show_detail"] 214 215 searchFilters := make(map[string]string, len(query)) 216 for key, value := range query { 217 if _, ok := StrategyFilterAttributes[key]; !ok { 218 log.Errorf("[Auth][Strategy] get strategies attribute(%s) it not allowed", key) 219 return api.NewAuthBatchQueryResponseWithMsg(apimodel.Code_InvalidParameter, key+" is not allowed") 220 } 221 searchFilters[key] = value 222 } 223 224 searchFilters = parseStrategySearchArgs(ctx, searchFilters) 225 offset, limit, err := utils.ParseOffsetAndLimit(searchFilters) 226 227 if err != nil { 228 return api.NewAuthBatchQueryResponse(apimodel.Code_InvalidParameter) 229 } 230 231 total, strategies, err := svr.storage.GetStrategies(searchFilters, offset, limit) 232 if err != nil { 233 log.Error("[Auth][Strategy] get strategies from store", zap.Any("query", searchFilters), 234 zap.Error(err)) 235 return api.NewAuthBatchQueryResponse(commonstore.StoreCode2APICode(err)) 236 } 237 238 resp := api.NewAuthBatchQueryResponse(apimodel.Code_ExecuteSuccess) 239 resp.Amount = utils.NewUInt32Value(total) 240 resp.Size = utils.NewUInt32Value(uint32(len(strategies))) 241 242 if strings.Compare(showDetail, "true") == 0 { 243 log.Info("[Auth][Strategy] fill strategy detail", utils.ZapRequestID(requestID)) 244 resp.AuthStrategies = enhancedAuthStrategy2Api(strategies, svr.authStrategyFull2Api) 245 } else { 246 resp.AuthStrategies = enhancedAuthStrategy2Api(strategies, svr.authStrategy2Api) 247 } 248 249 return resp 250 } 251 252 var resTypeFilter = map[string]string{ 253 "namespace": "0", 254 "service": "1", 255 "config_group": "2", 256 } 257 258 var principalTypeFilter = map[string]string{ 259 "user": "1", 260 "group": "2", 261 "groups": "2", 262 } 263 264 // parseStrategySearchArgs 处理鉴权策略的搜索参数 265 func parseStrategySearchArgs(ctx context.Context, searchFilters map[string]string) map[string]string { 266 if val, ok := searchFilters["res_type"]; ok { 267 if v, exist := resTypeFilter[val]; exist { 268 searchFilters["res_type"] = v 269 } else { 270 searchFilters["res_type"] = "0" 271 } 272 } 273 274 if val, ok := searchFilters["principal_type"]; ok { 275 if v, exist := principalTypeFilter[val]; exist { 276 searchFilters["principal_type"] = v 277 } else { 278 searchFilters["principal_type"] = "1" 279 } 280 } 281 282 if authcommon.ParseUserRole(ctx) != model.AdminUserRole { 283 // 如果当前账户不是 admin 角色,既不是走资源视角查看,也不是指定principal查看,那么只能查询当前操作用户被关联到的鉴权策略, 284 if _, ok := searchFilters["res_id"]; !ok { 285 // 设置 owner 参数,只能查看对应 owner 下的策略 286 searchFilters["owner"] = utils.ParseOwnerID(ctx) 287 if _, ok := searchFilters["principal_id"]; !ok { 288 // 如果当前不是 owner 角色,那么只能查询与自己有关的策略 289 if !utils.ParseIsOwner(ctx) { 290 searchFilters["principal_id"] = utils.ParseUserID(ctx) 291 searchFilters["principal_type"] = strconv.Itoa(int(model.PrincipalUser)) 292 } 293 } 294 } 295 } 296 297 return searchFilters 298 } 299 300 // GetStrategy 根据策略ID获取详细的鉴权策略 301 // Case 1 如果当前操作者是该策略 principal 中的一员,则可以查看 302 // Case 2 如果当前操作者是该策略的 owner,则可以查看 303 // Case 3 如果当前操作者是admin角色,直接查看 304 func (svr *Server) GetStrategy(ctx context.Context, req *apisecurity.AuthStrategy) *apiservice.Response { 305 requestID := utils.ParseRequestID(ctx) 306 userId := utils.ParseUserID(ctx) 307 isOwner := utils.ParseIsOwner(ctx) 308 309 if req.GetId().GetValue() == "" { 310 return api.NewAuthResponse(apimodel.Code_EmptyQueryParameter) 311 } 312 313 ret, err := svr.storage.GetStrategyDetail(req.GetId().GetValue()) 314 if err != nil { 315 log.Error("[Auth][Strategy] get strategt from store", 316 utils.ZapRequestID(requestID), zap.Error(err)) 317 return api.NewAuthResponse(commonstore.StoreCode2APICode(err)) 318 } 319 if ret == nil { 320 return api.NewAuthStrategyResponse(apimodel.Code_NotFoundAuthStrategyRule, req) 321 } 322 323 var canView bool 324 if isOwner { 325 // 是否是本鉴权策略的 owner 账户, 或者是否是超级管理员, 是的话则快速跳过下面的检查 326 canView = (ret.Owner == userId) || authcommon.ParseUserRole(ctx) == model.AdminUserRole 327 } 328 329 // 判断是否在该策略所属的成员列表中,如果自己在某个用户组,而该用户组又在这个策略的成员中,则也是可以查看的 330 if !canView { 331 for index := range ret.Principals { 332 principal := ret.Principals[index] 333 if principal.PrincipalRole == model.PrincipalUser && principal.PrincipalID == userId { 334 canView = true 335 break 336 } 337 if principal.PrincipalRole == model.PrincipalGroup { 338 if svr.cacheMgn.User().IsUserInGroup(userId, principal.PrincipalID) { 339 canView = true 340 break 341 } 342 } 343 } 344 } 345 346 if !canView { 347 log.Error("[Auth][Strategy] get strategy detail denied", 348 utils.ZapRequestID(requestID), 349 zap.String("user", userId), 350 zap.String("strategy", req.Id.Value), 351 zap.Bool("is-owner", isOwner), 352 ) 353 return api.NewAuthStrategyResponse(apimodel.Code_NotAllowedAccess, req) 354 } 355 356 return api.NewAuthStrategyResponse(apimodel.Code_ExecuteSuccess, svr.authStrategyFull2Api(ret)) 357 } 358 359 // GetPrincipalResources 获取某个principal可以获取到的所有资源ID数据信息 360 func (svr *Server) GetPrincipalResources(ctx context.Context, query map[string]string) *apiservice.Response { 361 requestID := utils.ParseRequestID(ctx) 362 if len(query) == 0 { 363 return api.NewAuthResponse(apimodel.Code_EmptyRequest) 364 } 365 366 principalId := query["principal_id"] 367 if principalId == "" { 368 return api.NewAuthResponse(apimodel.Code_EmptyQueryParameter) 369 } 370 371 var principalType string 372 if v, exist := principalTypeFilter[query["principal_type"]]; exist { 373 principalType = v 374 } else { 375 principalType = "1" 376 } 377 378 principalRole, _ := strconv.ParseInt(principalType, 10, 64) 379 if err := model.CheckPrincipalType(int(principalRole)); err != nil { 380 return api.NewAuthResponse(apimodel.Code_InvalidPrincipalType) 381 } 382 383 var ( 384 resources = make([]model.StrategyResource, 0, 20) 385 err error 386 ) 387 388 // 找这个用户所关联的用户组 389 if model.PrincipalType(principalRole) == model.PrincipalUser { 390 groupIds := svr.cacheMgn.User().GetUserLinkGroupIds(principalId) 391 for i := range groupIds { 392 res, err := svr.storage.GetStrategyResources(groupIds[i], model.PrincipalGroup) 393 if err != nil { 394 log.Error("[Auth][Strategy] get principal link resource", utils.ZapRequestID(requestID), 395 zap.String("principal-id", principalId), zap.Any("principal-role", principalRole), zap.Error(err)) 396 return api.NewAuthResponse(commonstore.StoreCode2APICode(err)) 397 } 398 resources = append(resources, res...) 399 } 400 } 401 402 pResources, err := svr.storage.GetStrategyResources(principalId, model.PrincipalType(principalRole)) 403 if err != nil { 404 log.Error("[Auth][Strategy] get principal link resource", utils.ZapRequestID(requestID), 405 zap.String("principal-id", principalId), zap.Any("principal-role", principalRole), zap.Error(err)) 406 return api.NewAuthResponse(commonstore.StoreCode2APICode(err)) 407 } 408 409 resources = append(resources, pResources...) 410 tmp := &apisecurity.AuthStrategy{ 411 Resources: &apisecurity.StrategyResources{ 412 Namespaces: make([]*apisecurity.StrategyResourceEntry, 0), 413 Services: make([]*apisecurity.StrategyResourceEntry, 0), 414 ConfigGroups: make([]*apisecurity.StrategyResourceEntry, 0), 415 }, 416 } 417 418 svr.fillResourceInfo(tmp, &model.StrategyDetail{ 419 Resources: resourceDeduplication(resources), 420 }) 421 422 return api.NewStrategyResourcesResponse(apimodel.Code_ExecuteSuccess, tmp.Resources) 423 } 424 425 // enhancedAuthStrategy2Api 426 func enhancedAuthStrategy2Api(s []*model.StrategyDetail, fn StrategyDetail2Api) []*apisecurity.AuthStrategy { 427 out := make([]*apisecurity.AuthStrategy, 0, len(s)) 428 for k := range s { 429 out = append(out, fn(s[k])) 430 } 431 return out 432 } 433 434 // authStrategy2Api 435 func (svr *Server) authStrategy2Api(s *model.StrategyDetail) *apisecurity.AuthStrategy { 436 if s == nil { 437 return nil 438 } 439 440 // note: 不包括token,token比较特殊 441 out := &apisecurity.AuthStrategy{ 442 Id: utils.NewStringValue(s.ID), 443 Name: utils.NewStringValue(s.Name), 444 Owner: utils.NewStringValue(s.Owner), 445 Comment: utils.NewStringValue(s.Comment), 446 Ctime: utils.NewStringValue(commontime.Time2String(s.CreateTime)), 447 Mtime: utils.NewStringValue(commontime.Time2String(s.ModifyTime)), 448 Action: apisecurity.AuthAction(apisecurity.AuthAction_value[s.Action]), 449 DefaultStrategy: utils.NewBoolValue(s.Default), 450 } 451 452 return out 453 } 454 455 // authStrategyFull2Api 456 func (svr *Server) authStrategyFull2Api(data *model.StrategyDetail) *apisecurity.AuthStrategy { 457 if data == nil { 458 return nil 459 } 460 461 users := make([]*wrappers.StringValue, 0, len(data.Principals)) 462 groups := make([]*wrappers.StringValue, 0, len(data.Principals)) 463 for index := range data.Principals { 464 principal := data.Principals[index] 465 if principal.PrincipalRole == model.PrincipalUser { 466 users = append(users, utils.NewStringValue(principal.PrincipalID)) 467 } else { 468 groups = append(groups, utils.NewStringValue(principal.PrincipalID)) 469 } 470 } 471 472 // note: 不包括token,token比较特殊 473 out := &apisecurity.AuthStrategy{ 474 Id: utils.NewStringValue(data.ID), 475 Name: utils.NewStringValue(data.Name), 476 Owner: utils.NewStringValue(data.Owner), 477 Comment: utils.NewStringValue(data.Comment), 478 Ctime: utils.NewStringValue(commontime.Time2String(data.CreateTime)), 479 Mtime: utils.NewStringValue(commontime.Time2String(data.ModifyTime)), 480 Action: apisecurity.AuthAction(apisecurity.AuthAction_value[data.Action]), 481 DefaultStrategy: utils.NewBoolValue(data.Default), 482 } 483 484 svr.fillPrincipalInfo(out, data) 485 svr.fillResourceInfo(out, data) 486 return out 487 } 488 489 // createAuthStrategyModel 创建鉴权策略的存储模型 490 func (svr *Server) createAuthStrategyModel(strategy *apisecurity.AuthStrategy) *model.StrategyDetail { 491 ret := &model.StrategyDetail{ 492 ID: utils.NewUUID(), 493 Name: strategy.Name.GetValue(), 494 Action: apisecurity.AuthAction_READ_WRITE.String(), 495 Comment: strategy.Comment.GetValue(), 496 Default: false, 497 Owner: strategy.Owner.GetValue(), 498 Valid: true, 499 Revision: utils.NewUUID(), 500 CreateTime: time.Now(), 501 ModifyTime: time.Now(), 502 } 503 504 // 收集涉及的资源信息 505 resEntry := make([]model.StrategyResource, 0, 20) 506 resEntry = append(resEntry, svr.collectResEntry(ret.ID, apisecurity.ResourceType_Namespaces, 507 strategy.GetResources().GetNamespaces(), false)...) 508 resEntry = append(resEntry, svr.collectResEntry(ret.ID, apisecurity.ResourceType_Services, 509 strategy.GetResources().GetServices(), false)...) 510 resEntry = append(resEntry, svr.collectResEntry(ret.ID, apisecurity.ResourceType_ConfigGroups, 511 strategy.GetResources().GetConfigGroups(), false)...) 512 513 // 收集涉及的 principal 信息 514 principals := make([]model.Principal, 0, 20) 515 principals = append(principals, collectPrincipalEntry(ret.ID, model.PrincipalUser, 516 strategy.GetPrincipals().GetUsers())...) 517 principals = append(principals, collectPrincipalEntry(ret.ID, model.PrincipalGroup, 518 strategy.GetPrincipals().GetGroups())...) 519 520 ret.Resources = resEntry 521 ret.Principals = principals 522 523 return ret 524 } 525 526 // updateAuthStrategyAttribute 更新计算鉴权策略的属性 527 func (svr *Server) updateAuthStrategyAttribute(ctx context.Context, strategy *apisecurity.ModifyAuthStrategy, 528 saved *model.StrategyDetail) (*model.ModifyStrategyDetail, bool) { 529 var needUpdate bool 530 ret := &model.ModifyStrategyDetail{ 531 ID: strategy.Id.GetValue(), 532 Name: saved.Name, 533 Action: saved.Action, 534 Comment: saved.Comment, 535 ModifyTime: time.Now(), 536 } 537 538 // 只有 owner 可以修改的属性 539 if utils.ParseIsOwner(ctx) { 540 if strategy.GetComment() != nil && strategy.GetComment().GetValue() != saved.Comment { 541 needUpdate = true 542 ret.Comment = strategy.GetComment().GetValue() 543 } 544 545 if strategy.GetName().GetValue() != "" && strategy.GetName().GetValue() != saved.Name { 546 needUpdate = true 547 ret.Name = strategy.GetName().GetValue() 548 } 549 } 550 551 if svr.computeResourceChange(ret, strategy) { 552 needUpdate = true 553 } 554 if computePrincipalChange(ret, strategy) { 555 needUpdate = true 556 } 557 558 return ret, needUpdate 559 } 560 561 // computeResourceChange 计算资源的变化情况,判断是否涉及变更 562 func (svr *Server) computeResourceChange( 563 modify *model.ModifyStrategyDetail, strategy *apisecurity.ModifyAuthStrategy) bool { 564 var needUpdate bool 565 addResEntry := make([]model.StrategyResource, 0) 566 addResEntry = append(addResEntry, svr.collectResEntry(modify.ID, apisecurity.ResourceType_Namespaces, 567 strategy.GetAddResources().GetNamespaces(), false)...) 568 addResEntry = append(addResEntry, svr.collectResEntry(modify.ID, apisecurity.ResourceType_Services, 569 strategy.GetAddResources().GetServices(), false)...) 570 addResEntry = append(addResEntry, svr.collectResEntry(modify.ID, apisecurity.ResourceType_ConfigGroups, 571 strategy.GetAddResources().GetConfigGroups(), false)...) 572 573 if len(addResEntry) != 0 { 574 needUpdate = true 575 modify.AddResources = addResEntry 576 } 577 578 removeResEntry := make([]model.StrategyResource, 0) 579 removeResEntry = append(removeResEntry, svr.collectResEntry(modify.ID, apisecurity.ResourceType_Namespaces, 580 strategy.GetRemoveResources().GetNamespaces(), true)...) 581 removeResEntry = append(removeResEntry, svr.collectResEntry(modify.ID, apisecurity.ResourceType_Services, 582 strategy.GetRemoveResources().GetServices(), true)...) 583 removeResEntry = append(removeResEntry, svr.collectResEntry(modify.ID, apisecurity.ResourceType_ConfigGroups, 584 strategy.GetRemoveResources().GetConfigGroups(), true)...) 585 586 if len(removeResEntry) != 0 { 587 needUpdate = true 588 modify.RemoveResources = removeResEntry 589 } 590 591 return needUpdate 592 } 593 594 // computePrincipalChange 计算 principal 的变化情况,判断是否涉及变更 595 func computePrincipalChange(modify *model.ModifyStrategyDetail, strategy *apisecurity.ModifyAuthStrategy) bool { 596 var needUpdate bool 597 addPrincipals := make([]model.Principal, 0) 598 addPrincipals = append(addPrincipals, collectPrincipalEntry(modify.ID, model.PrincipalUser, 599 strategy.GetAddPrincipals().GetUsers())...) 600 addPrincipals = append(addPrincipals, collectPrincipalEntry(modify.ID, model.PrincipalGroup, 601 strategy.GetAddPrincipals().GetGroups())...) 602 603 if len(addPrincipals) != 0 { 604 needUpdate = true 605 modify.AddPrincipals = addPrincipals 606 } 607 608 removePrincipals := make([]model.Principal, 0) 609 removePrincipals = append(removePrincipals, collectPrincipalEntry(modify.ID, model.PrincipalUser, 610 strategy.GetRemovePrincipals().GetUsers())...) 611 removePrincipals = append(removePrincipals, collectPrincipalEntry(modify.ID, model.PrincipalGroup, 612 strategy.GetRemovePrincipals().GetGroups())...) 613 614 if len(removePrincipals) != 0 { 615 needUpdate = true 616 modify.RemovePrincipals = removePrincipals 617 } 618 619 return needUpdate 620 } 621 622 // collectResEntry 将资源ID转换为对应的 []model.StrategyResource 数组 623 func (svr *Server) collectResEntry(ruleId string, resType apisecurity.ResourceType, 624 res []*apisecurity.StrategyResourceEntry, delete bool) []model.StrategyResource { 625 resEntries := make([]model.StrategyResource, 0, len(res)+1) 626 if len(res) == 0 { 627 return resEntries 628 } 629 630 for index := range res { 631 // 如果是添加的动作,那么需要进行归一化处理 632 if !delete { 633 // 归一化处理 634 if res[index].GetId().GetValue() == "*" || res[index].GetName().GetValue() == "*" { 635 return []model.StrategyResource{ 636 { 637 StrategyID: ruleId, 638 ResType: int32(resType), 639 ResID: "*", 640 }, 641 } 642 } 643 } 644 645 entry := model.StrategyResource{ 646 StrategyID: ruleId, 647 ResType: int32(resType), 648 ResID: res[index].GetId().GetValue(), 649 } 650 651 resEntries = append(resEntries, entry) 652 } 653 654 return resEntries 655 } 656 657 // collectPrincipalEntry 将 Principal 转换为对应的 []model.Principal 数组 658 func collectPrincipalEntry(ruleID string, uType model.PrincipalType, res []*apisecurity.Principal) []model.Principal { 659 principals := make([]model.Principal, 0, len(res)+1) 660 if len(res) == 0 { 661 return principals 662 } 663 664 for index := range res { 665 principals = append(principals, model.Principal{ 666 StrategyID: ruleID, 667 PrincipalID: res[index].GetId().GetValue(), 668 PrincipalRole: uType, 669 }) 670 } 671 672 return principals 673 } 674 675 // checkCreateStrategy 检查创建鉴权策略的请求 676 func (svr *Server) checkCreateStrategy(req *apisecurity.AuthStrategy) *apiservice.Response { 677 // 检查名称信息 678 if err := checkName(req.GetName()); err != nil { 679 return api.NewAuthStrategyResponse(apimodel.Code_InvalidUserName, req) 680 } 681 682 // 检查 owner 信息 683 if err := checkOwner(req.GetOwner()); err != nil { 684 return api.NewAuthStrategyResponse(apimodel.Code_InvalidAuthStrategyOwners, req) 685 } 686 687 // 检查用户是否存在 688 if err := svr.checkUserExist(convertPrincipalsToUsers(req.GetPrincipals())); err != nil { 689 return api.NewAuthStrategyResponse(apimodel.Code_NotFoundUser, req) 690 } 691 692 // 检查用户组是否存在 693 if err := svr.checkGroupExist(convertPrincipalsToGroups(req.GetPrincipals())); err != nil { 694 return api.NewAuthStrategyResponse(apimodel.Code_NotFoundUserGroup, req) 695 } 696 697 // 检查资源是否存在 698 if errResp := svr.checkResourceExist(req.GetResources()); errResp != nil { 699 return errResp 700 } 701 702 return nil 703 } 704 705 // checkUpdateStrategy 检查更新鉴权策略的请求 706 // Case 1. 修改的是默认鉴权策略的话,只能修改资源,不能添加用户 or 用户组 707 // Case 2. 鉴权策略只能被自己的 owner 对应的用户修改 708 func (svr *Server) checkUpdateStrategy(ctx context.Context, req *apisecurity.ModifyAuthStrategy, 709 saved *model.StrategyDetail) *apiservice.Response { 710 userId := utils.ParseUserID(ctx) 711 if authcommon.ParseUserRole(ctx) != model.AdminUserRole { 712 if !utils.ParseIsOwner(ctx) || userId != saved.Owner { 713 log.Error("[Auth][Strategy] modify strategy denied, current user not owner", 714 utils.ZapRequestID(utils.ParseRequestID(ctx)), 715 zap.String("user", userId), 716 zap.String("owner", saved.Owner), 717 zap.String("strategy", saved.ID)) 718 return api.NewModifyAuthStrategyResponse(apimodel.Code_NotAllowedAccess, req) 719 } 720 } 721 722 if saved.Default { 723 if len(req.AddPrincipals.Users) != 0 || 724 len(req.AddPrincipals.Groups) != 0 || 725 len(req.RemovePrincipals.Groups) != 0 || 726 len(req.RemovePrincipals.Users) != 0 { 727 return api.NewModifyAuthStrategyResponse(apimodel.Code_NotAllowModifyDefaultStrategyPrincipal, req) 728 } 729 730 // 主账户的默认策略禁止编辑 731 if len(saved.Principals) == 1 && saved.Principals[0].PrincipalRole == model.PrincipalUser { 732 if saved.Principals[0].PrincipalID == utils.ParseOwnerID(ctx) { 733 return api.NewAuthResponse(apimodel.Code_NotAllowModifyOwnerDefaultStrategy) 734 } 735 } 736 } 737 738 // 检查用户是否存在 739 if err := svr.checkUserExist(convertPrincipalsToUsers(req.GetAddPrincipals())); err != nil { 740 return api.NewModifyAuthStrategyResponse(apimodel.Code_NotFoundUser, req) 741 } 742 743 // 检查用户组是否存 744 if err := svr.checkGroupExist(convertPrincipalsToGroups(req.GetAddPrincipals())); err != nil { 745 return api.NewModifyAuthStrategyResponse(apimodel.Code_NotFoundUserGroup, req) 746 } 747 748 // 检查资源是否存在 749 if errResp := svr.checkResourceExist(req.GetAddResources()); errResp != nil { 750 return errResp 751 } 752 753 return nil 754 } 755 756 // authStrategyRecordEntry 转换为鉴权策略的记录结构体 757 func authStrategyRecordEntry(ctx context.Context, req *apisecurity.AuthStrategy, md *model.StrategyDetail, 758 operationType model.OperationType) *model.RecordEntry { 759 760 marshaler := jsonpb.Marshaler{} 761 detail, _ := marshaler.MarshalToString(req) 762 763 entry := &model.RecordEntry{ 764 ResourceType: model.RAuthStrategy, 765 ResourceName: fmt.Sprintf("%s(%s)", md.Name, md.ID), 766 OperationType: operationType, 767 Operator: utils.ParseOperator(ctx), 768 Detail: detail, 769 HappenTime: time.Now(), 770 } 771 772 return entry 773 } 774 775 // authModifyStrategyRecordEntry 776 func authModifyStrategyRecordEntry( 777 ctx context.Context, req *apisecurity.ModifyAuthStrategy, md *model.ModifyStrategyDetail, 778 operationType model.OperationType) *model.RecordEntry { 779 780 marshaler := jsonpb.Marshaler{} 781 detail, _ := marshaler.MarshalToString(req) 782 783 entry := &model.RecordEntry{ 784 ResourceType: model.RAuthStrategy, 785 ResourceName: fmt.Sprintf("%s(%s)", md.Name, md.ID), 786 OperationType: operationType, 787 Operator: utils.ParseOperator(ctx), 788 Detail: detail, 789 HappenTime: time.Now(), 790 } 791 792 return entry 793 } 794 795 func convertPrincipalsToUsers(principals *apisecurity.Principals) []*apisecurity.User { 796 if principals == nil { 797 return make([]*apisecurity.User, 0) 798 } 799 800 users := make([]*apisecurity.User, 0, len(principals.Users)) 801 for k := range principals.GetUsers() { 802 user := principals.GetUsers()[k] 803 users = append(users, &apisecurity.User{ 804 Id: user.Id, 805 }) 806 } 807 808 return users 809 } 810 811 func convertPrincipalsToGroups(principals *apisecurity.Principals) []*apisecurity.UserGroup { 812 if principals == nil { 813 return make([]*apisecurity.UserGroup, 0) 814 } 815 816 groups := make([]*apisecurity.UserGroup, 0, len(principals.Groups)) 817 for k := range principals.GetGroups() { 818 group := principals.GetGroups()[k] 819 groups = append(groups, &apisecurity.UserGroup{ 820 Id: group.Id, 821 }) 822 } 823 824 return groups 825 } 826 827 // checkUserExist 检查用户是否存在 828 func (svr *Server) checkUserExist(users []*apisecurity.User) error { 829 if len(users) == 0 { 830 return nil 831 } 832 833 userCache := svr.cacheMgn.User() 834 835 for index := range users { 836 if val := userCache.GetUserByID(users[index].GetId().GetValue()); val == nil { 837 return model.ErrorNoUser 838 } 839 } 840 841 return nil 842 } 843 844 // checkUserGroupExist 检查用户组是否存在 845 func (svr *Server) checkGroupExist(groups []*apisecurity.UserGroup) error { 846 if len(groups) == 0 { 847 return nil 848 } 849 userCache := svr.cacheMgn.User() 850 851 for index := range groups { 852 if val := userCache.GetGroup(groups[index].GetId().GetValue()); val == nil { 853 return model.ErrorNoUserGroup 854 } 855 } 856 857 return nil 858 } 859 860 // checkResourceExist 检查资源是否存在 861 func (svr *Server) checkResourceExist(resources *apisecurity.StrategyResources) *apiservice.Response { 862 namespaces := resources.GetNamespaces() 863 864 nsCache := svr.cacheMgn.Namespace() 865 for index := range namespaces { 866 val := namespaces[index] 867 if val.GetId().GetValue() == "*" { 868 break 869 } 870 ns := nsCache.GetNamespace(val.GetId().GetValue()) 871 if ns == nil { 872 return api.NewAuthResponse(apimodel.Code_NotFoundNamespace) 873 } 874 } 875 876 services := resources.GetServices() 877 svcCache := svr.cacheMgn.Service() 878 for index := range services { 879 val := services[index] 880 if val.GetId().GetValue() == "*" { 881 break 882 } 883 svc := svcCache.GetServiceByID(val.GetId().GetValue()) 884 if svc == nil { 885 return api.NewAuthResponse(apimodel.Code_NotFoundService) 886 } 887 } 888 889 return nil 890 } 891 892 // normalizeResource 对于资源进行归一化处理 893 // 894 // 如果出现 * 的话,则该资源访问策略就是 * 895 func (svr *Server) normalizeResource(resources *apisecurity.StrategyResources) *apisecurity.StrategyResources { 896 namespaces := resources.GetNamespaces() 897 for index := range namespaces { 898 val := namespaces[index] 899 if val.GetId().GetValue() == "*" { 900 resources.Namespaces = []*apisecurity.StrategyResourceEntry{{ 901 Id: utils.NewStringValue("*"), 902 }} 903 break 904 } 905 } 906 907 services := resources.GetServices() 908 for index := range services { 909 val := services[index] 910 if val.GetId().GetValue() == "*" { 911 resources.Services = []*apisecurity.StrategyResourceEntry{{ 912 Id: utils.NewStringValue("*"), 913 }} 914 break 915 } 916 } 917 918 return resources 919 } 920 921 // fillPrincipalInfo 填充 principal 摘要信息 922 func (svr *Server) fillPrincipalInfo(resp *apisecurity.AuthStrategy, data *model.StrategyDetail) { 923 users := make([]*apisecurity.Principal, 0, len(data.Principals)) 924 groups := make([]*apisecurity.Principal, 0, len(data.Principals)) 925 for index := range data.Principals { 926 principal := data.Principals[index] 927 if principal.PrincipalRole == model.PrincipalUser { 928 user := svr.cacheMgn.User().GetUserByID(principal.PrincipalID) 929 if user == nil { 930 continue 931 } 932 users = append(users, &apisecurity.Principal{ 933 Id: utils.NewStringValue(user.ID), 934 Name: utils.NewStringValue(user.Name), 935 }) 936 } else { 937 group := svr.cacheMgn.User().GetGroup(principal.PrincipalID) 938 if group == nil { 939 continue 940 } 941 groups = append(groups, &apisecurity.Principal{ 942 Id: utils.NewStringValue(group.ID), 943 Name: utils.NewStringValue(group.Name), 944 }) 945 } 946 } 947 948 resp.Principals = &apisecurity.Principals{ 949 Users: users, 950 Groups: groups, 951 } 952 } 953 954 // fillResourceInfo 填充资源摘要信息 955 func (svr *Server) fillResourceInfo(resp *apisecurity.AuthStrategy, data *model.StrategyDetail) { 956 namespaces := make([]*apisecurity.StrategyResourceEntry, 0, len(data.Resources)) 957 services := make([]*apisecurity.StrategyResourceEntry, 0, len(data.Resources)) 958 configGroups := make([]*apisecurity.StrategyResourceEntry, 0, len(data.Resources)) 959 960 var ( 961 autoAllNs bool 962 autoAllSvc bool 963 autoAllConfigGroup bool 964 ) 965 966 for index := range data.Resources { 967 res := data.Resources[index] 968 switch res.ResType { 969 case int32(apisecurity.ResourceType_Namespaces): 970 if res.ResID == "*" { 971 autoAllNs = true 972 namespaces = []*apisecurity.StrategyResourceEntry{ 973 { 974 Id: utils.NewStringValue("*"), 975 Namespace: utils.NewStringValue("*"), 976 Name: utils.NewStringValue("*"), 977 }, 978 } 979 continue 980 } 981 982 if !autoAllNs { 983 ns := svr.cacheMgn.Namespace().GetNamespace(res.ResID) 984 if ns == nil { 985 log.Warn("[Auth][Strategy] not found namespace in fill-info", 986 zap.String("id", data.ID), zap.String("namespace", res.ResID)) 987 continue 988 } 989 namespaces = append(namespaces, &apisecurity.StrategyResourceEntry{ 990 Id: utils.NewStringValue(ns.Name), 991 Namespace: utils.NewStringValue(ns.Name), 992 Name: utils.NewStringValue(ns.Name), 993 }) 994 } 995 case int32(apisecurity.ResourceType_Services): 996 if res.ResID == "*" { 997 autoAllSvc = true 998 services = []*apisecurity.StrategyResourceEntry{ 999 { 1000 Id: utils.NewStringValue("*"), 1001 Namespace: utils.NewStringValue("*"), 1002 Name: utils.NewStringValue("*"), 1003 }, 1004 } 1005 continue 1006 } 1007 1008 if !autoAllSvc { 1009 svc := svr.cacheMgn.Service().GetServiceByID(res.ResID) 1010 if svc == nil { 1011 log.Warn("[Auth][Strategy] not found service in fill-info", 1012 zap.String("id", data.ID), zap.String("service", res.ResID)) 1013 continue 1014 } 1015 services = append(services, &apisecurity.StrategyResourceEntry{ 1016 Id: utils.NewStringValue(svc.ID), 1017 Namespace: utils.NewStringValue(svc.Namespace), 1018 Name: utils.NewStringValue(svc.Name), 1019 }) 1020 } 1021 case int32(apisecurity.ResourceType_ConfigGroups): 1022 if res.ResID == "*" { 1023 autoAllConfigGroup = true 1024 configGroups = []*apisecurity.StrategyResourceEntry{ 1025 { 1026 Id: utils.NewStringValue("*"), 1027 Namespace: utils.NewStringValue("*"), 1028 Name: utils.NewStringValue("*"), 1029 }, 1030 } 1031 continue 1032 } 1033 if !autoAllConfigGroup { 1034 groupId, err := strconv.ParseUint(res.ResID, 10, 64) 1035 if err != nil { 1036 log.Warn("[Auth][Strategy] invalid resource id", 1037 zap.String("id", data.ID), zap.String("config_file_group", res.ResID)) 1038 continue 1039 } 1040 group := svr.cacheMgn.ConfigGroup().GetGroupByID(groupId) 1041 if group == nil { 1042 log.Warn("[Auth][Strategy] not found config_file_group in fill-info", 1043 zap.String("id", data.ID), zap.String("config_file_group", res.ResID)) 1044 continue 1045 } 1046 configGroups = append(configGroups, &apisecurity.StrategyResourceEntry{ 1047 Id: utils.NewStringValue(res.ResID), 1048 Namespace: utils.NewStringValue(group.Namespace), 1049 Name: utils.NewStringValue(group.Name), 1050 }) 1051 } 1052 } 1053 } 1054 1055 resp.Resources = &apisecurity.StrategyResources{ 1056 Namespaces: namespaces, 1057 Services: services, 1058 ConfigGroups: configGroups, 1059 } 1060 } 1061 1062 type resourceFilter struct { 1063 ns map[string]struct{} 1064 svc map[string]struct{} 1065 conf map[string]struct{} 1066 } 1067 1068 // filter different types of Strategy resources 1069 func resourceDeduplication(resources []model.StrategyResource) []model.StrategyResource { 1070 rLen := len(resources) 1071 ret := make([]model.StrategyResource, 0, rLen) 1072 rf := resourceFilter{ 1073 ns: make(map[string]struct{}, rLen), 1074 svc: make(map[string]struct{}, rLen), 1075 conf: make(map[string]struct{}, rLen), 1076 } 1077 1078 est := struct{}{} 1079 for i := range resources { 1080 res := resources[i] 1081 if res.ResType == int32(apisecurity.ResourceType_Namespaces) { 1082 if _, exist := rf.ns[res.ResID]; !exist { 1083 rf.ns[res.ResID] = est 1084 ret = append(ret, res) 1085 } 1086 continue 1087 } 1088 1089 if res.ResType == int32(apisecurity.ResourceType_Services) { 1090 if _, exist := rf.svc[res.ResID]; !exist { 1091 rf.svc[res.ResID] = est 1092 ret = append(ret, res) 1093 } 1094 1095 continue 1096 } 1097 1098 // other type conf 1099 if _, exist := rf.conf[res.ResID]; !exist { 1100 rf.conf[res.ResID] = est 1101 ret = append(ret, res) 1102 } 1103 } 1104 1105 return ret 1106 }