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 audit operation for data source and user #1981

Closed
wants to merge 2 commits into from

Conversation

rolin999
Copy link
Collaborator

Description

用户管理 SaaS 操作审计(模型 + 数据源 + 用户)

Checklist

  • 填写 PR 描述及相关 issue (write PR description and related issue)
  • 代码风格检查通过 (code style check passed)
  • PR 中包含单元测试 (include unit test)
  • 单元测试通过 (unit test passed)
  • 本地开发联调环境验证通过 (local development environment verification passed)

@@ -603,6 +608,8 @@ def post(self, request, *args, **kwargs):
operation=OperationEnum.SYNC_DATA_SOURCE,
object_type=ObjectTypeEnum.DATA_SOURCE,
object_id=data_source.id,
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_before, data_after, extras 都做成可选参数



class CurrentUserDepartmentRelationMixin:
"""获取用户与部门之间的映射关系"""
Copy link
Collaborator

Choose a reason for hiding this comment

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

需要讨论:加这个 mixin 来算数据,是否合适?

)

# 【审计】批量添加审计记录
batch_add_audit_records(
Copy link
Collaborator

Choose a reason for hiding this comment

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

@nannan00 感觉审计的逻辑有点太多了,比业务逻辑还要多 :(

)

# 若有数据变更,则添加记录
if data_before != data_after or extras != {}:
Copy link
Collaborator

Choose a reason for hiding this comment

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

数据还能有没变更的情况么,如果有得加 warning 日志?

Copy link
Collaborator

@narasux narasux left a comment

Choose a reason for hiding this comment

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

目前存在一个问题:操作审计的代码远多于业务代码,显得特别乱,可读性很差

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

# 【审计】记录变更前用户-部门映射
user_department_map_before = self.get_user_department_map(data_source_user_ids=data_source_user_ids)
Copy link
Collaborator

Choose a reason for hiding this comment

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

为审计预留的数据, 统一命名:

data_before_xxxxx
data_after_xxxx

operator=request.user.username,
tenant_id=self.get_current_tenant_id(),
objects=audit_objects,
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

@nannan00
每个审计看起来还是非常复杂的, 反客为主占用业务逻辑的带宽

是否能针对这类

  1. 构造 audit_objects 有没有更简单的方法,能够抽象出来
  2. 对象默认值设置好,只关注需要传入的对象
  3. 将审计代码抽成self._get_audit(xxxxx)?

# 操作后数据
data_after: Dict
# 额外信息
extras: Dict
Copy link
Collaborator

Choose a reason for hiding this comment

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

    # 操作对象名称
    name: str = ""
    ...
    # 操作前数据
    data_before: Dict = Field(default_factory=dict)
    # 操作后数据
    data_after: Dict = Field(default_factory=dict)
    # 额外信息
    extras: Dict = Field(default_factory=dict)

tenant_id=cur_tenant_id,
objects=audit_objects,
)

Copy link
Collaborator

@nannan00 nannan00 Nov 13, 2024

Choose a reason for hiding this comment

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

目前是 view -> apps.audit , view -> biz -> apps.audit

在 Biz 模块里,封装业务相关的审计,避免 审计代码过多导致 view 里本身业务逻辑的可读性变差

比如 biz/auditor.py 里 定义 TenantUserUpdateAuditor

# 是否定义对象更具体操作的审计?根据场景考虑,DataSourceDeleteAuditor、TenantUserUpdateAuditor 之类的
class TenantUserUpdateAuditor:
    """用于记录租户用户变更的审计"""
    def __init__(self, operator: str, tenant_id: str):
        self.operator = operator
        self.tenant_id = tenant_id

        // 根据实际需要定义
        self.data_befores = {} 

    def pre_record_data_before(self, tenant_user: TenantUser):
        # 执行变更前的相关数据记录

        # 【审计】记录修改前的数据源用户数据
         self.data_befores["data_source_user"] = get_model_dict(tenant_user.data_source_user)

        # 【审计】记录修改前的租户用户数据
        self.data_befores["tenant_user"] = get_model_dict(tenant_user)

         # 【审计】记录修改前的用户部门
        self.data_befores["department_ids"] = list(
            DataSourceDepartmentUserRelation.objects.filter(
                user=data_source_user,
            ).values_list("department_id", flat=True)
        )

        # 【审计】记录修改前的用户上级
        self.data_befores["leader_ids"] = list(
            DataSourceUserLeaderRelation.objects.filter(user=data_source_user).values_list("leader_id", flat=True)
        )

    def record(self, tenant_user:  TenantUser, data_source_user: DataSourceUser, leader_ids: List[int], department_ids: List[int]):
        # 组装相关数据,并调用 apps.audit 模块里的方法进行记录

        ds_user_object = {"id": data_source_user.id, "name": data_source_user.username, "type": ObjectTypeEnum.DATA_SOURCE_USER}

        audit_objects = [
            # 数据源用户本身信息
            AuditObject(
                **ds_user_object,
                operation=OperationEnum.MODIFY_DATA_SOURCE_USER,
                data_before=self.data_befores["data_source_user"],
                data_after=get_model_dict(data_source_user),
                
            ),
            # 数据源用户的部门
            AuditObject(
                **ds_user_object,
                operation=OperationEnum.MODIFY_USER_DEPARTMENT,
                data_before={"department_ids": self.data_befores["department_ids"]},
                data_after={"department_ids": department_ids},
            ),
            # 数据源用户的 Leader
            AuditObject(
                **ds_user_object,
                operation=OperationEnum.MODIFY_USER_LEADER,
                data_before={"leader_ids": self.data_befores["leader_ids"]},
                data_after={"leader_ids": leader_ids},
            ),
            # 租户用户
            AuditObject(
                id=tenant_user.id,
                type=ObjectTypeEnum.TENANT_USER,
                operation=OperationEnum.MODIFY_TENANT_USER,
                data_before=self.data_befores["tenant_user_before"],
                data_after=get_model_dict(tenant_user),
            )
        ]
        
        batch_add_audit_records(self.operator, self.tenant_id, audit_objects)

调用点(暂时不考虑 Context 方式调用):

auditor = TenantUserUpdateAuditor(request.user.username, cur_tenant_id)
auditor.pre_record_data_before(tenant_user)
......
# 执行变更
......
auditor.record(tenant_user.id, data_source_dept_ids , data_source_leader_ids)

operation=OperationEnum.MODIFY_USER_LEADER_RELATIONS,
# 采用 sorted 为了 list 对象比较
data_before={"leader_ids": sorted(data_source_leader_ids_before)},
data_after={"leader_ids": sorted(data_source_leader_ids)},
Copy link
Collaborator

Choose a reason for hiding this comment

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

比较是底层的审计方法职责,上层不需要关注

@rolin999 rolin999 closed this Nov 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants