diff --git a/src/bk-user/bkuser/apis/web/personal_center/views.py b/src/bk-user/bkuser/apis/web/personal_center/views.py index 1ce58247c..8f482903f 100644 --- a/src/bk-user/bkuser/apis/web/personal_center/views.py +++ b/src/bk-user/bkuser/apis/web/personal_center/views.py @@ -40,6 +40,8 @@ TenantUserRetrieveOutputSLZ, TenantUserTimeZoneUpdateInputSLZ, ) +from bkuser.apps.audit.constants import ObjectTypeEnum, OperationEnum +from bkuser.apps.audit.recorder import add_audit_record from bkuser.apps.permission.constants import PermAction from bkuser.apps.permission.permissions import perm_class from bkuser.apps.tenant.constants import UserFieldDataType @@ -195,12 +197,31 @@ def put(self, request, *args, **kwargs): VerificationCodeScene.UPDATE_PHONE, ) + # 【审计】记录更改前数据 + data_before = { + "is_inherited_phone": tenant_user.is_inherited_phone, + "custom_phone": tenant_user.custom_phone, + "custom_phone_country_code": tenant_user.custom_phone_country_code, + } + phone_info = TenantUserPhoneInfo( is_inherited_phone=is_inherited_phone, custom_phone=custom_phone, custom_phone_country_code=custom_phone_country_code, ) TenantUserHandler.update_tenant_user_phone(self.get_object(), phone_info) + + # 审计记录 + add_audit_record( + operator=request.user.username, + tenant_id=tenant_user.tenant_id, + operation=OperationEnum.MODIFY_USER_PHONE, + object_type=ObjectTypeEnum.USER, + object_id=tenant_user.id, + # 记录 name 方便前端展示 + extras={"data_before": data_before, "name": tenant_user.data_source_user.username}, + ) + return Response(status=status.HTTP_204_NO_CONTENT) def _validate_verification_code( @@ -296,8 +317,26 @@ def put(self, request, *args, **kwargs): self._validate_verification_code(custom_email, verification_code, VerificationCodeScene.UPDATE_EMAIL) + # 【审计】记录更改前数据 + data_before = { + "is_inherited_email": tenant_user.is_inherited_email, + "custom_email": tenant_user.custom_email, + } + email_info = TenantUserEmailInfo(is_inherited_email=is_inherited_email, custom_email=custom_email) TenantUserHandler.update_tenant_user_email(self.get_object(), email_info) + + # 审计记录 + add_audit_record( + operator=request.user.username, + tenant_id=tenant_user.tenant_id, + operation=OperationEnum.MODIFY_USER_EMAIL, + object_type=ObjectTypeEnum.USER, + object_id=tenant_user.id, + # 记录 name 方便前端展示 + extras={"data_before": data_before, "name": tenant_user.data_source_user.username}, + ) + return Response(status=status.HTTP_204_NO_CONTENT) def _validate_verification_code(self, email: str, code: str, scene: VerificationCodeScene): @@ -535,4 +574,15 @@ def put(self, request, *args, **kwargs): operator=request.user.username, ) + # 审计记录 + add_audit_record( + operator=request.user.username, + tenant_id=tenant_user.tenant_id, + operation=OperationEnum.MODIFY_USER_PASSWORD, + object_type=ObjectTypeEnum.USER, + object_id=tenant_user.id, + # 记录 name 方便前端展示 + extras={"name": tenant_user.data_source_user.username}, + ) + return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/src/bk-user/bkuser/apis/web/platform_management/views.py b/src/bk-user/bkuser/apis/web/platform_management/views.py index 74914d734..a10244d91 100644 --- a/src/bk-user/bkuser/apis/web/platform_management/views.py +++ b/src/bk-user/bkuser/apis/web/platform_management/views.py @@ -25,6 +25,8 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response +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, DataSourceDepartment, DataSourcePlugin, DataSourceUser from bkuser.apps.idp.data_models import gen_data_source_match_rule_of_local @@ -112,6 +114,23 @@ def post(self, request, *args, **kwargs): # 添加内置管理员账密登录认证源 self._add_builtin_management_local_idp(tenant.id, data_source.id) + # 审计记录 + add_audit_record( + operator=request.user.username, + tenant_id=tenant.id, + operation=OperationEnum.CREATE_TENANT, + object_type=ObjectTypeEnum.TENANT, + object_id=tenant.id, + extras={ + "name": data["name"], + "status": data["status"], + "notification_methods": data["notification_methods"], + "email": data["email"], + "phone": data["phone"], + "phone_country_code": data["phone_country_code"], + }, + ) + # 对租户内置管理员进行账密信息初始化 & 发送密码通知 initialize_identity_info_and_send_notification.delay(data_source.id) @@ -227,12 +246,25 @@ def put(self, request, *args, **kwargs): slz.is_valid(raise_exception=True) data = slz.validated_data + # 【审计】记录修改前数据 + data_before = {"name": tenant.name} + # 更新 tenant.name = data["name"] tenant.logo = data["logo"] tenant.updater = request.user.username tenant.save(update_fields=["name", "logo", "updater", "updated_at"]) + # 审计记录 + add_audit_record( + operator=request.user.username, + tenant_id=tenant.id, + operation=OperationEnum.MODIFY_TENANT, + object_type=ObjectTypeEnum.TENANT, + object_id=tenant.id, + extras={"data_before": data_before}, + ) + return Response(status=status.HTTP_204_NO_CONTENT) @swagger_auto_schema( @@ -248,6 +280,14 @@ def delete(self, request, *args, **kwargs): if tenant.status != TenantStatus.DISABLED: raise error_codes.TENANT_DELETE_FAILED.f(_("需要先停用租户才能删除")) + # 【审计】记录变更前数据,数据删除后便无法获取 + tenant_name = tenant.name + tenant_status = tenant.status + tenant_notification_methods = tenant.notification_methods + tenant_email = tenant.email + tenant_phone = tenant.phone + tenant_phone_country_code = tenant.phone_country_code + with transaction.atomic(): # 删除租户配置的认证源 idps = Idp.objects.filter(owner_tenant_id=tenant.id) @@ -266,6 +306,22 @@ def delete(self, request, *args, **kwargs): # 最后再删除租户 tenant.delete() + add_audit_record( + operator=request.user.username, + tenant_id=tenant.id, + operation=OperationEnum.DELETE_TENANT, + object_type=ObjectTypeEnum.TENANT, + object_id=tenant.id, + extras={ + "name": tenant_name, + "status": tenant_status, + "notification_methods": tenant_notification_methods, + "email": tenant_email, + "phone": tenant_phone, + "phone_country_code": tenant_phone_country_code, + }, + ) + return Response(status=status.HTTP_204_NO_CONTENT) @@ -287,10 +343,23 @@ def put(self, request, *args, **kwargs): if tenant.is_default: raise error_codes.TENANT_UPDATE_FAILED.f(_("默认租户不能停用")) + # 【审计】记录修改前数据 + data_before = {"status": tenant.status} + tenant.status = TenantStatus.DISABLED if tenant.status == TenantStatus.ENABLED else TenantStatus.ENABLED tenant.updater = request.user.username tenant.save(update_fields=["status", "updater", "updated_at"]) + # 审计记录 + add_audit_record( + operator=request.user.username, + tenant_id=tenant.id, + operation=OperationEnum.MODIFY_TENANT_STATUS, + object_type=ObjectTypeEnum.TENANT, + object_id=tenant.id, + extras={"data_before": data_before}, + ) + return Response(TenantStatusUpdateOutputSLZ(instance={"status": tenant.status.value}).data) diff --git a/src/bk-user/bkuser/apis/web/tenant_info/views.py b/src/bk-user/bkuser/apis/web/tenant_info/views.py index f80a3349a..1f3844881 100644 --- a/src/bk-user/bkuser/apis/web/tenant_info/views.py +++ b/src/bk-user/bkuser/apis/web/tenant_info/views.py @@ -24,6 +24,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 LocalDataSourceIdentityInfo from bkuser.apps.idp.constants import IdpStatus @@ -75,6 +77,13 @@ def put(self, request, *args, **kwargs): slz.is_valid(raise_exception=True) data = slz.validated_data + # 【审计】记录变更前数据 + data_before = { + "visible": tenant.visible, + "user_number_visible": tenant.user_number_visible, + "name": tenant.name, + } + # 更新 tenant.name = data["name"] tenant.logo = data["logo"] @@ -83,6 +92,16 @@ def put(self, request, *args, **kwargs): tenant.updater = request.user.username tenant.save(update_fields=["name", "logo", "visible", "user_number_visible", "updater", "updated_at"]) + # 审计记录 + add_audit_record( + operator=request.user.username, + tenant_id=tenant.id, + operation=OperationEnum.MODIFY_TENANT, + object_type=ObjectTypeEnum.TENANT, + object_id=tenant.id, + extras={"data_before": data_before}, + ) + return Response(status=status.HTTP_204_NO_CONTENT) @@ -242,6 +261,18 @@ def post(self, request, *args, **kwargs): [TenantManager(tenant_id=tenant_id, tenant_user_id=i) for i in waiting_create_ids] ) + # 审计记录 + add_audit_record( + operator=request.user.username, + tenant_id=tenant_id, + operation=OperationEnum.CREATE_TENANT_REAL_MANAGER, + object_type=ObjectTypeEnum.TENANT, + object_id=tenant_id, + extras={ + "tenant_real_manager_ids": list(waiting_create_ids), + }, + ) + return Response(status=status.HTTP_204_NO_CONTENT) @swagger_auto_schema( @@ -262,6 +293,16 @@ def delete(self, request, *args, **kwargs): tenant_user_id__in=ids, ).delete() + # 审计记录 + add_audit_record( + operator=request.user.username, + tenant_id=self.get_current_tenant_id(), + operation=OperationEnum.DELETE_TENANT_REAL_MANAGER, + object_type=ObjectTypeEnum.TENANT, + object_id=self.get_current_tenant_id(), + extras={"deleted_tenant_real_manager_ids": list(ids)}, + ) + return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/src/bk-user/bkuser/apis/web/tenant_setting/views.py b/src/bk-user/bkuser/apis/web/tenant_setting/views.py index 93af5a25d..c2e0aabf0 100644 --- a/src/bk-user/bkuser/apis/web/tenant_setting/views.py +++ b/src/bk-user/bkuser/apis/web/tenant_setting/views.py @@ -28,6 +28,8 @@ TenantUserValidityPeriodConfigInputSLZ, TenantUserValidityPeriodConfigOutputSLZ, ) +from bkuser.apps.audit.constants import ObjectTypeEnum, OperationEnum +from bkuser.apps.audit.recorder import add_audit_record from bkuser.apps.data_source.tasks import ( migrate_user_extras_with_mapping, remove_dropped_field_in_data_source_field_mapping, @@ -174,6 +176,16 @@ def put(self, request, *args, **kwargs): data = slz.validated_data cfg = self.get_object() + + # 【审计】记录变更前数据 + data_before = { + "enabled": cfg.enabled, + "validity_period": cfg.validity_period, + "remind_before_expire": cfg.remind_before_expire, + "enabled_notification_methods": cfg.enabled_notification_methods, + "notification_templates": cfg.notification_templates, + } + cfg.enabled = data["enabled"] cfg.validity_period = data["validity_period"] cfg.remind_before_expire = data["remind_before_expire"] @@ -182,4 +194,16 @@ def put(self, request, *args, **kwargs): cfg.updater = request.user.username cfg.save() + # 审计记录 + add_audit_record( + operator=request.user.username, + tenant_id=self.get_current_tenant_id(), + operation=OperationEnum.MODIFY_TENANT_ACCOUNT_VALIDITY_PERIOD_CONFIG, + object_type=ObjectTypeEnum.TENANT, + object_id=self.get_current_tenant_id(), + extras={ + "data_before": data_before, + }, + ) + return Response(status=status.HTTP_204_NO_CONTENT)