Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions bklog/apps/iam/handlers/permission.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,3 +526,19 @@ def grant_creator_action(self, resource: Resource, creator: str = None, raise_ex
raise e

return grant_result

def grant_creator_action_batch(self, resource: Resource, creators: list = None, raise_exception=False):
"""
为多个用户新建实例关联权限授权
:param resource: 资源实例
:param creators: 资源创建者列表
:param raise_exception: 是否抛出异常
:return: {creator: grant_result}
"""
# 权限中心单次授权仅接受一个 creator,去重后逐个授权
unique_creators = list(dict.fromkeys(creator for creator in (creators or []) if creator))

return {
creator: self.grant_creator_action(resource=resource, creator=creator, raise_exception=raise_exception)
for creator in unique_creators
}
41 changes: 41 additions & 0 deletions bklog/apps/log_databus/handlers/collector/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@ def custom_update(
is_platform_index=None,
platform_index_visibility=None,
platform_index_filter=None,
owners=None,
):
collector_config_update = {
"collector_config_name": collector_config_name,
Expand Down Expand Up @@ -654,6 +655,8 @@ def custom_update(
etl_handler.update_or_create(**etl_params)
self._sync_scene_tags_to_index_set(etl_params["labels"])

self._authorization_owners(self.data, owners)

custom_config.after_hook(self.data)

# add user_operation_record
Expand Down Expand Up @@ -1129,6 +1132,38 @@ def _authorization_collector(collector_config: CollectorConfig):
f"collector_config->({collector_config.collector_config_id}) grant creator action failed, reason: {e}"
)

@staticmethod
def _authorization_owners(collector_config: CollectorConfig, owners: list = None):
"""
将采集项及其索引集的新建关联权限授予指定用户,仅新增授权,不回收历史权限
"""
if not owners:
return

try:
permission = Permission()
permission.grant_creator_action_batch(
resource=ResourceEnum.COLLECTION.create_simple_instance(
collector_config.collector_config_id, attribute={"name": collector_config.collector_config_name}
),
creators=owners,
)

# 按采集项反查索引集,避免内存中的 collector_config.index_set_id 尚未刷新
index_set = LogIndexSet.objects.filter(collector_config_id=collector_config.collector_config_id).first()
if index_set:
permission.grant_creator_action_batch(
resource=ResourceEnum.INDICES.create_simple_instance(
index_set.index_set_id, attribute={"name": index_set.index_set_name}
),
creators=owners,
)
except Exception as e: # pylint: disable=broad-except
logger.warning(
f"collector_config->({collector_config.collector_config_id}) grant creator action to owners "
f"{owners} failed, reason: {e}"
)

def _itsm_start_judge(self):
if self.data.is_custom_scenario:
return
Expand Down Expand Up @@ -1439,6 +1474,7 @@ def custom_create(
platform_index_visibility=None,
platform_index_filter=None,
ignore_exists=False,
owners=None,
):
data_link_id = self.get_data_link_id(bk_biz_id=bk_biz_id, data_link_id=int(data_link_id or 0))
collector_config_params = {
Expand All @@ -1461,6 +1497,8 @@ def custom_create(
existing = CollectorConfig.objects.get(
collector_config_name_en=collector_config_name_en, bk_biz_id=bkdata_biz_id
)
# 幂等创建同样要保证 owners 拿到权限
self._authorization_owners(existing, owners)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 幂等创建会绕过实例权限给已有资源授权

custom_create 入口只校验业务级 create_collection_v2;当 ignore_exists=true 命中已有英文名时,这里直接对 existing 调用 _authorization_owners。于是仅有“采集新建”权限的用户可以把自己放进 owners,获得已有采集项的 view/manage 以及关联索引集的 manage 权限,而无需持有这两个实例的管理权限。

建议在 created=False 分支通过现有 IAM 实例鉴权入口校验调用者对已有采集项及索引集的相应管理权限后再授权,并补充“create-only 用户不能认领已有资源”的回归测试。

return {
"collector_config_id": existing.collector_config_id,
"index_set_id": existing.index_set_id,
Expand Down Expand Up @@ -1574,6 +1612,9 @@ def custom_create(
self.data.save(update_fields=["index_set_id"])
self._sync_scene_tags_to_index_set(params["labels"])

# 索引集ID在清洗创建后才最终确定,因此在此处再对 owners 授权
self._authorization_owners(self.data, owners)

custom_config.after_hook(self.data)

ret = {
Expand Down
6 changes: 6 additions & 0 deletions bklog/apps/log_databus/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1623,6 +1623,12 @@ class CustomCollectorBaseSerializer(CollectorETLParamsFieldSerializer, ParentInd
label=_("备注说明"), max_length=64, required=False, allow_null=True, allow_blank=True
)
is_display = serializers.BooleanField(label=_("是否展示"), default=True, required=False)
owners = serializers.ListField(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 请限制 owners 数量,避免无界串行 IAM 调用

ownersListField 没有 max_lengthgrant_creator_action_batch 又会为每个唯一用户同步调用一次 IAM,且采集项和索引集各执行一轮。当前字段可接受 1000 个唯一用户,这会展开为 2000 次串行远程授权;IAM 变慢或超时时会长期占用请求 worker。

建议按产品上限设置 max_length,并补充上限边界测试;如果确实需要大批量授权,应改为受控异步处理。

label=_("授权用户列表"),
required=False,
default=list,
child=serializers.CharField(max_length=64),
)

def validate(self, attrs: dict) -> dict:
# 先进行校验
Expand Down
8 changes: 6 additions & 2 deletions bklog/apps/log_databus/views/collector_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2000,6 +2000,7 @@ def custom_create(self, request):
@apiParam {Int} [storage_replies] 副本数量
@apiParam {String} category_id 数据分类 GlobalsConfig.category读取
@apiParam {String} description 备注说明
@apiParam {List} [owners] 授权用户列表,为其授予采集项与索引集的新建关联权限
@apiParamExample {json} 请求样例:
{
"bk_biz_id": 2,
Expand All @@ -2013,7 +2014,8 @@ def custom_create(self, request):
"retention": 1,
"es_shards": 1,
"storage_replies": 1,
"allocation_min_days": 1
"allocation_min_days": 1,
"owners": ["admin", "user1"]
}
@apiSuccessExample {json} 成功返回:
{
Expand Down Expand Up @@ -2051,6 +2053,7 @@ def custom_update(self, request, collector_config_id):
@apiParam {Int} allocation_min_days 冷热数据时间
@apiParam {Int} es_shards es分片数量
@apiParam {Int} [storage_replies] 副本数量
@apiParam {List} [owners] 授权用户列表,为其授予采集项与索引集的新建关联权限,仅新增不回收
@apiParamExample {json} 请求样例:
{
"collector_config_name": "xxxxx",
Expand All @@ -2060,7 +2063,8 @@ def custom_update(self, request, collector_config_id):
"retention": 1,
"storage_replies": 1,
"es_shards": 1,
"allocation_min_days": 1
"allocation_min_days": 1,
"owners": ["admin", "user1"]
}
@apiSuccessExample {json} 成功返回:
{
Expand Down
Loading