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 855922a4c..51d965164 100644 Binary files a/apps/locale/zh/LC_MESSAGES/django.mo and b/apps/locale/zh/LC_MESSAGES/django.mo differ 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