[Feature] 增加上传replay log的api

pull/828/merge
ibuler 2017-11-22 10:54:59 +08:00
parent 7f9ce57318
commit 818ead85ca
13 changed files with 59 additions and 20 deletions

1
.gitignore vendored
View File

@ -24,3 +24,4 @@ tags
jumpserver.iml jumpserver.iml
.python-version .python-version
tmp/* tmp/*
sessions/*

View File

@ -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, \
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):
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):
) )
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):
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)

View File

@ -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):
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)

View File

@ -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:

View File

@ -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(){
], ],
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);

View File

@ -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>

View File

@ -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

View File

@ -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() {

View File

@ -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() {

View File

@ -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(),

Binary file not shown.

View File

@ -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"

View File

@ -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