Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added operation audit for idp and user #1974

Closed
67 changes: 67 additions & 0 deletions src/bk-user/bkuser/apis/web/idp/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
from rest_framework.response import Response

from bkuser.apis.web.mixins import CurrentUserTenantMixin
from bkuser.apps.audit.constants import ObjectTypeEnum, OperationEnum
from bkuser.apps.audit.recorder import add_audit_record
from bkuser.apps.data_source.constants import DataSourceTypeEnum
from bkuser.apps.data_source.models import DataSource, DataSourceSensitiveInfo
from bkuser.apps.idp.constants import INVALID_REAL_DATA_SOURCE_ID, IdpStatus
Expand Down Expand Up @@ -149,6 +151,21 @@ def post(self, request, *args, **kwargs):
updater=current_user,
)

# 审计记录
add_audit_record(
operator=current_user,
tenant_id=current_tenant_id,
operation=OperationEnum.CREATE_IDP,
object_type=ObjectTypeEnum.IDP,
object_id=idp.id,
extras={
"name": idp.name,
"status": idp.status,
"plugin_config": idp.plugin_config,
"data_source_match_rules": idp.data_source_match_rules,
},
Copy link
Collaborator

Choose a reason for hiding this comment

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

对于创建场景,建议 plugin_id 也记录

)

return Response(IdpCreateOutputSLZ(instance=idp).data, status=status.HTTP_201_CREATED)


Expand Down Expand Up @@ -215,6 +232,14 @@ def put(self, request, *args, **kwargs):
slz.is_valid(raise_exception=True)
data = slz.validated_data

# 【审计】记录变更前数据
data_before = {
"name": idp.name,
"status": idp.status,
"plugin_config": idp.plugin_config,
"data_source_match_rules": idp.data_source_match_rules,
}

with transaction.atomic():
idp.name = data["name"]
idp.status = data["status"]
Expand All @@ -226,6 +251,16 @@ def put(self, request, *args, **kwargs):
)
idp.set_plugin_cfg(data["plugin_config"])

# 审计记录
add_audit_record(
operator=idp.updater,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: 建议提前 current_user 变量,避免依赖使用 B.x = u , C.y = B.x, D.z = C.y

tenant_id=current_tenant_id,
operation=OperationEnum.MODIFY_IDP,
object_type=ObjectTypeEnum.IDP,
object_id=idp.id,
extras={"data_before": data_before},
Copy link
Collaborator

Choose a reason for hiding this comment

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

不记录data_after?

)

return Response(status=status.HTTP_204_NO_CONTENT)


Expand Down Expand Up @@ -311,6 +346,21 @@ def post(self, request, *args, **kwargs):
# 由于需要替换敏感信息,因此需要独立调用 set_plugin_cfg 方法
data_source.set_plugin_cfg(plugin_config)

# 审计记录
add_audit_record(
operator=current_user,
tenant_id=current_tenant_id,
operation=OperationEnum.CREATE_IDP,
object_type=ObjectTypeEnum.IDP,
object_id=idp.id,
extras={
"name": idp.name,
"status": idp.status,
"plugin_config": idp.plugin_config,
"data_source_match_rules": idp.data_source_match_rules,
},
Copy link
Collaborator

Choose a reason for hiding this comment

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

对于创建场景,建议 plugin_id 也记录

)

return Response(IdpCreateOutputSLZ(instance=idp).data, status=status.HTTP_201_CREATED)


Expand Down Expand Up @@ -374,11 +424,28 @@ def put(self, request, *args, **kwargs):
slz.is_valid(raise_exception=True)
data = slz.validated_data

# 【审计】记录变更前的数据
data_before = {
"name": idp.name,
"status": idp.status,
"plugin_config": data_source.plugin_config,
}

with transaction.atomic():
idp.name = data["name"]
idp.status = data["status"]
idp.updater = request.user.username
idp.save(update_fields=["name", "status", "updater", "updated_at"])
data_source.set_plugin_cfg(data["plugin_config"])

# 审计记录
add_audit_record(
operator=idp.updater,
tenant_id=current_tenant_id,
operation=OperationEnum.MODIFY_IDP,
object_type=ObjectTypeEnum.IDP,
object_id=idp.id,
extras={"data_before": data_before},
)

return Response(status=status.HTTP_204_NO_CONTENT)
114 changes: 114 additions & 0 deletions src/bk-user/bkuser/apis/web/organization/views/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# to the current version of the project delivered to anyone in the future.

import itertools
from collections import defaultdict

from django.db import transaction
from drf_yasg.utils import swagger_auto_schema
Expand All @@ -30,6 +31,9 @@
TenantDeptUserRelationBatchUpdateInputSLZ,
)
from bkuser.apis.web.organization.views.mixins import CurrentUserTenantDataSourceMixin
from bkuser.apps.audit.constants import ObjectTypeEnum, OperationEnum
from bkuser.apps.audit.data_model import AuditObject
from bkuser.apps.audit.recorder import batch_add_audit_records
from bkuser.apps.data_source.models import DataSourceDepartmentUserRelation
from bkuser.apps.permission.constants import PermAction
from bkuser.apps.permission.permissions import perm_class
Expand Down Expand Up @@ -74,6 +78,31 @@ def post(self, request, *args, **kwargs):
# 由于复制操作不会影响存量的关联边,所以需要忽略冲突,避免出现用户复选的情况
DataSourceDepartmentUserRelation.objects.bulk_create(relations, ignore_conflicts=True)

