From 818ead85ca47c6617701b17dffaf163e961f9c02 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 22 Nov 2017 10:54:59 +0800 Subject: [PATCH] =?UTF-8?q?[Feature]=20=E5=A2=9E=E5=8A=A0=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0replay=20log=E7=9A=84api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + apps/applications/api.py | 50 +++++++++++++++--- apps/applications/models.py | 5 +- apps/applications/serializers.py | 4 +- .../templates/applications/terminal_list.html | 4 +- .../applications/terminal_update.html | 4 +- apps/applications/urls/api_urls.py | 3 +- .../audits/proxy_log_offline_list.html | 2 +- .../audits/proxy_log_online_list.html | 2 +- apps/audits/urls/views_urls.py | 1 - apps/locale/zh/LC_MESSAGES/django.mo | Bin 24195 -> 24201 bytes apps/locale/zh/LC_MESSAGES/django.po | 2 +- apps/manage.py | 1 + 13 files changed, 59 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index ac2d768f8..89b6e1384 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ tags jumpserver.iml .python-version tmp/* +sessions/* diff --git a/apps/applications/api.py b/apps/applications/api.py index 59696772c..1e2c38450 100644 --- a/apps/applications/api.py +++ b/apps/applications/api.py @@ -2,11 +2,15 @@ # from collections import OrderedDict import copy +import logging + +import os from rest_framework import viewsets, serializers from rest_framework.views import APIView, Response from rest_framework.permissions import AllowAny from django.shortcuts import get_object_or_404 from django.utils import timezone +from django.conf import settings from .models import Terminal, TerminalStatus, TerminalSession, TerminalTask from .serializers import TerminalSerializer, TerminalStatusSerializer, \ @@ -15,6 +19,8 @@ from .hands import IsSuperUserOrAppUser, IsAppUser, ProxyLog, \ IsSuperUserOrAppUserOrUserReadonly from common.utils import get_object_or_none +logger = logging.getLogger(__file__) + class TerminalViewSet(viewsets.ModelViewSet): queryset = Terminal.objects.filter(is_deleted=False) @@ -62,20 +68,28 @@ class TerminalStatusViewSet(viewsets.ModelViewSet): session_serializer_class = TerminalSessionSerializer def create(self, request, *args, **kwargs): + self.handle_sessions() + return super().create(request, *args, **kwargs) + + def handle_sessions(self): sessions_active = [] - for session_data in request.data.get("sessions", []): + for session_data in self.request.data.get("sessions", []): session_data["terminal"] = self.request.user.terminal.id _id = session_data["id"] session = get_object_or_none(TerminalSession, id=_id) if session: - serializer = TerminalSessionSerializer(data=session_data, instance=session) + serializer = TerminalSessionSerializer(data=session_data, + instance=session) else: serializer = TerminalSessionSerializer(data=session_data) if serializer.is_valid(): serializer.save() + else: + logger.error("session serializer is not valid {}".format( + serializer.errors)) - if session_data["is_finished"]: + if not session_data["is_finished"]: sessions_active.append(session_data["id"]) sessions_in_db_active = TerminalSession.objects.filter( @@ -83,13 +97,11 @@ class TerminalStatusViewSet(viewsets.ModelViewSet): ) for session in sessions_in_db_active: - if session.id not in sessions_active: + if str(session.id) not in sessions_active: session.is_finished = True session.date_end = timezone.now() session.save() - return super().create(request, *args, **kwargs) - def get_queryset(self): terminal_id = self.kwargs.get("terminal", None) if terminal_id: @@ -135,3 +147,29 @@ class TerminalTaskViewSet(viewsets.ModelViewSet): terminal = self.request.user.terminal self.queryset = terminal.terminalstatus_set.all() return self.queryset + + +class SessionReplayAPI(APIView): + permission_classes = (IsSuperUserOrAppUser,) + + def post(self, request, **kwargs): + session_id = kwargs.get("pk", None) + session = get_object_or_404(TerminalSession, id=session_id) + record_dir = settings.CONFIG.SESSION_RECORDE_DIR + date = session.date_start.strftime("%Y-%m-%d") + record_dir = os.path.join(record_dir, date) + record_filename = os.path.join(record_dir, str(session.id)) + + if not os.path.exists(record_dir): + os.makedirs(record_dir) + + archive_stream = request.data.get("archive") + if not archive_stream: + return Response("None file upload", status=400) + + with open(record_filename, 'wb') as f: + for chunk in archive_stream.chunks(): + f.write(chunk) + session.has_replay = True + session.save() + return Response({"session_id": session.id}, status=201) diff --git a/apps/applications/models.py b/apps/applications/models.py index 38e606498..adf3f1bbd 100644 --- a/apps/applications/models.py +++ b/apps/applications/models.py @@ -69,9 +69,6 @@ class TerminalStatus(models.Model): class Meta: db_table = 'terminal_status' - # def __str__(self): - # return "<{} status>".format(self.terminal.name) - class TerminalSession(models.Model): LOGIN_FROM_CHOICES = ( @@ -85,6 +82,8 @@ class TerminalSession(models.Model): system_user = models.CharField(max_length=128, verbose_name=_("System User")) login_from = models.CharField(max_length=2, choices=LOGIN_FROM_CHOICES, default="ST") is_finished = models.BooleanField(default=False) + has_replay = models.BooleanField(default=False, verbose_name=_("Replay")) + has_command = models.BooleanField(default=False, verbose_name=_("Command")) terminal = models.IntegerField(null=True, verbose_name=_("Terminal")) date_start = models.DateTimeField(verbose_name=_("Date Start")) date_end = models.DateTimeField(verbose_name=_("Date End"), null=True) diff --git a/apps/applications/serializers.py b/apps/applications/serializers.py index 6118ac62e..03667a369 100644 --- a/apps/applications/serializers.py +++ b/apps/applications/serializers.py @@ -19,11 +19,11 @@ class TerminalSerializer(serializers.ModelSerializer): @staticmethod def get_session_connected(obj): - return ProxyLog.objects.filter(terminal=obj.name, is_finished=False).count() + return TerminalSession.objects.filter(terminal=obj.id, is_finished=False) @staticmethod def get_is_alive(obj): - log = obj.terminalheatbeat_set.last() + log = obj.terminalstatus_set.last() if log and timezone.now() - log.date_created < timezone.timedelta(seconds=600): return True else: diff --git a/apps/applications/templates/applications/terminal_list.html b/apps/applications/templates/applications/terminal_list.html index bd3f72c0c..f85b2d5e7 100644 --- a/apps/applications/templates/applications/terminal_list.html +++ b/apps/applications/templates/applications/terminal_list.html @@ -79,7 +79,7 @@ $(document).ready(function(){ var reject_btn = '{% trans "Reject" %}' .replace('99991937', cellData) .replace('99991938', rowData.name); - var connect_btn = '{% trans "Connect" %} ' + var connect_btn = '{% trans "Connect" %} ' .replace('99991937', cellData); if (rowData.is_accepted) { {% if user.is_superuser %} @@ -96,7 +96,7 @@ $(document).ready(function(){ ], ajax_url: '{% url "api-applications:terminal-list" %}', columns: [{data: function(){return ""}}, {data: "name" }, {data: "remote_addr" }, {data: "ssh_port"}, {data: "http_port"}, - {data: "session_connected"}, {data: "is_alive" }, {data: 'is_alive'}, {data: "id"}], + {data: "session_connected"}, {data: "is_accepted" }, {data: 'is_alive'}, {data: "id"}], op_html: $('#actions').html() }; jumpserver.initDataTable(options); diff --git a/apps/applications/templates/applications/terminal_update.html b/apps/applications/templates/applications/terminal_update.html index d9621abaa..624dc6e7e 100644 --- a/apps/applications/templates/applications/terminal_update.html +++ b/apps/applications/templates/applications/terminal_update.html @@ -33,8 +33,8 @@

{% trans 'Info' %}

{% bootstrap_field form.name layout="horizontal" %} {% bootstrap_field form.remote_addr layout="horizontal" %} - {% bootstrap_field form.type layout="horizontal" %} - {% bootstrap_field form.url layout="horizontal" %} + {% bootstrap_field form.ssh_port layout="horizontal" %} + {% bootstrap_field form.http_port layout="horizontal" %}

{% trans 'Other' %}

diff --git a/apps/applications/urls/api_urls.py b/apps/applications/urls/api_urls.py index 160a66853..1169aaca8 100644 --- a/apps/applications/urls/api_urls.py +++ b/apps/applications/urls/api_urls.py @@ -12,11 +12,12 @@ app_name = 'applications' router = routers.DefaultRouter() router.register(r'v1/terminal/(?P[0-9]+)?/?status', api.TerminalStatusViewSet, 'terminal-status') router.register(r'v1/terminal/(?P[0-9]+)?/?sessions', api.TerminalSessionViewSet, 'terminal-sessions') -router.register(r'v1/terminal$', api.TerminalViewSet, 'terminal') +router.register(r'v1/terminal', api.TerminalViewSet, 'terminal') urlpatterns = [ # url(r'^v1/terminate/connection/$', api.TerminateConnectionView.as_view(), # name='terminate-connection') + url(r'^v1/sessions/(?P[0-9a-zA-Z\-_]+)/replay/$', api.SessionReplayAPI.as_view(), name='session-replay'), ] urlpatterns += router.urls diff --git a/apps/audits/templates/audits/proxy_log_offline_list.html b/apps/audits/templates/audits/proxy_log_offline_list.html index ffe695662..7dcac94b7 100644 --- a/apps/audits/templates/audits/proxy_log_offline_list.html +++ b/apps/audits/templates/audits/proxy_log_offline_list.html @@ -123,7 +123,7 @@ window.location.reload() }, 300) } - var the_url = "{% url 'api-applications:terminate-connection' %}"; + var the_url = ""; APIUpdateAttr({url: the_url, method: 'POST', body: JSON.stringify(data), success: success, success_message: 'Terminate success'}); } $(document).ready(function() { diff --git a/apps/audits/templates/audits/proxy_log_online_list.html b/apps/audits/templates/audits/proxy_log_online_list.html index 500fe68b3..099ab3fca 100644 --- a/apps/audits/templates/audits/proxy_log_online_list.html +++ b/apps/audits/templates/audits/proxy_log_online_list.html @@ -137,7 +137,7 @@ window.location.reload() }, 300) } - var the_url = "{% url 'api-applications:terminate-connection' %}"; + var the_url = ""; APIUpdateAttr({url: the_url, method: 'POST', body: JSON.stringify(data), success: success, success_message: 'Terminate success'}); } $(document).ready(function() { diff --git a/apps/audits/urls/views_urls.py b/apps/audits/urls/views_urls.py index 1ad6439b3..ce6fece85 100644 --- a/apps/audits/urls/views_urls.py +++ b/apps/audits/urls/views_urls.py @@ -10,7 +10,6 @@ urlpatterns = [ name='proxy-log-online-list'), url(r'^proxy-log/(?P\d+)/$', views.ProxyLogDetailView.as_view(), name='proxy-log-detail'), - # url(r'^proxy-log/(?P\d+)/commands$', views.ProxyLogCommandsListView.as_view(), name='proxy-log-commands-list'), url(r'^command-log/$', views.CommandLogListView.as_view(), name='command-log-list'), url(r'^login-log/$', views.LoginLogListView.as_view(), diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 855922a4c1be7f257f38036c48ab1b6d850cdbc0..51d965164f28a00125f52f6c2651008a4cd48ac1 100644 GIT binary patch delta 2714 zcmXxme@vBi6vy!g17>mc4zLZ)spXQyz(}I9G>R=&pkd+cgafzwQlll4zdvG<4PBmcafbH3+$zUTRJ@1Qx6 z)S5`@`*(U63xZ&KX%M7hJt}@5Gw`tEqh9Z|1NIxdkGk*he*Do6+smkaBlafhoC&X| zlm%fBClN1;7UtRoHWy>87oY}u(fJBnWot0TdY$8asJ`!^20Ls&LG}6E4wQvK@Dzz5 z5;44k+W5eVsBo6e#eDKbsD`_pZ?NrHK>ir2&n3*m5mdj-mC?TWHWzghPlr5c!*W|{ z*V{MkZhNo3jaUu3Py-!B9dy$9K~&$fjxRXAh^o8hd^qlf|7_}OQR7ThgISKV@nPaT z=gV!i-G&=A*;bW)) z9>+K?LG>wizTB=uowE_+eEx!39;T4k?}7ui3Duw#)vyz_vB&nI_6=eVCa?(qvI|#5 z18qa~+2yzq)u%-<=LhXPlwhX|{y=RQMZM{`^C_=K4P&VFOvm$Vw)1(8pTi95Ub1UY z`)W})wHHG@GoNQ2Qrr%4!dYlLI=Q=Qta+zrb-DG_jJi>z}U5t8z!e}8VLLIQe`D*(X zs(u&h!2Pxbb?}Fdk30U#@j28B{p$QblX-ss6HcV9j~dQFHCp6&Io1-FVipdd20d>t zp?;@V?HFpnJ2ts82vUgCQ2BJ!;8~cg@BgD-$g%nM8C2m5n2cqpgI9UI%Ih_#fp)TN`RFC?Hq!D#fO{jitsL#?z z&UZWRt>XSQX@W#P4xY%N5huwqfcL25j5Nfa^s24aM zy5LKD8rAR|s_>%YKkb;k>-Bhbbihoz0JVP!>T6c)xYE|4`qrZc3!8b+WF0Q(c0oVt zjS`N3KyA3-{1se8e9cbZ7zDY*OHp;TsDt*}5R-`YqYu4)vXX-SO*1o}OdUDhJNj*N K|L)TC%>Mz|_cp%( delta 2707 zcmXxmeN2^Q7{~Dk1Cjx%Px(c4!|= zY8_1aYHUiF8U#UoSrEtu%)s|B8xJ@>?D3Pf&whgsFz;JT!|&~|y^N}N!`?>iGw$(; zvLFnyNTjWd2J-AQTZkEq7oi$@-uZG{VQVmh@jA!5P<7u$HFm&$jH>gg?OPcJ!F&=! zBr@o_0tiDx*! z!dBYNsD|FLO*Zs+8|v!2oIep62E9)7qfT-bwee5(SLbg!9zhkli!n@F9leB4p&EDw zvv4-5PO0-N>>AWQ>oJS>FR0~V5{ccOu*Wu|3bdjMcA^&c*i)!={Wuc`a0&il3)Vyp zZAR7E?zjn6CsfS-K|2r2vC|WNLoK+8I_aqM6E<~iG@gk%@gp|h`5BJqV>aV2*j1>y zwWxJFG4cIx^gs(vW}w4ym+i5=n9cYZ)QNvV6&kk_HZ|@BG0~voe9UHkk>jOt?q401 zJ5lWkTTxfO4|P@VqY52#e8lky+lwmPj|F%hwf?sK8}+WFuZ!kQ$47}5uH*hyI8H*1 zRH5Q6j(0fT=Xk&4k5G*rvOTBZHd|>&~DW8*+RZRp%4vdsK&D~S>OLBJuuzQvU5=rpTlH)8MSel$16NugKBu2<7SKz zw>$pK<6qhz>=g|8DGqM&pp8nZq7Ij$3cljF+Sa1xZL^KG)pnvz-i=y+-1z}iLqFQ< z9=}`l|L=cw^jYU&;$5(FP#Z49Tztv-4X7*KWDiiQ=iKc?FIa*cVZSSEDxCWVhHIsCs))>-VD?JBT{K(a;k< zw_l?QorizYP>qExJgBn{Pw4i9Uet*O z9Dj#eaKZU2IE(n2o%{xy5|^On)uJ}qY4>6hvHtYZ>o1lRoZgt1Gxu~;*+V)10aNoZ AhX4Qo diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 1e68bc210..895c0c02d 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -1375,7 +1375,7 @@ msgstr "终止所选" #: audits/views.py:72 audits/views.py:209 audits/views.py:263 #: templates/_nav.html:56 msgid "Audits" -msgstr "审计" +msgstr "审计中心" #: audits/views.py:73 audits/views.py:264 msgid "Proxy log list" diff --git a/apps/manage.py b/apps/manage.py index d24c5fd38..4749e5693 100644 --- a/apps/manage.py +++ b/apps/manage.py @@ -6,6 +6,7 @@ import errno if __name__ == "__main__": try: os.makedirs('../logs') + os.makedirs('../sessions') except: pass