mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-05-06 15:16:32 +00:00
perf: Export resources to add operation logs
This commit is contained in:
parent
e8e0ea920b
commit
1f60e328b6
@ -1,65 +1,15 @@
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from django.db.models import Model
|
||||
from django.utils import translation
|
||||
from django.utils.translation import gettext_noop
|
||||
|
||||
from audits.const import ActionChoices
|
||||
from common.views.mixins import RecordViewLogMixin
|
||||
from common.utils import i18n_fmt
|
||||
from audits.handler import create_or_update_operate_log
|
||||
|
||||
|
||||
class AccountRecordViewLogMixin(RecordViewLogMixin):
|
||||
class AccountRecordViewLogMixin(object):
|
||||
get_object: callable
|
||||
get_queryset: callable
|
||||
|
||||
@staticmethod
|
||||
def _filter_params(params):
|
||||
new_params = {}
|
||||
need_pop_params = ('format', 'order')
|
||||
for key, value in params.items():
|
||||
if key in need_pop_params:
|
||||
continue
|
||||
if isinstance(value, list):
|
||||
value = list(filter(None, value))
|
||||
if value:
|
||||
new_params[key] = value
|
||||
return new_params
|
||||
|
||||
def get_resource_display(self, request):
|
||||
query_params = dict(request.query_params)
|
||||
params = self._filter_params(query_params)
|
||||
|
||||
spm_filter = params.pop("spm", None)
|
||||
|
||||
if not params and not spm_filter:
|
||||
display_message = gettext_noop("Export all")
|
||||
elif spm_filter:
|
||||
display_message = gettext_noop("Export only selected items")
|
||||
else:
|
||||
query = ",".join(
|
||||
["%s=%s" % (key, value) for key, value in params.items()]
|
||||
)
|
||||
display_message = i18n_fmt(gettext_noop("Export filtered: %s"), query)
|
||||
return display_message
|
||||
|
||||
@property
|
||||
def detail_msg(self):
|
||||
return i18n_fmt(
|
||||
gettext_noop('User %s view/export secret'), self.request.user
|
||||
)
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
list_func = getattr(super(), 'list')
|
||||
if not callable(list_func):
|
||||
return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)
|
||||
response = list_func(request, *args, **kwargs)
|
||||
with translation.override('en'):
|
||||
resource_display = self.get_resource_display(request)
|
||||
ids = [q.id for q in self.get_queryset()]
|
||||
self.record_logs(
|
||||
ids, ActionChoices.view, self.detail_msg, resource_display=resource_display
|
||||
)
|
||||
return response
|
||||
model: Model
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
retrieve_func = getattr(super(), 'retrieve')
|
||||
@ -67,9 +17,9 @@ class AccountRecordViewLogMixin(RecordViewLogMixin):
|
||||
return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)
|
||||
response = retrieve_func(request, *args, **kwargs)
|
||||
with translation.override('en'):
|
||||
resource = self.get_object()
|
||||
self.record_logs(
|
||||
[resource.id], ActionChoices.view, self.detail_msg, resource=resource
|
||||
create_or_update_operate_log(
|
||||
ActionChoices.view, self.model._meta.verbose_name,
|
||||
force=True, resource=self.get_object(),
|
||||
)
|
||||
return response
|
||||
|
||||
|
@ -24,6 +24,7 @@ class ActionChoices(TextChoices):
|
||||
update = "update", _("Update")
|
||||
delete = "delete", _("Delete")
|
||||
create = "create", _("Create")
|
||||
export = "export", _("Export")
|
||||
# Activities action
|
||||
download = "download", _("Download")
|
||||
connect = "connect", _("Connect")
|
||||
|
@ -6,12 +6,16 @@ from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db import models
|
||||
from django.db.models import F, Value, CharField
|
||||
from django.db.models.functions import Concat
|
||||
from django.utils import translation
|
||||
from itertools import chain
|
||||
|
||||
from common.db.fields import RelatedManager
|
||||
from common.utils import validate_ip, get_ip_city, get_logger
|
||||
from common.utils.timezone import as_current_tz
|
||||
from .const import DEFAULT_CITY
|
||||
from .const import DEFAULT_CITY, ActivityChoices as LogChoice
|
||||
from .handler import create_or_update_operate_log
|
||||
from .models import ActivityLog
|
||||
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@ -140,3 +144,15 @@ def construct_userlogin_usernames(user_queryset):
|
||||
).values_list("usernames_combined_field", flat=True)
|
||||
usernames = list(chain(usernames_original, usernames_combined))
|
||||
return usernames
|
||||
|
||||
|
||||
def record_operate_log_and_activity_log(ids, action, detail, model, **kwargs):
|
||||
from orgs.utils import current_org
|
||||
|
||||
org_id = current_org.id
|
||||
with translation.override('en'):
|
||||
resource_type = model._meta.verbose_name
|
||||
create_or_update_operate_log(action, resource_type, force=True, **kwargs)
|
||||
base_data = {'type': LogChoice.operate_log, 'detail': detail, 'org_id': org_id}
|
||||
activities = [ActivityLog(resource_id=r_id, **base_data) for r_id in ids]
|
||||
ActivityLog.objects.bulk_create(activities)
|
||||
|
@ -13,11 +13,13 @@ from rest_framework.utils import encoders, json
|
||||
|
||||
from common.serializers import fields as common_fields
|
||||
from common.utils import get_logger
|
||||
from .mixins import LogMixin
|
||||
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
class BaseFileRenderer(BaseRenderer):
|
||||
class BaseFileRenderer(LogMixin, BaseRenderer):
|
||||
# 渲染模板标识, 导入、导出、更新模板: ['import', 'update', 'export']
|
||||
template = 'export'
|
||||
serializer = None
|
||||
@ -256,6 +258,8 @@ class BaseFileRenderer(BaseRenderer):
|
||||
logger.debug(e, exc_info=True)
|
||||
value = 'Render error! ({})'.format(self.media_type).encode('utf-8')
|
||||
return value
|
||||
|
||||
self.record_logs(request, view, data)
|
||||
return value
|
||||
|
||||
def compress_into_zip_file(self, value, request, response):
|
||||
|
69
apps/common/drf/renders/mixins.py
Normal file
69
apps/common/drf/renders/mixins.py
Normal file
@ -0,0 +1,69 @@
|
||||
from django.utils.translation import gettext_noop
|
||||
|
||||
from audits.const import ActionChoices
|
||||
from audits.utils import record_operate_log_and_activity_log
|
||||
from common.utils import get_logger
|
||||
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
class LogMixin(object):
|
||||
@staticmethod
|
||||
def _clean_params(query_params):
|
||||
clean_params = {}
|
||||
ignore_params = ('format', 'order')
|
||||
for key, value in dict(query_params).items():
|
||||
if key in ignore_params:
|
||||
continue
|
||||
if isinstance(value, list):
|
||||
value = list(filter(None, value))
|
||||
if value:
|
||||
clean_params[key] = value
|
||||
return clean_params
|
||||
|
||||
@staticmethod
|
||||
def _get_model(view):
|
||||
model = getattr(view, 'model', None)
|
||||
if not model:
|
||||
serializer = view.get_serializer()
|
||||
if serializer:
|
||||
model = serializer.Meta.model
|
||||
return model
|
||||
|
||||
@staticmethod
|
||||
def _build_after(params, data):
|
||||
base = {
|
||||
gettext_noop('Resource count'): {'value': len(data)}
|
||||
}
|
||||
extra = {key: {'value': value} for key, value in params.items()}
|
||||
return {**extra, **base}
|
||||
|
||||
@staticmethod
|
||||
def get_resource_display(params):
|
||||
spm_filter = params.pop("spm", None)
|
||||
if not params and not spm_filter:
|
||||
display_message = gettext_noop("Export all")
|
||||
elif spm_filter:
|
||||
display_message = gettext_noop("Export only selected items")
|
||||
else:
|
||||
display_message = gettext_noop("Export filtered")
|
||||
return display_message
|
||||
|
||||
def record_logs(self, request, view, data):
|
||||
activity_ids, activity_detail = [], ''
|
||||
model = self._get_model(view)
|
||||
if not model:
|
||||
logger.warning('Model is not defined in view: %s' % view)
|
||||
return
|
||||
|
||||
params = self._clean_params(request.query_params)
|
||||
resource_display = self.get_resource_display(params)
|
||||
after = self._build_after(params, data)
|
||||
if hasattr(view, 'get_activity_detail_msg'):
|
||||
activity_detail = view.get_activity_detail_msg()
|
||||
activity_ids = [d['id'] for d in data if 'id' in d]
|
||||
record_operate_log_and_activity_log(
|
||||
activity_ids, ActionChoices.export, activity_detail,
|
||||
model, resource_display=resource_display, after=after
|
||||
)
|
@ -2,20 +2,14 @@
|
||||
#
|
||||
from django.contrib.auth.mixins import UserPassesTestMixin
|
||||
from django.http.response import JsonResponse
|
||||
from django.db.models import Model
|
||||
from django.utils import translation
|
||||
from rest_framework import permissions
|
||||
from rest_framework.request import Request
|
||||
|
||||
from audits.const import ActivityChoices
|
||||
from audits.handler import create_or_update_operate_log
|
||||
from audits.models import ActivityLog
|
||||
from common.exceptions import UserConfirmRequired
|
||||
from orgs.utils import current_org
|
||||
|
||||
|
||||
__all__ = [
|
||||
"PermissionsMixin",
|
||||
"RecordViewLogMixin",
|
||||
"UserConfirmRequiredExceptionMixin",
|
||||
]
|
||||
|
||||
@ -45,23 +39,3 @@ class PermissionsMixin(UserPassesTestMixin):
|
||||
if not permission_class().has_permission(self.request, self):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class RecordViewLogMixin:
|
||||
model: Model
|
||||
|
||||
def record_logs(self, ids, action, detail, model=None, **kwargs):
|
||||
with translation.override('en'):
|
||||
model = model or self.model
|
||||
resource_type = model._meta.verbose_name
|
||||
create_or_update_operate_log(
|
||||
action, resource_type, force=True, **kwargs
|
||||
)
|
||||
activities = [
|
||||
ActivityLog(
|
||||
resource_id=resource_id, type=ActivityChoices.operate_log,
|
||||
detail=detail, org_id=current_org.id,
|
||||
)
|
||||
for resource_id in ids
|
||||
]
|
||||
ActivityLog.objects.bulk_create(activities)
|
||||
|
@ -18,6 +18,7 @@ from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
|
||||
from audits.const import ActionChoices
|
||||
from audits.utils import record_operate_log_and_activity_log
|
||||
from common.api import AsyncApiMixin
|
||||
from common.const.http import GET, POST
|
||||
from common.drf.filters import BaseFilterSet
|
||||
@ -27,7 +28,6 @@ from common.permissions import IsServiceAccount
|
||||
from common.storage.replay import ReplayStorageHandler, SessionPartReplayStorageHandler
|
||||
from common.utils import data_to_json, is_uuid, i18n_fmt
|
||||
from common.utils import get_logger, get_object_or_none
|
||||
from common.views.mixins import RecordViewLogMixin
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from orgs.utils import tmp_to_root_org, tmp_to_org
|
||||
from rbac.permissions import RBACPermission
|
||||
@ -77,7 +77,7 @@ class SessionFilterSet(BaseFilterSet):
|
||||
return queryset.filter(terminal__name=value)
|
||||
|
||||
|
||||
class SessionViewSet(RecordViewLogMixin, OrgBulkModelViewSet):
|
||||
class SessionViewSet(OrgBulkModelViewSet):
|
||||
model = Session
|
||||
serializer_classes = {
|
||||
'default': serializers.SessionSerializer,
|
||||
@ -153,7 +153,7 @@ class SessionViewSet(RecordViewLogMixin, OrgBulkModelViewSet):
|
||||
detail = i18n_fmt(
|
||||
REPLAY_OP, self.request.user, _('Download'), str(session)
|
||||
)
|
||||
self.record_logs(
|
||||
record_operate_log_and_activity_log(
|
||||
[session.asset_id], ActionChoices.download, detail,
|
||||
model=Session, resource_display=str(session)
|
||||
)
|
||||
@ -211,7 +211,7 @@ class SessionViewSet(RecordViewLogMixin, OrgBulkModelViewSet):
|
||||
return super().perform_create(serializer)
|
||||
|
||||
|
||||
class SessionReplayViewSet(AsyncApiMixin, RecordViewLogMixin, viewsets.ViewSet):
|
||||
class SessionReplayViewSet(AsyncApiMixin, viewsets.ViewSet):
|
||||
serializer_class = serializers.ReplaySerializer
|
||||
download_cache_key = "SESSION_REPLAY_DOWNLOAD_{}"
|
||||
session = None
|
||||
@ -283,7 +283,7 @@ class SessionReplayViewSet(AsyncApiMixin, RecordViewLogMixin, viewsets.ViewSet):
|
||||
detail = i18n_fmt(
|
||||
REPLAY_OP, self.request.user, _('View'), str(session)
|
||||
)
|
||||
self.record_logs(
|
||||
record_operate_log_and_activity_log(
|
||||
[session.asset_id], ActionChoices.download, detail,
|
||||
model=Session, resource_display=str(session)
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user