Commit 818ead85 authored by ibuler's avatar ibuler

[Feature] 增加上传replay log的api

parent 7f9ce573
...@@ -24,3 +24,4 @@ tags ...@@ -24,3 +24,4 @@ tags
jumpserver.iml jumpserver.iml
.python-version .python-version
tmp/* tmp/*
sessions/*
...@@ -2,11 +2,15 @@ ...@@ -2,11 +2,15 @@
# #
from collections import OrderedDict from collections import OrderedDict
import copy import copy
import logging
import os
from rest_framework import viewsets, serializers from rest_framework import viewsets, serializers
from rest_framework.views import APIView, Response from rest_framework.views import APIView, Response
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils import timezone from django.utils import timezone
from django.conf import settings
from .models import Terminal, TerminalStatus, TerminalSession, TerminalTask from .models import Terminal, TerminalStatus, TerminalSession, TerminalTask
from .serializers import TerminalSerializer, TerminalStatusSerializer, \ from .serializers import TerminalSerializer, TerminalStatusSerializer, \
...@@ -15,6 +19,8 @@ from .hands import IsSuperUserOrAppUser, IsAppUser, ProxyLog, \ ...@@ -15,6 +19,8 @@ from .hands import IsSuperUserOrAppUser, IsAppUser, ProxyLog, \
IsSuperUserOrAppUserOrUserReadonly IsSuperUserOrAppUserOrUserReadonly
from common.utils import get_object_or_none from common.utils import get_object_or_none
logger = logging.getLogger(__file__)
class TerminalViewSet(viewsets.ModelViewSet): class TerminalViewSet(viewsets.ModelViewSet):
queryset = Terminal.objects.filter(is_deleted=False) queryset = Terminal.objects.filter(is_deleted=False)
...@@ -62,20 +68,28 @@ class TerminalStatusViewSet(viewsets.ModelViewSet): ...@@ -62,20 +68,28 @@ class TerminalStatusViewSet(viewsets.ModelViewSet):
session_serializer_class = TerminalSessionSerializer session_serializer_class = TerminalSessionSerializer
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
self.handle_sessions()
return super().create(request, *args, **kwargs)
def handle_sessions(self):
sessions_active = [] 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 session_data["terminal"] = self.request.user.terminal.id
_id = session_data["id"] _id = session_data["id"]
session = get_object_or_none(TerminalSession, id=_id) session = get_object_or_none(TerminalSession, id=_id)
if session: if session:
serializer = TerminalSessionSerializer(data=session_data, instance=session) serializer = TerminalSessionSerializer(data=session_data,
instance=session)
else: else:
serializer = TerminalSessionSerializer(data=session_data) serializer = TerminalSessionSerializer(data=session_data)
if serializer.is_valid(): if serializer.is_valid():
serializer.save() 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_active.append(session_data["id"])
sessions_in_db_active = TerminalSession.objects.filter( sessions_in_db_active = TerminalSession.objects.filter(
...@@ -83,13 +97,11 @@ class TerminalStatusViewSet(viewsets.ModelViewSet): ...@@ -83,13 +97,11 @@ class TerminalStatusViewSet(viewsets.ModelViewSet):
) )
for session in sessions_in_db_active: 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.is_finished = True
session.date_end = timezone.now() session.date_end = timezone.now()
session.save() session.save()
return super().create(request, *args, **kwargs)
def get_queryset(self): def get_queryset(self):
terminal_id = self.kwargs.get("terminal", None) terminal_id = self.kwargs.get("terminal", None)
if terminal_id: if terminal_id:
...@@ -135,3 +147,29 @@ class TerminalTaskViewSet(viewsets.ModelViewSet): ...@@ -135,3 +147,29 @@ class TerminalTaskViewSet(viewsets.ModelViewSet):
terminal = self.request.user.terminal terminal = self.request.user.terminal
self.queryset = terminal.terminalstatus_set.all() self.queryset = terminal.terminalstatus_set.all()
return self.queryset 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)
...@@ -69,9 +69,6 @@ class TerminalStatus(models.Model): ...@@ -69,9 +69,6 @@ class TerminalStatus(models.Model):
class Meta: class Meta:
db_table = 'terminal_status' db_table = 'terminal_status'
# def __str__(self):
# return "<{} status>".format(self.terminal.name)
class TerminalSession(models.Model): class TerminalSession(models.Model):
LOGIN_FROM_CHOICES = ( LOGIN_FROM_CHOICES = (
...@@ -85,6 +82,8 @@ class TerminalSession(models.Model): ...@@ -85,6 +82,8 @@ class TerminalSession(models.Model):
system_user = models.CharField(max_length=128, verbose_name=_("System User")) system_user = models.CharField(max_length=128, verbose_name=_("System User"))
login_from = models.CharField(max_length=2, choices=LOGIN_FROM_CHOICES, default="ST") login_from = models.CharField(max_length=2, choices=LOGIN_FROM_CHOICES, default="ST")
is_finished = models.BooleanField(default=False) 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")) terminal = models.IntegerField(null=True, verbose_name=_("Terminal"))
date_start = models.DateTimeField(verbose_name=_("Date Start")) date_start = models.DateTimeField(verbose_name=_("Date Start"))
date_end = models.DateTimeField(verbose_name=_("Date End"), null=True) date_end = models.DateTimeField(verbose_name=_("Date End"), null=True)
......
...@@ -19,11 +19,11 @@ class TerminalSerializer(serializers.ModelSerializer): ...@@ -19,11 +19,11 @@ class TerminalSerializer(serializers.ModelSerializer):
@staticmethod @staticmethod
def get_session_connected(obj): 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 @staticmethod
def get_is_alive(obj): 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): if log and timezone.now() - log.date_created < timezone.timedelta(seconds=600):
return True return True
else: else:
......
...@@ -79,7 +79,7 @@ $(document).ready(function(){ ...@@ -79,7 +79,7 @@ $(document).ready(function(){
var reject_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-del" data-id="99991937" data-name="99991938">{% trans "Reject" %}</a>' var reject_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-del" data-id="99991937" data-name="99991938">{% trans "Reject" %}</a>'
.replace('99991937', cellData) .replace('99991937', cellData)
.replace('99991938', rowData.name); .replace('99991938', rowData.name);
var connect_btn = '<a href="{% url "applications:terminal-connect" pk=99991937 %}"" class="btn btn-xs btn-warning btn-connect" >{% trans "Connect" %}</a> ' var connect_btn = '<a href="" class="btn btn-xs btn-warning btn-connect" >{% trans "Connect" %}</a> '
.replace('99991937', cellData); .replace('99991937', cellData);
if (rowData.is_accepted) { if (rowData.is_accepted) {
{% if user.is_superuser %} {% if user.is_superuser %}
...@@ -96,7 +96,7 @@ $(document).ready(function(){ ...@@ -96,7 +96,7 @@ $(document).ready(function(){
], ],
ajax_url: '{% url "api-applications:terminal-list" %}', ajax_url: '{% url "api-applications:terminal-list" %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "remote_addr" }, {data: "ssh_port"}, {data: "http_port"}, 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() op_html: $('#actions').html()
}; };
jumpserver.initDataTable(options); jumpserver.initDataTable(options);
......
...@@ -33,8 +33,8 @@ ...@@ -33,8 +33,8 @@
<h3>{% trans 'Info' %}</h3> <h3>{% trans 'Info' %}</h3>
{% bootstrap_field form.name layout="horizontal" %} {% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.remote_addr layout="horizontal" %} {% bootstrap_field form.remote_addr layout="horizontal" %}
{% bootstrap_field form.type layout="horizontal" %} {% bootstrap_field form.ssh_port layout="horizontal" %}
{% bootstrap_field form.url layout="horizontal" %} {% bootstrap_field form.http_port layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3> <h3>{% trans 'Other' %}</h3>
......
...@@ -12,11 +12,12 @@ app_name = 'applications' ...@@ -12,11 +12,12 @@ app_name = 'applications'
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register(r'v1/terminal/(?P<terminal>[0-9]+)?/?status', api.TerminalStatusViewSet, 'terminal-status') router.register(r'v1/terminal/(?P<terminal>[0-9]+)?/?status', api.TerminalStatusViewSet, 'terminal-status')
router.register(r'v1/terminal/(?P<terminal>[0-9]+)?/?sessions', api.TerminalSessionViewSet, 'terminal-sessions') router.register(r'v1/terminal/(?P<terminal>[0-9]+)?/?sessions', api.TerminalSessionViewSet, 'terminal-sessions')
router.register(r'v1/terminal$', api.TerminalViewSet, 'terminal') router.register(r'v1/terminal', api.TerminalViewSet, 'terminal')
urlpatterns = [ urlpatterns = [
# url(r'^v1/terminate/connection/$', api.TerminateConnectionView.as_view(), # url(r'^v1/terminate/connection/$', api.TerminateConnectionView.as_view(),
# name='terminate-connection') # name='terminate-connection')
url(r'^v1/sessions/(?P<pk>[0-9a-zA-Z\-_]+)/replay/$', api.SessionReplayAPI.as_view(), name='session-replay'),
] ]
urlpatterns += router.urls urlpatterns += router.urls
...@@ -123,7 +123,7 @@ ...@@ -123,7 +123,7 @@
window.location.reload() window.location.reload()
}, 300) }, 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'}); APIUpdateAttr({url: the_url, method: 'POST', body: JSON.stringify(data), success: success, success_message: 'Terminate success'});
} }
$(document).ready(function() { $(document).ready(function() {
......
...@@ -137,7 +137,7 @@ ...@@ -137,7 +137,7 @@
window.location.reload() window.location.reload()
}, 300) }, 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'}); APIUpdateAttr({url: the_url, method: 'POST', body: JSON.stringify(data), success: success, success_message: 'Terminate success'});
} }
$(document).ready(function() { $(document).ready(function() {
......
...@@ -10,7 +10,6 @@ urlpatterns = [ ...@@ -10,7 +10,6 @@ urlpatterns = [
name='proxy-log-online-list'), name='proxy-log-online-list'),
url(r'^proxy-log/(?P<pk>\d+)/$', views.ProxyLogDetailView.as_view(), url(r'^proxy-log/(?P<pk>\d+)/$', views.ProxyLogDetailView.as_view(),
name='proxy-log-detail'), name='proxy-log-detail'),
# url(r'^proxy-log/(?P<pk>\d+)/commands$', views.ProxyLogCommandsListView.as_view(), name='proxy-log-commands-list'),
url(r'^command-log/$', views.CommandLogListView.as_view(), url(r'^command-log/$', views.CommandLogListView.as_view(),
name='command-log-list'), name='command-log-list'),
url(r'^login-log/$', views.LoginLogListView.as_view(), url(r'^login-log/$', views.LoginLogListView.as_view(),
......
...@@ -1375,7 +1375,7 @@ msgstr "终止所选" ...@@ -1375,7 +1375,7 @@ msgstr "终止所选"
#: audits/views.py:72 audits/views.py:209 audits/views.py:263 #: audits/views.py:72 audits/views.py:209 audits/views.py:263
#: templates/_nav.html:56 #: templates/_nav.html:56
msgid "Audits" msgid "Audits"
msgstr "审计" msgstr "审计中心"
#: audits/views.py:73 audits/views.py:264 #: audits/views.py:73 audits/views.py:264
msgid "Proxy log list" msgid "Proxy log list"
......
...@@ -6,6 +6,7 @@ import errno ...@@ -6,6 +6,7 @@ import errno
if __name__ == "__main__": if __name__ == "__main__":
try: try:
os.makedirs('../logs') os.makedirs('../logs')
os.makedirs('../sessions')
except: except:
pass pass
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment