[Feature] 修改命令上传api

pull/828/merge
ibuler 2017-11-29 19:27:04 +08:00
parent 26607bc327
commit 7910292e0f
19 changed files with 281 additions and 17 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,2 @@
# ~*~ coding: utf-8 ~*~

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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'),
]

View File

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

View File

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

View File

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

View File

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

View File

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