-
Notifications
You must be signed in to change notification settings - Fork 66
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
Changes from all commits
b88ef25
72a55ec
c680065
5254b8e
d5e1714
78cc0e3
22c6151
19acdda
d70cbe6
fb450ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -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, | ||
}, | ||
) | ||
|
||
return Response(IdpCreateOutputSLZ(instance=idp).data, status=status.HTTP_201_CREATED) | ||
|
||
|
||
|
@@ -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"] | ||
|
@@ -226,6 +251,16 @@ def put(self, request, *args, **kwargs): | |
) | ||
idp.set_plugin_cfg(data["plugin_config"]) | ||
|
||
# 审计记录 | ||
add_audit_record( | ||
operator=idp.updater, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: 建议提前 current_user 变量,避免依赖使用 |
||
tenant_id=current_tenant_id, | ||
operation=OperationEnum.MODIFY_IDP, | ||
object_type=ObjectTypeEnum.IDP, | ||
object_id=idp.id, | ||
extras={"data_before": data_before}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 不记录data_after? |
||
) | ||
|
||
return Response(status=status.HTTP_204_NO_CONTENT) | ||
|
||
|
||
|
@@ -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, | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
|
||
|
||
|
@@ -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) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
}, | ||
) | ||
for user in TenantUser.objects.filter( | ||
tenant_id=cur_tenant_id, | ||
id__in=data["user_ids"], | ||
).select_related("data_source_user") | ||
] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
|
||
|
||
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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") | ||
] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 同上 |
||
|
||
# 移动操作:为数据源部门 & 用户添加关联边,但是会删除这批用户所有的存量关联边 | ||
with transaction.atomic(): | ||
# 先删除 | ||
|
@@ -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( | ||
|
@@ -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") | ||
] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 同上; |
||
|
||
# 移动操作:为数据源部门 & 用户添加关联边,但是会删除这批用户在当前部门的存量关联边 | ||
with transaction.atomic(): | ||
# 先删除(仅限于指定部门) | ||
|
@@ -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) | ||
|
||
|
||
|
@@ -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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
对于创建场景,建议 plugin_id 也记录