Skip to content

Commit

Permalink
Merge pull request TencentBlueKing#93 from Canway-shiisa/development
Browse files Browse the repository at this point in the history
fix: 审计支持失败记录 TencentBlueKing#71
  • Loading branch information
IMBlues authored Oct 26, 2021
2 parents 02f3f01 + f5c0cbe commit 2d9c70d
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 10 deletions.
13 changes: 10 additions & 3 deletions src/api/bkuser_core/audit/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

RESET_PASSWORD_VAILD_MINUTES = 3 * 60


TOKEN_IS_OK = 0
TOKEN_USED_CODE = 10000
TOKEN_EXPIRED_CODE = 10001
Expand All @@ -23,7 +22,6 @@


class LogInFailReasonEnum(AutoLowerEnum):

BAD_PASSWORD = auto()
EXPIRED_PASSWORD = auto()
TOO_MANY_FAILURE = auto()
Expand All @@ -40,7 +38,6 @@ class LogInFailReasonEnum(AutoLowerEnum):


class OperationEnum(AutoLowerEnum):

CREATE = auto()
UPDATE = auto()
DELETE = auto()
Expand All @@ -61,3 +58,13 @@ class OperationEnum(AutoLowerEnum):
(IMPORT, "导入"),
(RESTORATION, "恢复"),
)


class OperationStatusEnum(AutoLowerEnum):
SUCCEED = auto()
FAILED = auto()

_choices_labels = (
(SUCCEED, "成功"),
(FAILED, "失败"),
)
18 changes: 18 additions & 0 deletions src/api/bkuser_core/audit/migrations/0004_auto_20211021_1852.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.5 on 2021-10-21 10:52

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('audit', '0003_auto_20210516_1652'),
]

operations = [
migrations.AddField(
model_name='generallog',
name='status',
field=models.CharField(choices=[('succeed', '成功'), ('failed', '失败')], default='successed', max_length=16, verbose_name='状态'),
),
]
4 changes: 3 additions & 1 deletion src/api/bkuser_core/audit/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from django.db import models
from jsonfield import JSONField

from .constants import LogInFailReasonEnum
from .constants import LogInFailReasonEnum, OperationStatusEnum
from .managers import LogInManager, ResetPasswordManager


Expand Down Expand Up @@ -60,6 +60,8 @@ class Meta:
class GeneralLog(Log):
"""通用操作日志"""

status = models.CharField("状态", max_length=16, choices=OperationStatusEnum.get_choices())


class ApiRequest(Log):
"""API 请求日志"""
Expand Down
1 change: 1 addition & 0 deletions src/api/bkuser_core/audit/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class GeneralLogSerializer(CustomFieldsMixin, serializers.Serializer):
extra_value = serializers.JSONField(help_text=_("额外信息"))
operator = serializers.CharField(help_text=_("操作者"))
create_time = serializers.DateTimeField(help_text=_("创建时间"))
status = serializers.CharField(help_text=_("状态"))


class LoginLogSerializer(CustomFieldsMixin, serializers.Serializer):
Expand Down
30 changes: 28 additions & 2 deletions src/api/bkuser_core/audit/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@

from django.conf import settings

from ..common.error_codes import CoreAPIError
from . import models
from .constants import OperationEnum
from .constants import OperationEnum, OperationStatusEnum
from .models import GeneralLog, ProfileRelatedLog

if TYPE_CHECKING:
Expand Down Expand Up @@ -46,6 +47,7 @@ def create_general_log(
operator: str,
operate_type: str,
operator_obj: Any,
status: str = OperationStatusEnum.SUCCEED.value,
extra_info: Dict = None,
request=None,
) -> Optional[GeneralLog]:
Expand Down Expand Up @@ -77,7 +79,7 @@ def create_general_log(
operator_obj,
operator_obj.__class__.__name__,
)
return GeneralLog.objects.create(operator=operator, extra_value=extra_value)
return GeneralLog.objects.create(operator=operator, extra_value=extra_value, status=status)


