mirror of https://github.com/jumpserver/jumpserver
[Update] 修改command导出和搜索
parent
5f6af8c07d
commit
dfcbdb0c35
|
@ -44,8 +44,12 @@ app_view_patterns = [
|
|||
|
||||
|
||||
if settings.XPACK_ENABLED:
|
||||
app_view_patterns.append(path('xpack/', include('xpack.urls.view_urls', namespace='xpack')))
|
||||
api_v1.append(path('xpack/v1/', include('xpack.urls.api_urls', namespace='api-xpack')))
|
||||
app_view_patterns.append(
|
||||
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(
|
||||
path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'),
|
||||
|
|
|
@ -467,14 +467,15 @@ jumpserver.initDataTable = function (options) {
|
|||
];
|
||||
columnDefs = options.columnDefs ? options.columnDefs.concat(columnDefs) : columnDefs;
|
||||
var select = {
|
||||
style: 'multi',
|
||||
selector: 'td:first-child'
|
||||
};
|
||||
style: 'multi',
|
||||
selector: 'td:first-child'
|
||||
};
|
||||
var table = ele.DataTable({
|
||||
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>>',
|
||||
order: options.order || [],
|
||||
// select: options.select || 'multi',
|
||||
searchDelay: 800,
|
||||
buttons: [],
|
||||
columnDefs: columnDefs,
|
||||
ajax: {
|
||||
|
@ -574,6 +575,7 @@ jumpserver.initServerSideDataTable = function (options) {
|
|||
columnDefs: columnDefs,
|
||||
serverSide: true,
|
||||
processing: true,
|
||||
searchDelay: 800,
|
||||
ajax: {
|
||||
url: options.ajax_url ,
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
|
@ -1103,3 +1105,12 @@ function formatDateAsCN(d) {
|
|||
var date = new Date(d);
|
||||
return date.toISOString().replace("T", " ").replace(/\..*/, "");
|
||||
}
|
||||
|
||||
function getUrlParams(url) {
|
||||
url = url.split("?");
|
||||
let params = "";
|
||||
if (url.length === 2){
|
||||
params = url[1];
|
||||
}
|
||||
return params
|
||||
}
|
|
@ -2,4 +2,5 @@
|
|||
#
|
||||
from .terminal import *
|
||||
from .session import *
|
||||
from .command import *
|
||||
from .task import *
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
# -*- 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
|
|
@ -1,13 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import logging
|
||||
import os
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.core.files.storage import default_storage
|
||||
from django.http import HttpResponseNotFound
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from rest_framework.pagination import LimitOffsetPagination
|
||||
from rest_framework import viewsets
|
||||
from rest_framework.response import Response
|
||||
|
@ -15,16 +13,15 @@ from rest_framework_bulk import BulkModelViewSet
|
|||
import jms_storage
|
||||
|
||||
|
||||
from common.utils import is_uuid
|
||||
from common.utils import is_uuid, get_logger
|
||||
from common.permissions import IsOrgAdminOrAppUser, IsAuditor
|
||||
from ..hands import SystemUser
|
||||
from ..models import Terminal, Session
|
||||
from ..models import Session
|
||||
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):
|
||||
|
@ -32,15 +29,7 @@ class SessionViewSet(BulkModelViewSet):
|
|||
serializer_class = serializers.SessionSerializer
|
||||
pagination_class = LimitOffsetPagination
|
||||
permission_classes = (IsOrgAdminOrAppUser | IsAuditor, )
|
||||
|
||||
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
|
||||
filter_fields = ["user", "asset", "system_user", "terminal"]
|
||||
|
||||
def get_object(self):
|
||||
# 解决guacamole更新session时并发导致幽灵会话的问题
|
||||
|
@ -60,63 +49,6 @@ class SessionViewSet(BulkModelViewSet):
|
|||
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):
|
||||
serializer_class = serializers.ReplaySerializer
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
|
|
|
@ -80,7 +80,7 @@ $(document).ready(function () {
|
|||
table = initTable().on("init", function () {
|
||||
var dateFromRef = $("#date_from");
|
||||
var dateToRef = $("#date_to");
|
||||
let options = {
|
||||
var options = {
|
||||
format: "yyyy-mm-dd",
|
||||
todayBtn: "linked",
|
||||
keyboardNavigation: false,
|
||||
|
@ -90,16 +90,15 @@ $(document).ready(function () {
|
|||
language: navigator.language || "en",
|
||||
};
|
||||
dateFromRef.datepicker(options).on("changeDate", function () {
|
||||
let date = new Date($(this).val() + ' 0:0:0');
|
||||
let url = table.ajax.url();
|
||||
var date = new Date($(this).val() + ' 0:0:0');
|
||||
var url = table.ajax.url();
|
||||
url = setUrlParam(url, "date_from", date.getTime()/1000);
|
||||
table.ajax.url(url);
|
||||
table.ajax.reload();
|
||||
console.log("On change")
|
||||
});
|
||||
dateToRef.datepicker(options).on("changeDate", function () {
|
||||
let date = new Date($(this).val() + ' 23:59:59');
|
||||
let url = table.ajax.url();
|
||||
var date = new Date($(this).val() + ' 23:59:59');
|
||||
var url = table.ajax.url();
|
||||
url = setUrlParam(url, "date_to", date.getTime()/1000);
|
||||
table.ajax.url(url);
|
||||
table.ajax.reload();
|
||||
|
@ -107,15 +106,11 @@ $(document).ready(function () {
|
|||
});
|
||||
})
|
||||
.on('click', '#btn_bulk_update', function(){
|
||||
var action = $('#slct_bulk_update').val();
|
||||
var param_action = '&action=' + action;
|
||||
var local_params = window.location.search;
|
||||
if(!local_params){
|
||||
param_action = '?action=' + action;
|
||||
}
|
||||
var params = local_params + param_action;
|
||||
var pathname = window.location.pathname + 'export/';
|
||||
var url = pathname + params;
|
||||
// var action = $('#slct_bulk_update').val();
|
||||
var params = getUrlParams(table.ajax.url());
|
||||
|
||||
var exportPath = "{% url 'api-terminal:command-export' %}";
|
||||
var url = exportPath + "?" + params;
|
||||
window.open(url);
|
||||
}).on("click", '#command_table_filter input', function (e) {
|
||||
e.preventDefault();
|
||||
|
@ -182,8 +177,13 @@ function format(d) {
|
|||
return output
|
||||
}
|
||||
|
||||
var commandListUrl = '{% url "api-terminal:command-list" %}';
|
||||
var dateFrom = "{{ date_from.timestamp }}";
|
||||
var dateTo = "{{ date_to.timestamp }}";
|
||||
|
||||
function initTable() {
|
||||
commandListUrl = setUrlParam(commandListUrl, "date_from", dateFrom);
|
||||
commandListUrl = setUrlParam(commandListUrl, "date_to", dateTo);
|
||||
var options = {
|
||||
ele: $('#command_table'),
|
||||
columnDefs: [
|
||||
|
@ -202,7 +202,7 @@ function initTable() {
|
|||
}},
|
||||
],
|
||||
toggle: true,
|
||||
ajax_url: '{% url "api-terminal:command-list" %}',
|
||||
ajax_url: commandListUrl,
|
||||
columns: [
|
||||
{data: "id"}, {data: "input", orderable: false}, {data: "user", orderable: false},
|
||||
{data: "asset"}, {data: "system_user"},
|
||||
|
|
|
@ -26,6 +26,7 @@ urlpatterns = [
|
|||
path('terminal/<uuid:terminal>/access-key/', api.TerminalTokenApi.as_view(),
|
||||
name='terminal-access-key'),
|
||||
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
|
||||
# path('v2/sessions/<uuid:pk>/replay/',
|
||||
# api.SessionReplayV2ViewSet.as_view({'get': 'retrieve'}),
|
||||
|
|
|
@ -25,6 +25,5 @@ urlpatterns = [
|
|||
|
||||
# Command view
|
||||
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
|
|||
|
||||
|
||||
def get_session_asset_list():
|
||||
return Asset.objects.values_list()
|
||||
return Asset.objects.values_list('hostname', flat=True)
|
||||
|
||||
|
||||
def get_session_user_list():
|
||||
|
|
|
@ -10,10 +10,9 @@ import time
|
|||
|
||||
from common.mixins import DatetimeSearchMixin
|
||||
from common.permissions import PermissionsMixin, IsOrgAdmin, IsAuditor
|
||||
from ..models import Command
|
||||
from ..backends import get_multi_command_storage
|
||||
|
||||
__all__ = ['CommandListView', 'CommandExportView']
|
||||
__all__ = ['CommandListView']
|
||||
common_storage = get_multi_command_storage()
|
||||
|
||||
|
||||
|
@ -33,45 +32,3 @@ class CommandListView(DatetimeSearchMixin, PermissionsMixin, TemplateView):
|
|||
kwargs.update(context)
|
||||
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):
|
|||
filter_kwargs = dict()
|
||||
filter_kwargs['date_start__gt'] = self.date_from
|
||||
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
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'user_list': utils.get_session_user_list(),
|
||||
'asset_list': utils.get_session_asset_list(),
|
||||
'system_user_list': utils.get_session_system_user_list(),
|
||||
'asset_list': utils.get_session_asset_list()[:10],
|
||||
'date_from': self.date_from,
|
||||
'date_to': self.date_to,
|
||||
'user': self.user,
|
||||
|
|
Loading…
Reference in New Issue