diff --git a/jumpserver/jumpserver/apps/assets/templates/assets/system_user_list.html b/jumpserver/jumpserver/apps/assets/templates/assets/system_user_list.html
index e6b259585ea07fac00448d0ec26363add18ffc56..0e88b64610285d82a6cbc382e020de728fde3864 100644
--- a/jumpserver/jumpserver/apps/assets/templates/assets/system_user_list.html
+++ b/jumpserver/jumpserver/apps/assets/templates/assets/system_user_list.html
@@ -2,36 +2,13 @@
{% load i18n %}
{% block help_message %}
-
{% trans 'System user is Jumpserver jump login assets used by the users, can be understood as the user login assets, such as web, sa, the dba (` ssh web@some-host `), rather than using a user the username login server jump (` ssh xiaoming@some-host `); '%}
{% trans 'In simple terms, users log into Jumpserver using their own username, and Jumpserver uses system users to log into assets. '%}
{% trans 'When system users are created, if you choose auto push Jumpserver to use Ansible push system users into the asset, if the asset (Switch) does not support ansible, please manually fill in the account password.' %}
-
{% endblock %}
{% block table_search %}
-
+ {% include '_csv_import_export.html' %}
{% endblock %}
{% block table_container %}
@@ -59,8 +36,6 @@
- {% include 'assets/_system_user_import_modal.html' %}
- {% include 'assets/_system_user_update_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
{% endblock %}
diff --git a/jumpserver/jumpserver/apps/assets/urls/api_urls.py b/jumpserver/jumpserver/apps/assets/urls/api_urls.py
index 35651429bfbe39cb38bf9c47ff49be0b60d22123..653b714472ba7ea138e6dc831b0f2efbb5851000 100644
--- a/jumpserver/jumpserver/apps/assets/urls/api_urls.py
+++ b/jumpserver/jumpserver/apps/assets/urls/api_urls.py
@@ -12,6 +12,7 @@ app_name = 'assets'
router = BulkRouter()
router.register(r'assets', api.AssetViewSet, 'asset')
+router.register(r'platforms', api.AssetPlatformViewSet, 'platform')
router.register(r'admin-users', api.AdminUserViewSet, 'admin-user')
router.register(r'system-users', api.SystemUserViewSet, 'system-user')
router.register(r'labels', api.LabelViewSet, 'label')
@@ -23,6 +24,8 @@ router.register(r'asset-users', api.AssetUserViewSet, 'asset-user')
router.register(r'asset-users-info', api.AssetUserExportViewSet, 'asset-user-info')
router.register(r'gathered-users', api.GatheredUserViewSet, 'gathered-user')
router.register(r'favorite-assets', api.FavoriteAssetViewSet, 'favorite-asset')
+router.register(r'system-users-assets-relations', api.SystemUserAssetRelationViewSet, 'system-users-assets-relation')
+router.register(r'system-users-nodes-relations', api.SystemUserNodeRelationViewSet, 'system-users-nodes-relation')
cmd_filter_router = routers.NestedDefaultRouter(router, r'cmd-filters', lookup='filter')
cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-rule')
@@ -35,6 +38,8 @@ urlpatterns = [
api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'),
path('assets/
/gateway/',
api.AssetGatewayApi.as_view(), name='asset-gateway'),
+ path('assets//platform/',
+ api.AssetPlatformRetrieveApi.as_view(), name='asset-platform-detail'),
path('asset-users/auth-info/',
api.AssetUserAuthInfoApi.as_view(), name='asset-user-auth-info'),
diff --git a/jumpserver/jumpserver/apps/assets/urls/views_urls.py b/jumpserver/jumpserver/apps/assets/urls/views_urls.py
index 71483a4df9355cc6e0c914ee027fd45dc1d8e0a7..eec9dcc0dfed3ad5cede418eed3bb6e3fcd8f4d4 100644
--- a/jumpserver/jumpserver/apps/assets/urls/views_urls.py
+++ b/jumpserver/jumpserver/apps/assets/urls/views_urls.py
@@ -16,6 +16,11 @@ urlpatterns = [
# Asset user view
path('asset//asset-user/', views.AssetUserListView.as_view(), name='asset-user-list'),
+ path('platform/', views.PlatformListView.as_view(), name='platform-list'),
+ path('platform/create/', views.PlatformCreateView.as_view(), name='platform-create'),
+ path('platform//', views.PlatformDetailView.as_view(), name='platform-detail'),
+ path('platform//update/', views.PlatformUpdateView.as_view(), name='platform-update'),
+
# User asset view
path('user-asset/', views.UserAssetListView.as_view(), name='user-asset-list'),
diff --git a/jumpserver/jumpserver/apps/assets/utils.py b/jumpserver/jumpserver/apps/assets/utils.py
index e0b316ad7c20c73869da236ae102a7ae05f4d607..eaf3d502a4a6e9b0ed033ad44e74464e70c6c427 100644
--- a/jumpserver/jumpserver/apps/assets/utils.py
+++ b/jumpserver/jumpserver/apps/assets/utils.py
@@ -84,11 +84,15 @@ class TreeService(Tree):
children_ids = self.all_children_ids(nid, with_self=with_self)
return [self.get_node(i, deep=deep) for i in children_ids]
- def ancestors(self, nid, with_self=False, deep=False):
+ def ancestors_ids(self, nid, with_self=True):
ancestor_ids = list(self.rsearch(nid))
ancestor_ids.pop()
if not with_self:
ancestor_ids.pop(0)
+ return ancestor_ids
+
+ def ancestors(self, nid, with_self=False, deep=False):
+ ancestor_ids = self.ancestors_ids(nid, with_self=with_self)
return [self.get_node(i, deep=deep) for i in ancestor_ids]
def get_node_full_tag(self, nid):
diff --git a/jumpserver/jumpserver/apps/assets/views/__init__.py b/jumpserver/jumpserver/apps/assets/views/__init__.py
index 04fc6c31ca9e5f6b0708972bb5e9c02a5ea7b275..74055a76cac2aee016a60c80e6e7441670a3a80b 100644
--- a/jumpserver/jumpserver/apps/assets/views/__init__.py
+++ b/jumpserver/jumpserver/apps/assets/views/__init__.py
@@ -1,5 +1,6 @@
# coding:utf-8
from .asset import *
+from .platform import *
from .system_user import *
from .admin_user import *
from .label import *
diff --git a/jumpserver/jumpserver/apps/assets/views/asset.py b/jumpserver/jumpserver/apps/assets/views/asset.py
index f08c08db857a70231cb9912ccf95a08fd54cb4c3..63179f4f8f2fb41feac7ae7edbdb201c66e0b5cd 100644
--- a/jumpserver/jumpserver/apps/assets/views/asset.py
+++ b/jumpserver/jumpserver/apps/assets/views/asset.py
@@ -74,7 +74,7 @@ class UserAssetListView(PermissionsMixin, TemplateView):
class AssetCreateView(PermissionsMixin, FormMixin, TemplateView):
model = Asset
- form_class = forms.AssetCreateForm
+ form_class = forms.AssetCreateUpdateForm
template_name = 'assets/asset_create.html'
success_url = reverse_lazy('assets:asset-list')
permission_classes = [IsOrgAdmin]
@@ -110,7 +110,7 @@ class AssetCreateView(PermissionsMixin, FormMixin, TemplateView):
class AssetUpdateView(PermissionsMixin, UpdateView):
model = Asset
- form_class = forms.AssetUpdateForm
+ form_class = forms.AssetCreateUpdateForm
template_name = 'assets/asset_update.html'
success_url = reverse_lazy('assets:asset-list')
permission_classes = [IsOrgAdmin]
diff --git a/jumpserver/jumpserver/apps/assets/views/domain.py b/jumpserver/jumpserver/apps/assets/views/domain.py
index 7b4dcfcce16078d65f3f4fe76a0ce8101873922e..ad7fad1b6ab400745beddb4d8ff845c25e4fa329 100644
--- a/jumpserver/jumpserver/apps/assets/views/domain.py
+++ b/jumpserver/jumpserver/apps/assets/views/domain.py
@@ -1,13 +1,14 @@
# -*- coding: utf-8 -*-
#
-from django.views.generic import TemplateView, CreateView, \
- UpdateView, DeleteView, DetailView
+from django.views.generic import (
+ TemplateView, CreateView, UpdateView, DeleteView, DetailView
+)
from django.views.generic.detail import SingleObjectMixin
from django.utils.translation import ugettext_lazy as _
from django.urls import reverse_lazy, reverse
-from common.permissions import PermissionsMixin ,IsOrgAdmin
+from common.permissions import PermissionsMixin, IsOrgAdmin
from common.const import create_success_msg, update_success_msg
from common.utils import get_object_or_none
from ..models import Domain, Gateway
diff --git a/jumpserver/jumpserver/apps/assets/views/platform.py b/jumpserver/jumpserver/apps/assets/views/platform.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e3aff49ccf2304b383f5cf10bcbd80cbc9c53cf
--- /dev/null
+++ b/jumpserver/jumpserver/apps/assets/views/platform.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+from django.views import generic
+from django.utils.translation import ugettext as _
+
+from common.permissions import PermissionsMixin, IsSuperUser
+from ..models import Platform
+from ..forms import PlatformForm, PlatformMetaForm
+
+__all__ = [
+ 'PlatformListView', 'PlatformUpdateView', 'PlatformCreateView',
+ 'PlatformDetailView',
+]
+
+
+class PlatformListView(PermissionsMixin, generic.TemplateView):
+ template_name = 'assets/platform_list.html'
+ permission_classes = (IsSuperUser,)
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context.update({
+ 'app': _('Assets'),
+ 'action': _("Platform list"),
+ })
+ return context
+
+
+class PlatformCreateView(PermissionsMixin, generic.CreateView):
+ form_class = PlatformForm
+ permission_classes = (IsSuperUser,)
+ template_name = 'assets/platform_create_update.html'
+ model = Platform
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ meta_form = PlatformMetaForm()
+ context.update({
+ 'app': _('Assets'),
+ 'action': _("Create platform"),
+ 'meta_form': meta_form,
+ })
+ return context
+
+
+class PlatformUpdateView(generic.UpdateView):
+ form_class = PlatformForm
+ permission_classes = (IsSuperUser,)
+ model = Platform
+ template_name = 'assets/platform_create_update.html'
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ meta_form = PlatformMetaForm(initial=self.object.meta)
+ context.update({
+ 'app': _('Assets'),
+ 'action': _("Update platform"),
+ 'type': 'update',
+ 'meta_form': meta_form,
+ })
+ return context
+
+
+class PlatformDetailView(generic.DetailView):
+ permission_classes = (IsSuperUser,)
+ model = Platform
+ template_name = 'assets/platform_detail.html'
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context.update({
+ 'app': _('Assets'),
+ 'action': _("Platform detail"),
+ })
+ return context
diff --git a/jumpserver/jumpserver/apps/audits/api.py b/jumpserver/jumpserver/apps/audits/api.py
index 4d7165b4bf116b424afc9abe984b2bc06a0dce2e..3677a8e8e8dbedc409aaddd14495f81c5e1656c2 100644
--- a/jumpserver/jumpserver/apps/audits/api.py
+++ b/jumpserver/jumpserver/apps/audits/api.py
@@ -11,4 +11,4 @@ class FTPLogViewSet(OrgModelViewSet):
model = FTPLog
serializer_class = FTPLogSerializer
permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor,)
-
+ http_method_names = ['get', 'post', 'head', 'options']
diff --git a/jumpserver/jumpserver/apps/audits/migrations/0007_auto_20191202_1010.py b/jumpserver/jumpserver/apps/audits/migrations/0007_auto_20191202_1010.py
new file mode 100644
index 0000000000000000000000000000000000000000..3c355ff69be79c4cab3303526c976a670db1d75e
--- /dev/null
+++ b/jumpserver/jumpserver/apps/audits/migrations/0007_auto_20191202_1010.py
@@ -0,0 +1,28 @@
+# Generated by Django 2.2.7 on 2019-12-02 02:10
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('audits', '0006_auto_20190726_1753'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='ftplog',
+ name='remote_addr',
+ field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Remote addr'),
+ ),
+ migrations.AlterField(
+ model_name='operatelog',
+ name='remote_addr',
+ field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Remote addr'),
+ ),
+ migrations.AlterField(
+ model_name='passwordchangelog',
+ name='remote_addr',
+ field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Remote addr'),
+ ),
+ ]
diff --git a/jumpserver/jumpserver/apps/audits/models.py b/jumpserver/jumpserver/apps/audits/models.py
index 5b53d1c85f90b7e3603a4a025ecd0aafefc58fe1..81866bb1dae1ddcbe1b113895ec7287923e04bb6 100644
--- a/jumpserver/jumpserver/apps/audits/models.py
+++ b/jumpserver/jumpserver/apps/audits/models.py
@@ -16,7 +16,7 @@ __all__ = [
class FTPLog(OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
user = models.CharField(max_length=128, verbose_name=_('User'))
- remote_addr = models.CharField(max_length=15, verbose_name=_("Remote addr"), blank=True, null=True)
+ remote_addr = models.CharField(max_length=128, verbose_name=_("Remote addr"), blank=True, null=True)
asset = models.CharField(max_length=1024, verbose_name=_("Asset"))
system_user = models.CharField(max_length=128, verbose_name=_("System user"))
operate = models.CharField(max_length=16, verbose_name=_("Operate"))
@@ -39,7 +39,7 @@ class OperateLog(OrgModelMixin):
action = models.CharField(max_length=16, choices=ACTION_CHOICES, verbose_name=_("Action"))
resource_type = models.CharField(max_length=64, verbose_name=_("Resource Type"))
resource = models.CharField(max_length=128, verbose_name=_("Resource"))
- remote_addr = models.CharField(max_length=15, verbose_name=_("Remote addr"), blank=True, null=True)
+ remote_addr = models.CharField(max_length=128, verbose_name=_("Remote addr"), blank=True, null=True)
datetime = models.DateTimeField(auto_now=True)
def __str__(self):
@@ -50,7 +50,7 @@ class PasswordChangeLog(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
user = models.CharField(max_length=128, verbose_name=_('User'))
change_by = models.CharField(max_length=128, verbose_name=_("Change by"))
- remote_addr = models.CharField(max_length=15, verbose_name=_("Remote addr"), blank=True, null=True)
+ remote_addr = models.CharField(max_length=128, verbose_name=_("Remote addr"), blank=True, null=True)
datetime = models.DateTimeField(auto_now=True)
def __str__(self):
@@ -110,5 +110,14 @@ class UserLoginLog(models.Model):
login_logs = login_logs.filter(username__in=username_list)
return login_logs
+ @property
+ def reason_display(self):
+ from authentication.errors import reason_choices, old_reason_choices
+ reason = reason_choices.get(self.reason)
+ if reason:
+ return reason
+ reason = old_reason_choices.get(self.reason, self.reason)
+ return reason
+
class Meta:
ordering = ['-datetime', 'username']
diff --git a/jumpserver/jumpserver/apps/audits/signals_handler.py b/jumpserver/jumpserver/apps/audits/signals_handler.py
index 0f201b464a704fb614d387ed66f0e6329a1497a6..dab56fa5ca4d272aa9d48123cef5cf9897cb7ae2 100644
--- a/jumpserver/jumpserver/apps/audits/signals_handler.py
+++ b/jumpserver/jumpserver/apps/audits/signals_handler.py
@@ -4,18 +4,22 @@
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.db import transaction
+from django.utils import timezone
from rest_framework.renderers import JSONRenderer
+from rest_framework.request import Request
from jumpserver.utils import current_request
from common.utils import get_request_ip, get_logger, get_syslogger
from users.models import User
+from users.signals import post_user_change_password
+from authentication.signals import post_auth_failed, post_auth_success
from terminal.models import Session, Command
-from terminal.backends.command.serializers import SessionCommandSerializer
+from common.utils.encode import model_to_json
+from .utils import write_login_log
from . import models
-from . import serializers
logger = get_logger(__name__)
-sys_logger = get_syslogger("audits")
+sys_logger = get_syslogger(__name__)
json_render = JSONRenderer()
@@ -23,6 +27,8 @@ MODELS_NEED_RECORD = (
'User', 'UserGroup', 'Asset', 'Node', 'AdminUser', 'SystemUser',
'Domain', 'Gateway', 'Organization', 'AssetPermission', 'CommandFilter',
'CommandFilterRule', 'License', 'Setting', 'Account', 'SyncInstanceTask',
+ 'Platform', 'ChangeAuthPlan', 'GatherUserTask',
+ 'RemoteApp', 'RemoteAppPermission', 'DatabaseApp', 'DatabaseAppPermission',
)
@@ -47,8 +53,11 @@ def create_operate_log(action, sender, resource):
logger.error("Create operate log error: {}".format(e))
-@receiver(post_save, dispatch_uid="my_unique_identifier")
-def on_object_created_or_update(sender, instance=None, created=False, **kwargs):
+@receiver(post_save)
+def on_object_created_or_update(sender, instance=None, created=False, update_fields=None, **kwargs):
+ if instance._meta.object_name == 'User' and \
+ update_fields and 'last_login' in update_fields:
+ return
if created:
action = models.OperateLog.ACTION_CREATE
else:
@@ -56,46 +65,79 @@ def on_object_created_or_update(sender, instance=None, created=False, **kwargs):
create_operate_log(action, sender, instance)
-@receiver(post_delete, dispatch_uid="my_unique_identifier")
+@receiver(post_delete)
def on_object_delete(sender, instance=None, **kwargs):
create_operate_log(models.OperateLog.ACTION_DELETE, sender, instance)
-@receiver(post_save, sender=User, dispatch_uid="my_unique_identifier")
-def on_user_change_password(sender, instance=None, **kwargs):
- if hasattr(instance, '_set_password'):
- if not current_request or not current_request.user.is_authenticated:
- return
- with transaction.atomic():
- models.PasswordChangeLog.objects.create(
- user=instance, change_by=current_request.user,
- remote_addr=get_request_ip(current_request),
- )
+@receiver(post_user_change_password, sender=User)
+def on_user_change_password(sender, user=None, **kwargs):
+ if not current_request:
+ remote_addr = '127.0.0.1'
+ change_by = 'System'
+ else:
+ remote_addr = get_request_ip(current_request)
+ if not current_request.user.is_authenticated:
+ change_by = str(user)
+ else:
+ change_by = str(current_request.user)
+ with transaction.atomic():
+ models.PasswordChangeLog.objects.create(
+ user=str(user), change_by=change_by,
+ remote_addr=remote_addr,
+ )
def on_audits_log_create(sender, instance=None, **kwargs):
if sender == models.UserLoginLog:
category = "login_log"
- serializer = serializers.LoginLogSerializer
elif sender == models.FTPLog:
- serializer = serializers.FTPLogSerializer
category = "ftp_log"
elif sender == models.OperateLog:
category = "operation_log"
- serializer = serializers.OperateLogSerializer
elif sender == models.PasswordChangeLog:
category = "password_change_log"
- serializer = serializers.PasswordChangeLogSerializer
elif sender == Session:
category = "host_session_log"
- serializer = serializers.SessionAuditSerializer
elif sender == Command:
category = "session_command_log"
- serializer = SessionCommandSerializer
else:
return
- s = serializer(instance=instance)
- data = json_render.render(s.data).decode(errors='ignore')
+ data = model_to_json(instance, indent=None)
msg = "{} - {}".format(category, data)
sys_logger.info(msg)
+
+
+def generate_data(username, request):
+ user_agent = request.META.get('HTTP_USER_AGENT', '')
+ login_ip = get_request_ip(request) or '0.0.0.0'
+ if isinstance(request, Request):
+ login_type = request.META.get('HTTP_X_JMS_LOGIN_TYPE', '')
+ else:
+ login_type = 'W'
+
+ data = {
+ 'username': username,
+ 'ip': login_ip,
+ 'type': login_type,
+ 'user_agent': user_agent,
+ 'datetime': timezone.now()
+ }
+ return data
+
+
+@receiver(post_auth_success)
+def on_user_auth_success(sender, user, request, **kwargs):
+ logger.debug('User login success: {}'.format(user.username))
+ data = generate_data(user.username, request)
+ data.update({'mfa': int(user.mfa_enabled), 'status': True})
+ write_login_log(**data)
+
+
+@receiver(post_auth_failed)
+def on_user_auth_failed(sender, username, request, reason, **kwargs):
+ logger.debug('User login failed: {}'.format(username))
+ data = generate_data(username, request)
+ data.update({'reason': reason, 'status': False})
+ write_login_log(**data)
diff --git a/jumpserver/jumpserver/apps/audits/tasks.py b/jumpserver/jumpserver/apps/audits/tasks.py
index 90d8f47db1b0a5eb5b4f6619003389b5dd2a77ed..2dfe4a2dd3c80702f5e6ad4e2fe8194d81a86afd 100644
--- a/jumpserver/jumpserver/apps/audits/tasks.py
+++ b/jumpserver/jumpserver/apps/audits/tasks.py
@@ -6,7 +6,7 @@ from django.conf import settings
from celery import shared_task
from ops.celery.decorator import register_as_period_task
-from .models import UserLoginLog
+from .models import UserLoginLog, OperateLog
@register_as_period_task(interval=3600*24)
@@ -19,3 +19,15 @@ def clean_login_log_period():
days = 90
expired_day = now - datetime.timedelta(days=days)
UserLoginLog.objects.filter(datetime__lt=expired_day).delete()
+
+
+@register_as_period_task(interval=3600*24)
+@shared_task
+def clean_operation_log_period():
+ now = timezone.now()
+ try:
+ days = int(settings.LOGIN_LOG_KEEP_DAYS)
+ except ValueError:
+ days = 90
+ expired_day = now - datetime.timedelta(days=days)
+ OperateLog.objects.filter(datetime__lt=expired_day).delete()
diff --git a/jumpserver/jumpserver/apps/audits/templates/audits/ftp_log_list.html b/jumpserver/jumpserver/apps/audits/templates/audits/ftp_log_list.html
index 9e92f8481fb1467415c71910724cc1f7f1a53a18..e0399ca734c3d5fedb83e6990b39a5f5599974de 100644
--- a/jumpserver/jumpserver/apps/audits/templates/audits/ftp_log_list.html
+++ b/jumpserver/jumpserver/apps/audits/templates/audits/ftp_log_list.html
@@ -5,8 +5,6 @@
{% load common_tags %}
{% block custom_head_css_js %}
-
-
{% endblock %}
diff --git a/jumpserver/jumpserver/apps/audits/templates/audits/login_log_list.html b/jumpserver/jumpserver/apps/audits/templates/audits/login_log_list.html
index 151fccb13a1d912f18e49b65c8cca670a05778dd..1ac74d31127e74f1eaed2ec3db2130a8e02de919 100644
--- a/jumpserver/jumpserver/apps/audits/templates/audits/login_log_list.html
+++ b/jumpserver/jumpserver/apps/audits/templates/audits/login_log_list.html
@@ -78,7 +78,7 @@
{{ login_log.ip }}
{{ login_log.city }}
{{ login_log.get_mfa_display }}
- {% trans login_log.reason %}
+ {{ login_log.reason_display }}
{{ login_log.get_status_display }}
{{ login_log.datetime }}
@@ -102,47 +102,47 @@
{% block custom_foot_js %}
-
-
+
+ })
+
{% endblock %}
diff --git a/jumpserver/jumpserver/apps/audits/templates/audits/operate_log_list.html b/jumpserver/jumpserver/apps/audits/templates/audits/operate_log_list.html
index 31e219a85f8d4a0199322b4778b65def7319a992..fdc03ce359243496a785cecf2dbf93e1abe0dc70 100644
--- a/jumpserver/jumpserver/apps/audits/templates/audits/operate_log_list.html
+++ b/jumpserver/jumpserver/apps/audits/templates/audits/operate_log_list.html
@@ -5,8 +5,6 @@
{% load common_tags %}
{% block custom_head_css_js %}
-
-
@@ -69,30 +76,34 @@