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

refactor: 重构审计记录方式 #127

Merged
merged 4 commits into from
Nov 3, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions src/api/bkuser_core/audit/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,8 @@

RESET_PASSWORD_VAILD_MINUTES = 3 * 60

TOKEN_IS_OK = 0
TOKEN_USED_CODE = 10000
TOKEN_EXPIRED_CODE = 10001
TOKEN_PROFILE_NOT_EXIST_CODE = 10002
TOKEN_NOT_EXIST_CODE = 10003


class LogInFailReasonEnum(AutoLowerEnum):
class LogInFailReason(AutoLowerEnum):
BAD_PASSWORD = auto()
EXPIRED_PASSWORD = auto()
TOO_MANY_FAILURE = auto()
Expand All @@ -37,7 +31,7 @@ class LogInFailReasonEnum(AutoLowerEnum):
)


class OperationEnum(AutoLowerEnum):
class OperationType(AutoLowerEnum):
CREATE = auto()
UPDATE = auto()
DELETE = auto()
Expand All @@ -60,7 +54,7 @@ class OperationEnum(AutoLowerEnum):
)


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

Expand Down
52 changes: 35 additions & 17 deletions src/api/bkuser_core/audit/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,46 @@
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
import logging
from typing import TYPE_CHECKING

from bkuser_core.audit.constants import OperationType
from bkuser_core.audit.utils import create_general_log, create_profile_log
from bkuser_core.profiles.signals import post_profile_create, post_profile_update
from bkuser_core.categories.signals import post_category_create
from bkuser_core.departments.signals import post_department_create
from bkuser_core.profiles.signals import post_field_create, post_profile_create, post_profile_update
from bkuser_core.user_settings.signals import post_setting_create
from django.dispatch import receiver

if TYPE_CHECKING:
from bkuser_core.profiles.models import Profile


logger = logging.getLogger(__name__)


@receiver(post_profile_create)
@receiver(post_profile_update)
def create_audit_log(sender, profile, operator, operation_type, extra_values, **kwargs):
"""Create an audit log"""
request = extra_values["request"]
def create_reset_password_log(sender, instance: "Profile", operator: str, extra_values: dict, **kwargs):
"""Create an audit log for profile"""
# 当密码信息存在时,我们需要增加一条记录
if "raw_password" in extra_values:
try:
create_profile_log(
instance,
"ResetPassword",
{"is_success": True, "password": extra_values["raw_password"]},
IMBlues marked this conversation as resolved.
Show resolved Hide resolved
extra_values["request"],
)
except Exception: # pylint: disable=broad-except
logger.exception("failed to create reset password log")


@receiver([post_profile_create, post_department_create, post_category_create, post_field_create, post_setting_create])
def create_audit_log(sender, instance: "Profile", operator: str, extra_values: dict, **kwargs):
"""Create an audit log for instance"""
create_general_log(
operator=operator,
operate_type=operation_type,
operator_obj=profile,
request=request,
operate_type=OperationType.CREATE.value,
operator_obj=instance,
request=extra_values["request"],
)

# 当密码信息存在时,我们需要增加一条记录,
if "raw_password" in extra_values:
create_profile_log(
profile,
"ResetPassword",
{"is_success": True, "password": extra_values["raw_password"]},
request,
)
6 changes: 3 additions & 3 deletions src/api/bkuser_core/audit/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from django.core.exceptions import ObjectDoesNotExist
from django.db import models

from .constants import LogInFailReasonEnum
from .constants import LogInFailReason


class ResetPasswordManager(models.Manager):
Expand All @@ -26,8 +26,8 @@ def latest_failed_count(self) -> int:
create_time = self.filter(is_success=True).latest().create_time
return self.filter(
is_success=False,
reason=LogInFailReasonEnum.BAD_PASSWORD.value, # type: ignore
reason=LogInFailReason.BAD_PASSWORD.value, # type: ignore
create_time__gt=create_time,
).count()
except ObjectDoesNotExist:
return self.filter(is_success=False, reason=LogInFailReasonEnum.BAD_PASSWORD.value).count() # type: ignore
return self.filter(is_success=False, reason=LogInFailReason.BAD_PASSWORD.value).count() # type: ignore
9 changes: 4 additions & 5 deletions src/api/bkuser_core/audit/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@
from dataclasses import dataclass
from typing import Optional

from bkuser_core.audit.constants import LogInFailReason, OperationStatus
from bkuser_core.audit.managers import LogInManager, ResetPasswordManager
from bkuser_core.common.fields import EncryptField
from bkuser_core.common.models import TimestampedModel
from django.db import models
from jsonfield import JSONField

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