def create_profile_log(
Expand All @@ -93,3 +95,27 @@ def create_profile_log(
raise ValueError("unknown operation for profile log")
except Exception:
raise ValueError("operation is not a profile log type")


def audit_error_general_log(operate_type):
"""定义捕获异常的审计日志装饰器"""

def catch_exc(func):
def _catch_exc(self, request, *args, **kwargs):
try:
func(self, request, *args, **kwargs)

except CoreAPIError as e:
create_general_log(
operator=request.operator,
operate_type=operate_type,
operator_obj=self.get_object(),
request=request,
status=OperationStatusEnum.FAILED.value,
extra_info={"failed_info": f"{e.message}"},
)
return

return _catch_exc

return catch_exc
14 changes: 11 additions & 3 deletions src/api/bkuser_core/categories/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from typing import List

from bkuser_core.audit.constants import OperationEnum
from bkuser_core.audit.utils import create_general_log
from bkuser_core.audit.utils import audit_error_general_log, create_general_log
from bkuser_core.bkiam.permissions import IAMAction, IAMHelper, IAMPermissionExtraInfo, need_iam
from bkuser_core.categories.constants import CategoryType, SyncTaskType
from bkuser_core.categories.exceptions import ExistsSyncingTaskError, FetchDataFromRemoteFailed
Expand Down Expand Up @@ -118,15 +118,13 @@ def create(self, request, *args, **kwargs):
max_order = ProfileCategory.objects.get_max_order()
instance.order = max_order + 1
instance.save(update_fields=["order"])

post_category_create.send(sender=self, category=instance, creator=request.operator)
create_general_log(
operator=request.operator,
operate_type=OperationEnum.CREATE.value,
operator_obj=instance,
request=request,
)

return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

def get_serializer(self, *args, **kwargs):
Expand All @@ -135,6 +133,7 @@ def get_serializer(self, *args, **kwargs):
else:
return self.serializer_class(*args, **kwargs)

@audit_error_general_log(operate_type=OperationEnum.UPDATE.value)
@method_decorator(clear_cache_if_succeed)
def update(self, request, *args, **kwargs):
"""
Expand Down Expand Up @@ -170,6 +169,7 @@ def update(self, request, *args, **kwargs):

return Response(serializer.data)

@audit_error_general_log(operate_type=OperationEnum.DELETE.value)
def destroy(self, request, *args, **kwargs):
"""
删除用户目录
Expand All @@ -179,6 +179,12 @@ def destroy(self, request, *args, **kwargs):
raise error_codes.CANNOT_DELETE_DEFAULT_CATEGORY

post_category_delete.send(sender=self, category=instance, operator=request.operator)
create_general_log(
operator=request.operator,
operate_type=OperationEnum.DELETE.value,
operator_obj=instance,
request=request,
)
return super().destroy(request, *args, **kwargs)

@swagger_auto_schema(
Expand Down Expand Up @@ -257,6 +263,7 @@ def test_fetch_data(self, request, lookup_value):

return Response()

@audit_error_general_log(operate_type=OperationEnum.SYNC.value)
@method_decorator(clear_cache_if_succeed)
@swagger_auto_schema(request_body=CategorySyncSerializer, responses={"200": CategorySyncResponseSLZ()})
def sync(self, request, lookup_value):
Expand Down Expand Up @@ -303,6 +310,7 @@ class CategoryFileViewSet(AdvancedModelViewSet, AdvancedListAPIView):
lookup_field = "id"
ordering = ["-create_time"]

@audit_error_general_log(operate_type=OperationEnum.IMPORT.value)
@method_decorator(clear_cache_if_succeed)
@swagger_auto_schema(request_body=CategorySyncSerializer, responses={"200": EmptySerializer()})
def import_data_file(self, request, lookup_value):
Expand Down
13 changes: 12 additions & 1 deletion src/api/bkuser_core/common/viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from operator import or_
from typing import Any, Dict, List, Optional

from bkuser_core.audit.constants import OperationEnum
from bkuser_core.audit.constants import OperationEnum, OperationStatusEnum
from bkuser_core.audit.utils import create_general_log
from bkuser_core.bkiam.exceptions import IAMPermissionDenied
from bkuser_core.bkiam.filters import IAMFilter
Expand Down Expand Up @@ -324,6 +324,17 @@ def restoration(self, request, lookup_value):
instance.enable()
except Exception as why:
# TODO: 基于 issue71 更新操作日志
create_general_log(
operator=request.operator,
operate_type=OperationEnum.RESTORATION.value,
operator_obj=instance,
request=request,
status=OperationStatusEnum.FAILED.value,
extra_info={
"action": f"restoration {instance._meta.model_name}.{self.lookup_field}-{lookup_value}",
"FAILED_info": f"{why}",
},
)
logger.exception("failed to restoration instance: %s, error: %s", instance, why)
else:
create_general_log(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.5 on 2021-09-26 11:08

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('user_settings', '0008_auto_20210706_1702'),
]

operations = [
migrations.AlterField(
model_name='settingmeta',
name='category_type',
field=models.CharField(choices=[('local', '本地目录'), ('mad', 'Microsoft Active Directory'), ('ldap', 'OpenLDAP'), ('tof', 'TOF'), ('custom', '自定义目录'), ('pluggable', '可插拔目录')], max_length=32, verbose_name='类型'),
),
]

0 comments on commit 2d9c70d

Please sign in to comment.