# 【审计】批量创建审计对象
objects = [
AuditObject(
id=user.id,
extras={
"departments": list(data_source_dept_ids),
# 记录 name 便于前端展示
"name": user.data_source_user.username,
Copy link
Collaborator

Choose a reason for hiding this comment

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

  1. 存在二义性字段建议明确名称, departments => data_source_department_ids
  2. name -> username ,不建议偷偷修改变量 Key,引入排查和理解成本

},
)
for user in TenantUser.objects.filter(
tenant_id=cur_tenant_id,
id__in=data["user_ids"],
).select_related("data_source_user")
]
Copy link
Collaborator

Choose a reason for hiding this comment

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

这样查询是将之前已经添加都查询出来了,而不是本次添加的


# 审计记录
batch_add_audit_records(
operator=request.user.username,
tenant_id=cur_tenant_id,
operation=OperationEnum.MODIFY_USER_DEPARTMENT_RELATIONS,
object_type=ObjectTypeEnum.USER,
objects=objects,
)

return Response(status=status.HTTP_204_NO_CONTENT)


Expand Down Expand Up @@ -107,6 +136,32 @@ def put(self, request, *args, **kwargs):
id__in=data["user_ids"],
).values_list("data_source_user_id", flat=True)

# 【审计】获取用户与部门之间的映射
user_department_relations = DataSourceDepartmentUserRelation.objects.filter(
user_id__in=data_source_user_ids
).values("department_id", "user_id")
user_departments_map = defaultdict(list)
Copy link
Collaborator

Choose a reason for hiding this comment

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

user_departments_map 就近原则


# 【审计】将用户的所有部门存储在列表中
for relation in user_department_relations:
user_departments_map[relation["user_id"]].append(relation["department_id"])

# 【审计】批量创建审计对象
objects = [
AuditObject(
id=user.id,
extras={
"data_before": {"departments": user_departments_map[user.data_source_user_id]},
# 记录 name 便于前端展示
"name": user.data_source_user.username,
},
)
for user in TenantUser.objects.filter(
tenant_id=cur_tenant_id,
id__in=data["user_ids"],
).select_related("data_source_user")
]
Copy link
Collaborator

Choose a reason for hiding this comment

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

同上


# 移动操作:为数据源部门 & 用户添加关联边,但是会删除这批用户所有的存量关联边
with transaction.atomic():
# 先删除
Expand All @@ -118,6 +173,15 @@ def put(self, request, *args, **kwargs):
]
DataSourceDepartmentUserRelation.objects.bulk_create(relations)

# 审计记录
batch_add_audit_records(
operator=request.user.username,
tenant_id=cur_tenant_id,
operation=OperationEnum.MODIFY_USER_DEPARTMENT_RELATIONS,
object_type=ObjectTypeEnum.USER,
objects=objects,
)

return Response(status=status.HTTP_204_NO_CONTENT)

@swagger_auto_schema(
Expand Down Expand Up @@ -147,6 +211,22 @@ def patch(self, request, *args, **kwargs):
id__in=data["user_ids"],
).values_list("data_source_user_id", flat=True)

# 【审计】批量创建审计对象
objects = [
AuditObject(
id=user.id,
extras={
"data_before": {"department": source_data_source_dept.id},
# 记录 name 便于前端展示
"name": user.data_source_user.username,
},
)
for user in TenantUser.objects.filter(
tenant_id=cur_tenant_id,
id__in=data["user_ids"],
).select_related("data_source_user")
]
Copy link
Collaborator

Choose a reason for hiding this comment

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

同上;
下同


# 移动操作:为数据源部门 & 用户添加关联边,但是会删除这批用户在当前部门的存量关联边
with transaction.atomic():
# 先删除(仅限于指定部门)
Expand All @@ -160,6 +240,15 @@ def patch(self, request, *args, **kwargs):
]
DataSourceDepartmentUserRelation.objects.bulk_create(relations, ignore_conflicts=True)

# 批量操作
batch_add_audit_records(
operator=request.user.username,
tenant_id=cur_tenant_id,
operation=OperationEnum.MODIFY_USER_DEPARTMENT_RELATIONS,
object_type=ObjectTypeEnum.USER,
objects=objects,
)

return Response(status=status.HTTP_204_NO_CONTENT)


Expand Down Expand Up @@ -191,8 +280,33 @@ def delete(self, request, *args, **kwargs):
id__in=data["user_ids"],
).values_list("data_source_user_id", flat=True)

# 【审计】批量创建审计对象
objects = [
AuditObject(
id=user.id,
extras={
"department": source_data_source_dept.id,
# 记录 name 便于前端展示
"name": user.data_source_user.username,
},
)
for user in TenantUser.objects.filter(
tenant_id=cur_tenant_id,
id__in=data["user_ids"],
).select_related("data_source_user")
]

DataSourceDepartmentUserRelation.objects.filter(
user_id__in=data_source_user_ids, department=source_data_source_dept
).delete()

# 审计记录
batch_add_audit_records(
operator=request.user.username,
tenant_id=cur_tenant_id,
operation=OperationEnum.MODIFY_USER_DEPARTMENT_RELATIONS,
object_type=ObjectTypeEnum.USER,
objects=objects,
)

return Response(status=status.HTTP_204_NO_CONTENT)
Loading