@dataclass
class AuditObjMetaInfo:
Expand Down Expand Up @@ -60,7 +59,7 @@ class Meta:
class GeneralLog(Log):
"""通用操作日志"""

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


class ApiRequest(Log):
Expand All @@ -79,7 +78,7 @@ class LogIn(ProfileRelatedLog):
reason = models.CharField(
"登陆失败原因",
max_length=32,
choices=LogInFailReasonEnum.get_choices(),
choices=LogInFailReason.get_choices(),
null=True,
blank=True,
)
Expand Down
45 changes: 28 additions & 17 deletions src/api/bkuser_core/audit/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
import functools
import logging
from typing import TYPE_CHECKING, Any, Dict, Optional

from bkuser_core.audit import models as log_models_module
from bkuser_core.audit.constants import OperationStatus, OperationType
from bkuser_core.audit.models import GeneralLog, ProfileRelatedLog
from bkuser_core.common.error_codes import CoreAPIError
from django.conf import settings

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

if TYPE_CHECKING:
from bkuser_core.profiles.models import Profile
from rest_framework.request import Request
Expand Down Expand Up @@ -47,7 +47,7 @@ def create_general_log(
operator: str,
operate_type: str,
operator_obj: Any,
status: str = OperationStatusEnum.SUCCEED.value,
status: str = OperationStatus.SUCCEED.value,
extra_info: Dict = None,
request=None,
) -> Optional[GeneralLog]:
Expand All @@ -58,7 +58,7 @@ def create_general_log(
logger.exception("Object<%s> should add to_audit_info() method", operator_obj.__class__)
return None

if not OperationEnum.has_value(operate_type):
if not OperationType.has_value(operate_type):
logger.exception("operate type<%s> unknown", operate_type)
return None

Expand Down Expand Up @@ -90,31 +90,42 @@ def create_profile_log(
create_params.update({"extra_value": {"client_ip": get_client_ip(request)}})

try:
return getattr(models, operation).objects.create(profile=profile, **create_params)
return getattr(log_models_module, operation).objects.create(profile=profile, **create_params)
except AttributeError:
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 audit_general_log(operate_type: OperationType):
IMBlues marked this conversation as resolved.
Show resolved Hide resolved
"""定义捕获异常的审计日志装饰器"""

if operate_type == OperationType.CREATE.value:
raise ValueError("audit_general_log decoration does not support create views")

def catch_exc(func):
@functools.wraps(func)
def _catch_exc(self, request, *args, **kwargs):
_params = {
"operator": request.operator,
"operate_type": operate_type,
"request": request,
}
try:
func(self, request, *args, **kwargs)

r = func(self, request, *args, **kwargs)
except CoreAPIError as e:
IMBlues marked this conversation as resolved.
Show resolved Hide resolved
# get updated obj
_params["operator_obj"] = self.get_object()
create_general_log(
operator=request.operator,
operate_type=operate_type,
operator_obj=self.get_object(),
request=request,
status=OperationStatusEnum.FAILED.value,
**_params,
status=OperationStatus.FAILED.value,
extra_info={"failed_info": f"{e.message}"},
)
return
raise
else:
_params["operator_obj"] = self.get_object()
create_general_log(**_params)
return r

return _catch_exc

Expand Down
8 changes: 4 additions & 4 deletions src/api/bkuser_core/categories/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@


@receiver(post_category_create)
def create_creator_actions(sender, category, **kwargs):
def create_creator_actions(sender, instance, **kwargs):
"""请求权限中心,创建新建关联权限记录"""
logger.info("going to create resource_creator_action for Category<%s>", category.id)
logger.info("going to create resource_creator_action for Category<%s>", instance.id)
helper = IAMHelper()
try:
helper.create_creator_actions(kwargs["creator"], category)
helper.create_creator_actions(kwargs["creator"], instance)
except Exception: # pylint: disable=broad-except
logger.exception("failed to create resource_creator_action (category related)")

# 创建目录之后,默认拥有了目录 & 组织的管理能力
try:
helper.create_auth_by_ancestor(
username=kwargs["creator"],
ancestor=category,
ancestor=instance,
target_type=ResourceType.DEPARTMENT.value,
action_ids=[IAMAction.MANAGE_DEPARTMENT, IAMAction.VIEW_DEPARTMENT],
)
Expand Down
46 changes: 26 additions & 20 deletions src/api/bkuser_core/categories/plugins/ldap/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
specific language governing permissions and limitations under the License.
"""
import logging
from typing import TYPE_CHECKING

from bkuser_core.categories.constants import CategoryType
from bkuser_core.categories.loader import get_plugin_by_category
Expand All @@ -18,46 +19,51 @@
update_periodic_sync_task,
)
from bkuser_core.categories.signals import post_category_create, post_category_delete
from bkuser_core.user_settings.signals import post_setting_create_or_update
from bkuser_core.user_settings.signals import post_setting_create, post_setting_update
from django.dispatch import receiver

if TYPE_CHECKING:
from bkuser_core.categories.models import ProfileCategory
from bkuser_core.user_settings.models import Setting

logger = logging.getLogger(__name__)


@receiver(post_category_create)
def create_sync_tasks(sender, category, creator: str, **kwargs):
if category.type not in [CategoryType.LDAP.value, CategoryType.MAD.value]:
def create_sync_tasks(sender, instance: "ProfileCategory", operator: str, **kwargs):
if instance.type not in [CategoryType.LDAP.value, CategoryType.MAD.value]:
return

logger.info("going to add periodic task for Category<%s>", category.id)
logger.info("going to add periodic task for Category<%s>", instance.id)
make_periodic_sync_task(
category_id=category.id,
operator=creator,
interval_seconds=get_plugin_by_category(category).extra_config["default_sync_period"],
category_id=instance.id,
operator=operator,
interval_seconds=get_plugin_by_category(instance).extra_config["default_sync_period"],
)


@receiver(post_category_delete)
def delete_sync_tasks(sender, category, **kwargs):
if category.type not in [CategoryType.LDAP.value, CategoryType.MAD.value]:
def delete_sync_tasks(sender, instance: "ProfileCategory", **kwargs):
if instance.type not in [CategoryType.LDAP.value, CategoryType.MAD.value]:
return

logger.info("going to delete periodic task for Category<%s>", category.id)
delete_periodic_sync_task(category.id)
logger.info("going to delete periodic task for Category<%s>", instance.id)
delete_periodic_sync_task(instance.id)


@receiver(post_setting_create_or_update)
def update_sync_tasks(sender, setting, operator: str, **kwargs):
if setting.category.type not in [CategoryType.LDAP.value, CategoryType.MAD.value]:
@receiver(post_setting_update)
@receiver(post_setting_create)
def update_sync_tasks(sender, instance: "Setting", operator: str, **kwargs):
if instance.category.type not in [CategoryType.LDAP.value, CategoryType.MAD.value]:
return

if not setting.meta.key == "pull_cycle":
if not instance.meta.key == "pull_cycle":
return

cycle_value = int(setting.value)
category_config = get_plugin_by_category(setting.category)
cycle_value = int(instance.value)
category_config = get_plugin_by_category(instance.category)
if cycle_value <= 0:
delete_periodic_sync_task(category_id=setting.category_id)
delete_periodic_sync_task(category_id=instance.category_id)
return

elif cycle_value < category_config.extra_config["min_sync_period"]:
Expand All @@ -66,10 +72,10 @@ def update_sync_tasks(sender, setting, operator: str, **kwargs):
# 尝试更新周期任务周期
logger.info(
"going to update category<%s> sync interval to %s",
setting.category_id,
instance.category_id,
cycle_value,
)
try:
update_periodic_sync_task(category_id=setting.category_id, operator=operator, interval_seconds=cycle_value)
update_periodic_sync_task(category_id=instance.category_id, operator=operator, interval_seconds=cycle_value)
except Exception: # pylint: disable=broad-except
logger.exception("failed to update periodic task schedule")
12 changes: 8 additions & 4 deletions src/api/bkuser_core/categories/plugins/local/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,22 @@
specific language governing permissions and limitations under the License.
"""
import logging
from typing import TYPE_CHECKING

from bkuser_core.categories.constants import CategoryType
from bkuser_core.categories.signals import post_category_create
from django.dispatch import receiver

if TYPE_CHECKING:
from bkuser_core.categories.models import ProfileCategory

logger = logging.getLogger(__name__)


@receiver(post_category_create)
def make_local_default_settings(sender, category, **kwargs):
if category.type not in [CategoryType.LOCAL.value]:
def make_local_default_settings(sender, instance: "ProfileCategory", **kwargs):
if instance.type not in [CategoryType.LOCAL.value]:
return

logger.info("going to make default settings for Category<%s>", category.id)
category.make_default_settings()
logger.info("going to make default settings for Category<%s>", instance.id)
instance.make_default_settings()
4 changes: 2 additions & 2 deletions src/api/bkuser_core/categories/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
"""
import django

post_category_create = django.dispatch.Signal(providing_args=["category", "creator"])
post_category_delete = django.dispatch.Signal(providing_args=["category", "operator"])
post_category_create = django.dispatch.Signal(providing_args=["instance", "operator", "extra_values"])
post_category_delete = django.dispatch.Signal(providing_args=["instance", "operator", "extra_values"])
Loading