Commit dfcbdb0c authored by ibuler's avatar ibuler

[Update] 修改command导出和搜索

parent 5f6af8c0
...@@ -44,8 +44,12 @@ app_view_patterns = [ ...@@ -44,8 +44,12 @@ app_view_patterns = [
if settings.XPACK_ENABLED: if settings.XPACK_ENABLED:
app_view_patterns.append(path('xpack/', include('xpack.urls.view_urls', namespace='xpack'))) app_view_patterns.append(
api_v1.append(path('xpack/v1/', include('xpack.urls.api_urls', namespace='api-xpack'))) path('xpack/', include('xpack.urls.view_urls', namespace='xpack'))
)
api_v1.append(
path('xpack/v1/', include('xpack.urls.api_urls', namespace='api-xpack'))
)
js_i18n_patterns = i18n_patterns( js_i18n_patterns = i18n_patterns(
path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'), path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'),
......
...@@ -467,14 +467,15 @@ jumpserver.initDataTable = function (options) { ...@@ -467,14 +467,15 @@ jumpserver.initDataTable = function (options) {
]; ];
columnDefs = options.columnDefs ? options.columnDefs.concat(columnDefs) : columnDefs; columnDefs = options.columnDefs ? options.columnDefs.concat(columnDefs) : columnDefs;
var select = { var select = {
style: 'multi', style: 'multi',
selector: 'td:first-child' selector: 'td:first-child'
}; };
var table = ele.DataTable({ var table = ele.DataTable({
pageLength: options.pageLength || 15, pageLength: options.pageLength || 15,
dom: options.dom || '<"#uc.pull-left">flt<"row m-t"<"col-md-8"<"#op.col-md-6"><"col-md-6 text-center"i>><"col-md-4"p>>', dom: options.dom || '<"#uc.pull-left">flt<"row m-t"<"col-md-8"<"#op.col-md-6"><"col-md-6 text-center"i>><"col-md-4"p>>',
order: options.order || [], order: options.order || [],
// select: options.select || 'multi', // select: options.select || 'multi',
searchDelay: 800,
buttons: [], buttons: [],
columnDefs: columnDefs, columnDefs: columnDefs,
ajax: { ajax: {
...@@ -574,6 +575,7 @@ jumpserver.initServerSideDataTable = function (options) { ...@@ -574,6 +575,7 @@ jumpserver.initServerSideDataTable = function (options) {
columnDefs: columnDefs, columnDefs: columnDefs,
serverSide: true, serverSide: true,
processing: true, processing: true,
searchDelay: 800,
ajax: { ajax: {
url: options.ajax_url , url: options.ajax_url ,
error: function(jqXHR, textStatus, errorThrown) { error: function(jqXHR, textStatus, errorThrown) {
...@@ -1103,3 +1105,12 @@ function formatDateAsCN(d) { ...@@ -1103,3 +1105,12 @@ function formatDateAsCN(d) {
var date = new Date(d); var date = new Date(d);
return date.toISOString().replace("T", " ").replace(/\..*/, ""); return date.toISOString().replace("T", " ").replace(/\..*/, "");
} }
function getUrlParams(url) {
url = url.split("?");
let params = "";
if (url.length === 2){
params = url[1];
}
return params
}
\ No newline at end of file
...@@ -2,4 +2,5 @@ ...@@ -2,4 +2,5 @@
# #
from .terminal import * from .terminal import *
from .session import * from .session import *
from .command import *
from .task import * from .task import *
# -*- coding: utf-8 -*-
#
import time
from django.utils import timezone
from django.shortcuts import HttpResponse
from rest_framework.pagination import LimitOffsetPagination
from rest_framework import viewsets
from rest_framework import generics
from rest_framework.response import Response
from django.template import loader
from common.permissions import IsOrgAdminOrAppUser, IsAuditor
from common.utils import get_logger
from ..backends import (
get_command_storage, get_multi_command_storage,
SessionCommandSerializer,
)
logger = get_logger(__name__)
__all__ = ['CommandViewSet', 'CommandExportApi']
class CommandQueryMixin:
command_store = get_command_storage()
pagination_class = LimitOffsetPagination
permission_classes = [IsOrgAdminOrAppUser | IsAuditor]
filter_fields = [
"asset", "system_user", "user", "input", "session",
]
default_days_ago = 5
def get_queryset(self):
date_from, date_to = self.get_date_range()
multi_command_storage = get_multi_command_storage()
queryset = multi_command_storage.filter(date_from=date_from, date_to=date_to)
return queryset
def get_filter_fields(self):
fields = self.filter_fields
fields.extend(["date_from", "date_to"])
return fields
def get_date_range(self):
now = timezone.now()
days_ago = now - timezone.timedelta(days=self.default_days_ago)
default_start_st = days_ago.timestamp()
default_end_st = now.timestamp()
query_params = self.request.query_params
date_from_st = query_params.get("date_from") or default_start_st
date_to_st = query_params.get("date_to") or default_end_st
return float(date_from_st), float(date_to_st)
class CommandViewSet(CommandQueryMixin, viewsets.ModelViewSet):
"""接受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_storage()
serializer_class = SessionCommandSerializer
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:
msg = "Command not valid: {}".format(serializer.errors)
logger.error(msg)
return Response({"msg": msg}, status=401)
class CommandExportApi(CommandQueryMixin, generics.ListAPIView):
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
template = 'terminal/command_report.html'
context = {
'queryset': queryset,
'total_count': len(queryset),
'now': time.time(),
}
content = loader.render_to_string(template, context, request)
content_type = 'application/octet-stream'
response = HttpResponse(content, content_type)
filename = 'command-report-{}.html'.format(int(time.time()))
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
return response
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import logging
import os import os
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.core.files.storage import default_storage from django.core.files.storage import default_storage
from django.http import HttpResponseNotFound from django.http import HttpResponseNotFound
from django.conf import settings from django.conf import settings
from django.utils import timezone
from rest_framework.pagination import LimitOffsetPagination from rest_framework.pagination import LimitOffsetPagination
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.response import Response from rest_framework.response import Response
...@@ -15,16 +13,15 @@ from rest_framework_bulk import BulkModelViewSet ...@@ -15,16 +13,15 @@ from rest_framework_bulk import BulkModelViewSet
import jms_storage import jms_storage
from common.utils import is_uuid from common.utils import is_uuid, get_logger
from common.permissions import IsOrgAdminOrAppUser, IsAuditor from common.permissions import IsOrgAdminOrAppUser, IsAuditor
from ..hands import SystemUser from ..hands import SystemUser
from ..models import Terminal, Session from ..models import Session
from .. import serializers from .. import serializers
from ..backends import get_command_storage, get_multi_command_storage, \
SessionCommandSerializer
__all__ = ['SessionViewSet', 'SessionReplayViewSet', 'CommandViewSet']
logger = logging.getLogger(__file__) __all__ = ['SessionViewSet', 'SessionReplayViewSet',]
logger = get_logger(__name__)
class SessionViewSet(BulkModelViewSet): class SessionViewSet(BulkModelViewSet):
...@@ -32,15 +29,7 @@ class SessionViewSet(BulkModelViewSet): ...@@ -32,15 +29,7 @@ class SessionViewSet(BulkModelViewSet):
serializer_class = serializers.SessionSerializer serializer_class = serializers.SessionSerializer
pagination_class = LimitOffsetPagination pagination_class = LimitOffsetPagination
permission_classes = (IsOrgAdminOrAppUser | IsAuditor, ) permission_classes = (IsOrgAdminOrAppUser | IsAuditor, )
filter_fields = ["user", "asset", "system_user", "terminal"]
def get_queryset(self):
queryset = super().get_queryset()
terminal_id = self.kwargs.get("terminal", None)
if terminal_id:
terminal = get_object_or_404(Terminal, id=terminal_id)
queryset = queryset.filter(terminal=terminal)
return queryset
return queryset
def get_object(self): def get_object(self):
# 解决guacamole更新session时并发导致幽灵会话的问题 # 解决guacamole更新session时并发导致幽灵会话的问题
...@@ -60,63 +49,6 @@ class SessionViewSet(BulkModelViewSet): ...@@ -60,63 +49,6 @@ class SessionViewSet(BulkModelViewSet):
return super().perform_create(serializer) return super().perform_create(serializer)
class CommandViewSet(viewsets.ModelViewSet):
"""接受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_storage()
serializer_class = SessionCommandSerializer
pagination_class = LimitOffsetPagination
permission_classes = [IsOrgAdminOrAppUser | IsAuditor]
filter_fields = [
"asset", "system_user", "user", "input", "session",
]
default_days_ago = 5
def get_queryset(self):
date_from, date_to = self.get_date_range()
multi_command_storage = get_multi_command_storage()
queryset = multi_command_storage.filter(date_from=date_from, date_to=date_to)
return queryset
def get_filter_fields(self):
fields = self.filter_fields
fields.extend(["date_from", "date_to"])
return fields
def get_date_range(self):
now = timezone.now()
days_ago = now - timezone.timedelta(days=self.default_days_ago)
default_start_st = days_ago.timestamp()
default_end_st = now.timestamp()
query_params = self.request.query_params
date_from_st = query_params.get("date_from") or default_start_st
date_to_st = query_params.get("date_to") or default_end_st
return int(date_from_st), int(date_to_st)
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:
msg = "Command not valid: {}".format(serializer.errors)
logger.error(msg)
return Response({"msg": msg}, status=401)
class SessionReplayViewSet(viewsets.ViewSet): class SessionReplayViewSet(viewsets.ViewSet):
serializer_class = serializers.ReplaySerializer serializer_class = serializers.ReplaySerializer
permission_classes = (IsOrgAdminOrAppUser,) permission_classes = (IsOrgAdminOrAppUser,)
......
...@@ -80,7 +80,7 @@ $(document).ready(function () { ...@@ -80,7 +80,7 @@ $(document).ready(function () {
table = initTable().on("init", function () { table = initTable().on("init", function () {
var dateFromRef = $("#date_from"); var dateFromRef = $("#date_from");
var dateToRef = $("#date_to"); var dateToRef = $("#date_to");
let options = { var options = {
format: "yyyy-mm-dd", format: "yyyy-mm-dd",
todayBtn: "linked", todayBtn: "linked",
keyboardNavigation: false, keyboardNavigation: false,
...@@ -90,16 +90,15 @@ $(document).ready(function () { ...@@ -90,16 +90,15 @@ $(document).ready(function () {
language: navigator.language || "en", language: navigator.language || "en",
}; };
dateFromRef.datepicker(options).on("changeDate", function () { dateFromRef.datepicker(options).on("changeDate", function () {
let date = new Date($(this).val() + ' 0:0:0'); var date = new Date($(this).val() + ' 0:0:0');
let url = table.ajax.url(); var url = table.ajax.url();
url = setUrlParam(url, "date_from", date.getTime()/1000); url = setUrlParam(url, "date_from", date.getTime()/1000);
table.ajax.url(url); table.ajax.url(url);
table.ajax.reload(); table.ajax.reload();
console.log("On change")
}); });
dateToRef.datepicker(options).on("changeDate", function () { dateToRef.datepicker(options).on("changeDate", function () {
let date = new Date($(this).val() + ' 23:59:59'); var date = new Date($(this).val() + ' 23:59:59');
let url = table.ajax.url(); var url = table.ajax.url();
url = setUrlParam(url, "date_to", date.getTime()/1000); url = setUrlParam(url, "date_to", date.getTime()/1000);
table.ajax.url(url); table.ajax.url(url);
table.ajax.reload(); table.ajax.reload();
...@@ -107,15 +106,11 @@ $(document).ready(function () { ...@@ -107,15 +106,11 @@ $(document).ready(function () {
}); });
}) })
.on('click', '#btn_bulk_update', function(){ .on('click', '#btn_bulk_update', function(){
var action = $('#slct_bulk_update').val(); // var action = $('#slct_bulk_update').val();
var param_action = '&action=' + action; var params = getUrlParams(table.ajax.url());
var local_params = window.location.search;
if(!local_params){ var exportPath = "{% url 'api-terminal:command-export' %}";
param_action = '?action=' + action; var url = exportPath + "?" + params;
}
var params = local_params + param_action;
var pathname = window.location.pathname + 'export/';
var url = pathname + params;
window.open(url); window.open(url);
}).on("click", '#command_table_filter input', function (e) { }).on("click", '#command_table_filter input', function (e) {
e.preventDefault(); e.preventDefault();
...@@ -182,8 +177,13 @@ function format(d) { ...@@ -182,8 +177,13 @@ function format(d) {
return output return output
} }
var commandListUrl = '{% url "api-terminal:command-list" %}';
var dateFrom = "{{ date_from.timestamp }}";
var dateTo = "{{ date_to.timestamp }}";
function initTable() { function initTable() {
commandListUrl = setUrlParam(commandListUrl, "date_from", dateFrom);
commandListUrl = setUrlParam(commandListUrl, "date_to", dateTo);
var options = { var options = {
ele: $('#command_table'), ele: $('#command_table'),
columnDefs: [ columnDefs: [
...@@ -202,7 +202,7 @@ function initTable() { ...@@ -202,7 +202,7 @@ function initTable() {
}}, }},
], ],
toggle: true, toggle: true,
ajax_url: '{% url "api-terminal:command-list" %}', ajax_url: commandListUrl,
columns: [ columns: [
{data: "id"}, {data: "input", orderable: false}, {data: "user", orderable: false}, {data: "id"}, {data: "input", orderable: false}, {data: "user", orderable: false},
{data: "asset"}, {data: "system_user"}, {data: "asset"}, {data: "system_user"},
......
...@@ -26,6 +26,7 @@ urlpatterns = [ ...@@ -26,6 +26,7 @@ urlpatterns = [
path('terminal/<uuid:terminal>/access-key/', api.TerminalTokenApi.as_view(), path('terminal/<uuid:terminal>/access-key/', api.TerminalTokenApi.as_view(),
name='terminal-access-key'), name='terminal-access-key'),
path('terminal/config/', api.TerminalConfig.as_view(), name='terminal-config'), path('terminal/config/', api.TerminalConfig.as_view(), name='terminal-config'),
path('commands/export/', api.CommandExportApi.as_view(), name="command-export")
# v2: get session's replay # v2: get session's replay
# path('v2/sessions/<uuid:pk>/replay/', # path('v2/sessions/<uuid:pk>/replay/',
# api.SessionReplayV2ViewSet.as_view({'get': 'retrieve'}), # api.SessionReplayV2ViewSet.as_view({'get': 'retrieve'}),
......
...@@ -25,6 +25,5 @@ urlpatterns = [ ...@@ -25,6 +25,5 @@ urlpatterns = [
# Command view # Command view
path('command/', views.CommandListView.as_view(), name='command-list'), path('command/', views.CommandListView.as_view(), name='command-list'),
path('command/export/', views.CommandExportView.as_view(), name='command-export')
] ]
...@@ -9,7 +9,7 @@ from .const import USERS_CACHE_KEY, ASSETS_CACHE_KEY, SYSTEM_USER_CACHE_KEY ...@@ -9,7 +9,7 @@ from .const import USERS_CACHE_KEY, ASSETS_CACHE_KEY, SYSTEM_USER_CACHE_KEY
def get_session_asset_list(): def get_session_asset_list():
return Asset.objects.values_list() return Asset.objects.values_list('hostname', flat=True)
def get_session_user_list(): def get_session_user_list():
......
...@@ -10,10 +10,9 @@ import time ...@@ -10,10 +10,9 @@ import time
from common.mixins import DatetimeSearchMixin from common.mixins import DatetimeSearchMixin
from common.permissions import PermissionsMixin, IsOrgAdmin, IsAuditor from common.permissions import PermissionsMixin, IsOrgAdmin, IsAuditor
from ..models import Command
from ..backends import get_multi_command_storage from ..backends import get_multi_command_storage
__all__ = ['CommandListView', 'CommandExportView'] __all__ = ['CommandListView']
common_storage = get_multi_command_storage() common_storage = get_multi_command_storage()
...@@ -33,45 +32,3 @@ class CommandListView(DatetimeSearchMixin, PermissionsMixin, TemplateView): ...@@ -33,45 +32,3 @@ class CommandListView(DatetimeSearchMixin, PermissionsMixin, TemplateView):
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class CommandExportView(DatetimeSearchMixin, PermissionsMixin, View):
model = Command
command = user = asset = system_user = action = ''
date_from = date_to = None
permission_classes = [IsOrgAdmin | IsAuditor]
def get(self, request, *args, **kwargs):
queryset = self.get_queryset()
template = 'terminal/command_report.html'
context = {
'queryset': queryset,
'total_count': len(queryset),
'now': time.time(),
}
content = loader.render_to_string(template, context, request)
content_type = 'application/octet-stream'
response = HttpResponse(content, content_type)
filename = 'command-report-{}.html'.format(int(time.time()))
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
return response
def get_queryset(self):
self.get_date_range()
self.action = self.request.GET.get('action', '')
self.command = self.request.GET.get('command', '')
self.user = self.request.GET.get("user", '')
self.asset = self.request.GET.get('asset', '')
self.system_user = self.request.GET.get('system_user', '')
filter_kwargs = dict()
filter_kwargs['date_from'] = self.date_from
filter_kwargs['date_to'] = self.date_to
if self.user:
filter_kwargs['user'] = self.user
if self.asset:
filter_kwargs['asset'] = self.asset
if self.system_user:
filter_kwargs['system_user'] = self.system_user
if self.command:
filter_kwargs['input'] = self.command
queryset = common_storage.filter(**filter_kwargs)
return queryset
...@@ -38,21 +38,11 @@ class SessionListView(PermissionsMixin, DatetimeSearchMixin, ListView): ...@@ -38,21 +38,11 @@ class SessionListView(PermissionsMixin, DatetimeSearchMixin, ListView):
filter_kwargs = dict() filter_kwargs = dict()
filter_kwargs['date_start__gt'] = self.date_from filter_kwargs['date_start__gt'] = self.date_from
filter_kwargs['date_start__lt'] = self.date_to filter_kwargs['date_start__lt'] = self.date_to
if self.user:
filter_kwargs['user'] = self.user
if self.asset:
filter_kwargs['asset'] = self.asset
if self.system_user:
filter_kwargs['system_user'] = self.system_user
if filter_kwargs:
self.queryset = self.queryset.filter(**filter_kwargs)
return self.queryset return self.queryset
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'user_list': utils.get_session_user_list(), 'asset_list': utils.get_session_asset_list()[:10],
'asset_list': utils.get_session_asset_list(),
'system_user_list': utils.get_session_system_user_list(),
'date_from': self.date_from, 'date_from': self.date_from,
'date_to': self.date_to, 'date_to': self.date_to,
'user': self.user, 'user': self.user,
......
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