mirror of https://github.com/jumpserver/jumpserver
[Feature] 修改命令上传api
parent
26607bc327
commit
7910292e0f
|
@ -1,8 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import base64
|
||||
from collections import OrderedDict
|
||||
import copy
|
||||
import logging
|
||||
import tarfile
|
||||
|
||||
import os
|
||||
from rest_framework import viewsets, serializers
|
||||
|
@ -12,12 +14,13 @@ from django.shortcuts import get_object_or_404
|
|||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
|
||||
from common.utils import get_object_or_none
|
||||
from .models import Terminal, TerminalStatus, TerminalSession, TerminalTask
|
||||
from .serializers import TerminalSerializer, TerminalStatusSerializer, \
|
||||
TerminalSessionSerializer, TerminalTaskSerializer
|
||||
from .hands import IsSuperUserOrAppUser, IsAppUser, ProxyLog, \
|
||||
IsSuperUserOrAppUserOrUserReadonly
|
||||
from common.utils import get_object_or_none
|
||||
from .backends import get_command_store, get_replay_store, SessionCommandSerializer
|
||||
|
||||
logger = logging.getLogger(__file__)
|
||||
|
||||
|
@ -157,11 +160,14 @@ class SessionReplayAPI(APIView):
|
|||
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))
|
||||
record_dir = os.path.join(record_dir, date, str(session.id))
|
||||
record_filename = os.path.join(record_dir, "replay.tar.gz2")
|
||||
|
||||
if not os.path.exists(record_dir):
|
||||
os.makedirs(record_dir)
|
||||
if not os.path.isdir(record_dir):
|
||||
try:
|
||||
os.makedirs(record_dir)
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
archive_stream = request.data.get("archive")
|
||||
if not archive_stream:
|
||||
|
@ -173,3 +179,41 @@ class SessionReplayAPI(APIView):
|
|||
session.has_replay = True
|
||||
session.save()
|
||||
return Response({"session_id": session.id}, status=201)
|
||||
|
||||
|
||||
class SessionCommandViewSet(viewsets.ViewSet):
|
||||
"""接受app发送来的command log, 格式如下
|
||||
{
|
||||
"user": "admin",
|
||||
"asset": "localhost",
|
||||
"system_user": "web",
|
||||
"session": "xxxxxx",
|
||||
"input": "whoami",
|
||||
"output": "d2hvbWFp", # base64.b64encode(s)
|
||||
"timestamp": 1485238673.0
|
||||
}
|
||||
|
||||
"""
|
||||
command_store = get_command_store()
|
||||
serializer_class = SessionCommandSerializer
|
||||
permission_classes = (IsSuperUserOrAppUser,)
|
||||
|
||||
def get_queryset(self):
|
||||
self.command_store.all()
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
serializer = self.serializer_class(data=request.data, many=True)
|
||||
if serializer.is_valid():
|
||||
ok = self.command_store.bulk_save(serializer.validated_data)
|
||||
if ok:
|
||||
return Response("ok", status=201)
|
||||
else:
|
||||
return Response("save error", status=500)
|
||||
else:
|
||||
print(serializer.errors)
|
||||
return Response({"msg": "Not valid: {}".format(serializer.errors)}, status=401)
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
queryset = list(self.command_store.all())
|
||||
serializer = self.serializer_class(queryset, many=True)
|
||||
return Response(serializer.data)
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
from importlib import import_module
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from .command.serializers import SessionCommandSerializer
|
||||
|
||||
|
||||
def get_command_store():
|
||||
command_engine = import_module(settings.COMMAND_STORE_BACKEND)
|
||||
command_store = command_engine.CommandStore()
|
||||
return command_store
|
||||
|
||||
|
||||
def get_replay_store():
|
||||
replay_engine = import_module(settings.RECORD_STORE_BACKEND)
|
||||
replay_store = replay_engine.RecordStore()
|
||||
return replay_store
|
|
@ -0,0 +1,22 @@
|
|||
# coding: utf-8
|
||||
import abc
|
||||
|
||||
|
||||
class CommandBase(object):
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
@abc.abstractmethod
|
||||
def save(self, command):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def bulk_save(self, commands):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def filter(self, date_from=None, date_to=None, user=None,
|
||||
asset=None, system_user=None, command=None, session=None):
|
||||
pass
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
import datetime
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
from .base import CommandBase
|
||||
|
||||
|
||||
class CommandStore(CommandBase):
|
||||
|
||||
def __init__(self):
|
||||
from applications.models import SessionCommand
|
||||
self.model = SessionCommand
|
||||
|
||||
def save(self, command):
|
||||
"""
|
||||
保存命令到数据库
|
||||
"""
|
||||
|
||||
self.model.objects.create(
|
||||
user=command["user"], asset=command["asset"],
|
||||
system_user=command["system_user"], input=command["input"],
|
||||
output=command["output"], session=command["session"],
|
||||
timestamp=command["timestamp"]
|
||||
)
|
||||
|
||||
def bulk_save(self, commands):
|
||||
"""
|
||||
批量保存命令到数据库, command的顺序和save中一致
|
||||
"""
|
||||
_commands = []
|
||||
for c in commands:
|
||||
_commands.append(self.model(
|
||||
user=c["user"], asset=c["asset"], system_user=c["system_user"],
|
||||
input=c["input"], output=c["output"], session=c["session"],
|
||||
timestamp=c["timestamp"]
|
||||
))
|
||||
return self.model.objects.bulk_create(_commands)
|
||||
|
||||
def filter(self, date_from=None, date_to=None, user=None,
|
||||
asset=None, system_user=None, _input=None, session=None):
|
||||
filter_kwargs = {}
|
||||
|
||||
if date_from:
|
||||
filter_kwargs['timestamp__gte'] = int(date_from.timestamp())
|
||||
else:
|
||||
week_ago = timezone.now() - datetime.timedelta(days=7)
|
||||
filter_kwargs['timestamp__gte'] = int(week_ago.timestamp())
|
||||
if date_to:
|
||||
filter_kwargs['timestamp__lte'] = int(date_to.timestamp())
|
||||
else:
|
||||
filter_kwargs['timestamp__lte'] = int(timezone.now().timestamp())
|
||||
if user:
|
||||
filter_kwargs['user'] = user
|
||||
if asset:
|
||||
filter_kwargs['asset'] = asset
|
||||
if system_user:
|
||||
filter_kwargs['system_user'] = system_user
|
||||
if _input:
|
||||
filter_kwargs['input__icontains'] = _input
|
||||
if session:
|
||||
filter_kwargs['session'] = session
|
||||
|
||||
queryset = self.model.objects.filter(**filter_kwargs)
|
||||
return queryset
|
||||
|
||||
def all(self):
|
||||
"""返回所有数据"""
|
||||
return self.model.objects.iterator()
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import uuid
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class AbstractSessionCommand(models.Model):
|
||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
user = models.CharField(max_length=64, verbose_name=_("User"))
|
||||
asset = models.CharField(max_length=128, verbose_name=_("Asset"))
|
||||
system_user = models.CharField(max_length=64, verbose_name=_("System user"))
|
||||
input = models.CharField(max_length=128, db_index=True, verbose_name=_("Input"))
|
||||
output = models.CharField(max_length=1024, verbose_name=_("Output"))
|
||||
session = models.CharField(max_length=36, db_index=True, verbose_name=_("Session"))
|
||||
timestamp = models.IntegerField(db_index=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def __str__(self):
|
||||
return self.input
|
|
@ -0,0 +1,16 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
class SessionCommandSerializer(serializers.Serializer):
|
||||
"""使用这个类作为基础Command Log Serializer类, 用来序列化"""
|
||||
|
||||
id = serializers.UUIDField(read_only=True)
|
||||
user = serializers.CharField(max_length=64)
|
||||
asset = serializers.CharField(max_length=128)
|
||||
system_user = serializers.CharField(max_length=64)
|
||||
input = serializers.CharField(max_length=128)
|
||||
output = serializers.CharField(max_length=1024)
|
||||
session = serializers.CharField(max_length=36)
|
||||
timestamp = serializers.IntegerField()
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# coding: utf-8
|
||||
import abc
|
||||
|
||||
|
||||
class RecordBase(object):
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
@abc.abstractmethod
|
||||
def save(self, proxy_log_id, output, timestamp):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def filter(self, date_from_ts=None, proxy_log_id=None):
|
||||
pass
|
|
@ -0,0 +1,31 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
|
||||
from .base import RecordBase
|
||||
from audits.models import RecordLog
|
||||
|
||||
|
||||
class RecordStore(RecordBase):
|
||||
model = RecordLog
|
||||
queryset = []
|
||||
|
||||
def save(self, proxy_log_id, output, timestamp):
|
||||
return self.model.objects.create(
|
||||
proxy_log_id=proxy_log_id, output=output, timestamp=timestamp
|
||||
)
|
||||
|
||||
def filter(self, date_from_ts=None, proxy_log_id=''):
|
||||
filter_kwargs = {}
|
||||
|
||||
if date_from_ts:
|
||||
filter_kwargs['timestamp__gte'] = date_from_ts
|
||||
if proxy_log_id:
|
||||
filter_kwargs['proxy_log_id'] = proxy_log_id
|
||||
|
||||
if filter_kwargs:
|
||||
self.queryset = self.model.objects.filter(**filter_kwargs)
|
||||
return self.queryset
|
||||
|
||||
def all(self):
|
||||
"""返回所有数据"""
|
||||
return self.model.objects.all()
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
import base64
|
||||
from rest_framework import serializers
|
||||
from audits.models import RecordLog
|
||||
from audits.backends import record_store
|
||||
|
||||
|
||||
class RecordSerializer(serializers.ModelSerializer):
|
||||
"""使用这个类作为基础Command Log Serializer类, 用来序列化"""
|
||||
class Meta:
|
||||
model = RecordLog
|
||||
fields = '__all__'
|
||||
|
||||
def create(self, validated_data):
|
||||
try:
|
||||
output = validated_data['output']
|
||||
validated_data['output'] = base64.b64decode(output)
|
||||
except IndexError:
|
||||
pass
|
||||
return record_store.save(**dict(validated_data))
|
|
@ -6,6 +6,7 @@ from django.db import models
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from users.models import User
|
||||
from .backends.command.models import AbstractSessionCommand
|
||||
|
||||
|
||||
class Terminal(models.Model):
|
||||
|
@ -88,7 +89,7 @@ class TerminalSession(models.Model):
|
|||
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.UUIDField(null=True, verbose_name=_("Terminal"))
|
||||
date_start = models.DateTimeField(verbose_name=_("Date Start"))
|
||||
date_end = models.DateTimeField(verbose_name=_("Date End"), null=True)
|
||||
|
||||
|
@ -110,3 +111,9 @@ class TerminalTask(models.Model):
|
|||
|
||||
class Meta:
|
||||
db_table = "terminal_task"
|
||||
|
||||
|
||||
class SessionCommand(AbstractSessionCommand):
|
||||
|
||||
class Meta:
|
||||
db_table = "session_command"
|
||||
|
|
|
@ -28,9 +28,9 @@
|
|||
</th>
|
||||
<th class="text-center">{% trans 'Name' %}</th>
|
||||
<th class="text-center">{% trans 'Addr' %}</th>
|
||||
<th class="text-center">{% trans 'SSH Port' %}</th>
|
||||
<th class="text-center">{% trans 'Http Port' %}</th>
|
||||
<th class="text-center">{% trans 'Connected' %}</th>
|
||||
<th class="text-center">{% trans 'SSH port' %}</th>
|
||||
<th class="text-center">{% trans 'Http port' %}</th>
|
||||
<th class="text-center">{% trans 'Sessions' %}</th>
|
||||
<th class="text-center">{% trans 'Active' %}</th>
|
||||
<th class="text-center">{% trans 'Alive' %}</th>
|
||||
<th class="text-center">{% trans 'Action' %}</th>
|
||||
|
|
|
@ -13,10 +13,9 @@ 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]+)?/?sessions', api.TerminalSessionViewSet, 'terminal-sessions')
|
||||
router.register(r'v1/terminal', api.TerminalViewSet, 'terminal')
|
||||
router.register(r'v1/command', api.SessionCommandViewSet, 'command')
|
||||
|
||||
urlpatterns = [
|
||||
# url(r'^v1/terminate/connection/$', api.TerminateConnectionView.as_view(),
|
||||
# name='terminate-connection')
|
||||
url(r'^v1/sessions/(?P<pk>[0-9a-zA-Z\-_]+)/replay/$', api.SessionReplayAPI.as_view(), name='session-replay'),
|
||||
]
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ router.register(r'v1/system-user', api.SystemUserViewSet, 'system-user')
|
|||
|
||||
urlpatterns = [
|
||||
url(r'^v1/assets-bulk/$', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'),
|
||||
url(r'^v1/system-user/(?P<pk>[0-9]+)/auth-info/', api.SystemUserAuthInfoApi.as_view(),
|
||||
url(r'^v1/system-user/(?P<pk>[0-9a-zA-Z\-]+)/auth-info/', api.SystemUserAuthInfoApi.as_view(),
|
||||
name='system-user-auth-info'),
|
||||
url(r'^v1/assets/(?P<pk>[0-9a-zA-Z\-]+)/groups/$',
|
||||
api.AssetUpdateGroupApi.as_view(), name='asset-update-group'),
|
||||
|
|
|
@ -8,7 +8,7 @@ app_name = 'audits'
|
|||
router = routers.DefaultRouter()
|
||||
router.register(r'v1/proxy-log', api.ProxyLogViewSet, 'proxy-log')
|
||||
router.register(r'v1/command-log', api.CommandLogViewSet, 'command-log')
|
||||
router.register(r'v1/record-log', api.RecordLogViewSet, 'record-log')
|
||||
router.register(r'v1/replay-log', api.RecordLogViewSet, 'replay-log')
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^v1/proxy-log/receive/$', api.ProxyLogReceiveView.as_view(),
|
||||
|
|
|
@ -363,8 +363,8 @@ CAPTCHA_FOREGROUND_COLOR = '#001100'
|
|||
CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_dots',)
|
||||
CAPTCHA_TEST_MODE = CONFIG.CAPTCHA_TEST_MODE
|
||||
|
||||
COMMAND_STORE_BACKEND = 'audits.backends.command.db'
|
||||
RECORD_STORE_BACKEND = 'audits.backends.record.db'
|
||||
COMMAND_STORE_BACKEND = 'applications.backends.command.db'
|
||||
RECORD_STORE_BACKEND = 'applications.backends.replay.db'
|
||||
|
||||
|
||||
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
|
||||
|
|
|
@ -1434,7 +1434,7 @@ msgid "Task summary"
|
|||
msgstr ""
|
||||
|
||||
#: ops/templates/ops/task_detail.html:19
|
||||
msgid "Task record detail"
|
||||
msgid "Task replay detail"
|
||||
msgstr "任务记录详情"
|
||||
|
||||
#: ops/templates/ops/task_detail.html:62
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<div class="panel-options">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active">
|
||||
<a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Task record detail' %} </a>
|
||||
<a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Task replay detail' %} </a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue