Commit 4efad4e5 authored by BaiJiangJie's avatar BaiJiangJie

[Feature] 会话管理: Session 页面展示;Command 待续

parent 1e49de79
...@@ -126,7 +126,7 @@ ...@@ -126,7 +126,7 @@
</tr> </tr>
<tr> <tr>
<td>{% trans 'Date joined' %}:</td> <td>{% trans 'Date joined' %}:</td>
<td><b>{{ asset.date_joined|date:"Y-m-j H:i:s" }}</b></td> <td><b>{{ asset.date_created|date:"Y-m-j H:i:s" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Comment' %}:</td> <td>{% trans 'Comment' %}:</td>
......
...@@ -146,7 +146,7 @@ function activeNav() { ...@@ -146,7 +146,7 @@ function activeNav() {
if (app === ''){ if (app === ''){
$('#index').addClass('active'); $('#index').addClass('active');
} }
else if (app === 'xpack' && resource === 'cloud') { else if ((app === 'xpack' && resource === 'cloud') || (app === 'terminal' && (resource === 'asset' || resource === 'database'))) {
var item = url_array[3]; var item = url_array[3];
$("#" + app).addClass('active'); $("#" + app).addClass('active');
$('#' + app + ' #' + resource).addClass('active'); $('#' + app + ' #' + resource).addClass('active');
......
...@@ -59,9 +59,22 @@ ...@@ -59,9 +59,22 @@
<i class="fa fa-rocket" style="width: 14px"></i> <span class="nav-label">{% trans 'Sessions' %}</span><span class="fa arrow"></span> <i class="fa fa-rocket" style="width: 14px"></i> <span class="nav-label">{% trans 'Sessions' %}</span><span class="fa arrow"></span>
</a> </a>
<ul class="nav nav-second-level"> <ul class="nav nav-second-level">
<li id="session-online"><a href="{% url 'terminal:session-online-list' %}">{% trans 'Session online' %}</a></li> <li id="asset">
<li id="session-offline"><a href="{% url 'terminal:session-offline-list' %}">{% trans 'Session offline' %}</a></li> <a href="#"><span class="nav-label">{% trans 'Asset sessions' %}</span><span class="fa arrow"></span></a>
<li id="command"><a href="{% url 'terminal:command-list' %}">{% trans 'Commands' %}</a></li> <ul class="nav nav-third-level">
<li id="session-online"><a href="{% url 'terminal:session-online-list' %}">{% trans 'Session online' %}</a></li>
<li id="session-offline"><a href="{% url 'terminal:session-offline-list' %}">{% trans 'Session offline' %}</a></li>
<li id="command"><a href="{% url 'terminal:command-list' %}">{% trans 'Commands' %}</a></li>
</ul>
</li>
<li id="database">
<a href="#"><span class="nav-label">{% trans 'Database sessions' %}</span><span class="fa arrow"></span></a>
<ul class="nav nav-third-level">
<li id="session-online"><a href="{% url 'terminal:database-session-online-list' %}">{% trans 'Session online' %}</a></li>
<li id="session-offline"><a href="{% url 'terminal:database-session-offline-list' %}">{% trans 'Session offline' %}</a></li>
{# <li id="command"><a href="{% url 'terminal:command-list' %}">{% trans 'Commands' %}</a></li>#}
</ul>
</li>
<li> <li>
<a href="{% url 'terminal:web-terminal' %}" target="_blank"> <a href="{% url 'terminal:web-terminal' %}" target="_blank">
<span class="nav-label">{% trans 'Web terminal' %}</span> <span class="nav-label">{% trans 'Web terminal' %}</span>
......
...@@ -14,33 +14,19 @@ from common.permissions import IsOrgAdminOrAppUser, IsAuditor ...@@ -14,33 +14,19 @@ from common.permissions import IsOrgAdminOrAppUser, IsAuditor
from common.utils import get_logger from common.utils import get_logger
from ..backends import ( from ..backends import (
get_command_storage, get_multi_command_storage, get_command_storage, get_multi_command_storage,
SessionCommandSerializer, SessionCommandSerializer, DatabaseSessionCommandSerializer
) )
logger = get_logger(__name__) logger = get_logger(__name__)
__all__ = ['CommandViewSet', 'CommandExportApi'] __all__ = ['CommandViewSet', 'CommandExportApi', 'DatabaseCommandViewSet']
class CommandQueryMixin: class BaseCommandQueryMixin:
command_store = get_command_storage() command_store = get_command_storage()
pagination_class = LimitOffsetPagination pagination_class = LimitOffsetPagination
permission_classes = [IsOrgAdminOrAppUser | IsAuditor] permission_classes = [IsOrgAdminOrAppUser | IsAuditor]
filter_fields = [
"asset", "system_user", "user", "session",
]
default_days_ago = 5 default_days_ago = 5
def get_queryset(self):
date_from, date_to = self.get_date_range()
q = self.request.query_params
multi_command_storage = get_multi_command_storage()
queryset = multi_command_storage.filter(
date_from=date_from, date_to=date_to, input=q.get("input"),
user=q.get("user"), asset=q.get("asset"),
system_user=q.get("system_user")
)
return queryset
def filter_queryset(self, queryset): def filter_queryset(self, queryset):
return queryset return queryset
...@@ -60,21 +46,49 @@ class CommandQueryMixin: ...@@ -60,21 +46,49 @@ class CommandQueryMixin:
return float(date_from_st), float(date_to_st) return float(date_from_st), float(date_to_st)
class CommandViewSet(CommandQueryMixin, viewsets.ModelViewSet): class CommandQueryMixin(BaseCommandQueryMixin):
"""接受app发送来的command log, 格式如下 """
{ Asset Command Query Mixin
"user": "admin", """
"asset": "localhost", filter_fields = [
"system_user": "web", "user", "asset", "system_user", "session",
"session": "xxxxxx", ]
"input": "whoami",
"output": "d2hvbWFp", # base64.b64encode(s) def get_queryset(self):
"timestamp": 1485238673.0 date_from, date_to = self.get_date_range()
} q = self.request.query_params
multi_command_storage = get_multi_command_storage()
queryset = multi_command_storage.filter(
date_from=date_from, date_to=date_to, input=q.get("input"),
user=q.get("user"), asset=q.get("asset"),
system_user=q.get("system_user")
)
return queryset
class DatabaseCommandQueryMixin(BaseCommandQueryMixin):
"""
Database Command Query Mixin
""" """
filter_fields = [
"user", "database", "db_host", "db_name", "db_user"
]
def get_queryset(self):
date_from, date_to = self.get_date_range()
q = self.request.query_params
multi_command_storage = get_multi_command_storage()
queryset = multi_command_storage.filter(
date_from=date_from, date_to=date_to, input=q.get("input"),
user=q.get("user"), database=q.get("database"),
db_host=q.get("db_host"), db_name=q.get("db_name"),
db_user=q.get("db_user")
)
return queryset
class BaseCommandViewSet(viewsets.ModelViewSet):
command_store = get_command_storage() command_store = get_command_storage()
serializer_class = SessionCommandSerializer
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data, many=True) serializer = self.serializer_class(data=request.data, many=True)
...@@ -90,6 +104,31 @@ class CommandViewSet(CommandQueryMixin, viewsets.ModelViewSet): ...@@ -90,6 +104,31 @@ class CommandViewSet(CommandQueryMixin, viewsets.ModelViewSet):
return Response({"msg": msg}, status=401) return Response({"msg": msg}, status=401)
class CommandViewSet(CommandQueryMixin, BaseCommandViewSet):
"""
Asset Command ViewSet
接受app发送来的command log, 格式如下
{
"user": "admin",
"asset": "localhost",
"system_user": "web",
"session": "xxxxxx",
"input": "whoami",
"output": "d2hvbWFp", # base64.b64encode(s)
"timestamp": 1485238673.0
}
"""
serializer_class = SessionCommandSerializer
class DatabaseCommandViewSet(DatabaseCommandQueryMixin, BaseCommandViewSet):
"""
Database Command ViewSet
"""
serializer_class = DatabaseSessionCommandSerializer
class CommandExportApi(CommandQueryMixin, generics.ListAPIView): class CommandExportApi(CommandQueryMixin, generics.ListAPIView):
serializer_class = SessionCommandSerializer serializer_class = SessionCommandSerializer
...@@ -108,3 +147,5 @@ class CommandExportApi(CommandQueryMixin, generics.ListAPIView): ...@@ -108,3 +147,5 @@ class CommandExportApi(CommandQueryMixin, generics.ListAPIView):
filename = 'command-report-{}.html'.format(int(time.time())) filename = 'command-report-{}.html'.format(int(time.time()))
response['Content-Disposition'] = 'attachment; filename="%s"' % filename response['Content-Disposition'] = 'attachment; filename="%s"' % filename
return response return response
...@@ -68,7 +68,8 @@ class DatabaseSessionViewSet(OrgBulkModelViewSet): ...@@ -68,7 +68,8 @@ class DatabaseSessionViewSet(OrgBulkModelViewSet):
pagination_class = LimitOffsetPagination pagination_class = LimitOffsetPagination
permission_classes = (IsOrgAdminOrAppUser | IsAuditor, ) permission_classes = (IsOrgAdminOrAppUser | IsAuditor, )
filter_fields = [ filter_fields = [
'user', 'database', 'db_host', 'db_name', 'db_user' 'user', 'database', 'db_host', 'db_name', 'db_user',
'is_finished', 'terminal', 'remote_addr'
] ]
date_range_filter_fields = [ date_range_filter_fields = [
('date_start', ('date_from', 'date_to')) ('date_start', ('date_from', 'date_to'))
......
...@@ -6,7 +6,7 @@ from rest_framework_bulk import BulkModelViewSet ...@@ -6,7 +6,7 @@ from rest_framework_bulk import BulkModelViewSet
from common.utils import get_object_or_none from common.utils import get_object_or_none
from common.permissions import IsOrgAdminOrAppUser from common.permissions import IsOrgAdminOrAppUser
from ..models import Session, Task from ..models import Session, Task, DatabaseSession
from .. import serializers from .. import serializers
...@@ -28,6 +28,8 @@ class KillSessionAPI(APIView): ...@@ -28,6 +28,8 @@ class KillSessionAPI(APIView):
validated_session = [] validated_session = []
for session_id in request.data: for session_id in request.data:
session = get_object_or_none(Session, id=session_id) session = get_object_or_none(Session, id=session_id)
if not session:
session = get_object_or_none(DatabaseSession, id=session_id)
if session and not session.is_finished: if session and not session.is_finished:
validated_session.append(session_id) validated_session.append(session_id)
self.model.objects.create( self.model.objects.create(
......
from importlib import import_module from importlib import import_module
from django.conf import settings from django.conf import settings
from .command.serializers import SessionCommandSerializer from .command.serializers import (
SessionCommandSerializer, DatabaseSessionCommandSerializer
)
from common import utils from common import utils
......
...@@ -7,19 +7,14 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -7,19 +7,14 @@ from django.utils.translation import ugettext_lazy as _
from orgs.mixins import OrgModelMixin from orgs.mixins import OrgModelMixin
class AbstractSessionCommand(OrgModelMixin): class BaseAbstractSessionCommand(OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
user = models.CharField(max_length=64, db_index=True, verbose_name=_("User")) user = models.CharField(max_length=64, db_index=True, verbose_name=_("User"))
asset = models.CharField(max_length=128, db_index=True, verbose_name=_("Asset"))
system_user = models.CharField(max_length=64, db_index=True, verbose_name=_("System user"))
input = models.CharField(max_length=128, db_index=True, verbose_name=_("Input")) input = models.CharField(max_length=128, db_index=True, verbose_name=_("Input"))
output = models.CharField(max_length=1024, blank=True, verbose_name=_("Output")) output = models.CharField(max_length=1024, blank=True, verbose_name=_("Output"))
session = models.CharField(max_length=36, db_index=True, verbose_name=_("Session")) session = models.CharField(max_length=36, db_index=True, verbose_name=_("Session"))
timestamp = models.IntegerField(db_index=True) timestamp = models.IntegerField(db_index=True)
class Meta:
abstract = True
@classmethod @classmethod
def from_dict(cls, d): def from_dict(cls, d):
self = cls() self = cls()
...@@ -43,3 +38,27 @@ class AbstractSessionCommand(OrgModelMixin): ...@@ -43,3 +38,27 @@ class AbstractSessionCommand(OrgModelMixin):
def __str__(self): def __str__(self):
return self.input return self.input
class Meta:
abstract = True
class AbstractSessionCommand(BaseAbstractSessionCommand):
asset = models.CharField(max_length=128, db_index=True, verbose_name=_("Asset"))
system_user = models.CharField(max_length=64, db_index=True, verbose_name=_("System user"))
class Meta:
abstract = True
class AbstractDatabaseSessionCommand(BaseAbstractSessionCommand):
user_id = models.CharField(max_length=36, db_index=True, verbose_name=_("User ID"))
database = models.CharField(max_length=128, verbose_name=_('Database'))
database_id = models.CharField(max_length=36, db_index=True, verbose_name=_("Database ID"))
db_host = models.CharField(max_length=128, verbose_name=_('Database host'))
db_name = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Database name'))
db_user = models.CharField(max_length=32, blank=True, null=True, verbose_name=_('Database user'))
class Meta:
abstract = True
...@@ -2,16 +2,26 @@ ...@@ -2,16 +2,26 @@
from rest_framework import serializers from rest_framework import serializers
class SessionCommandSerializer(serializers.Serializer): class BaseSessionCommandSerializer(serializers.Serializer):
"""使用这个类作为基础Command Log Serializer类, 用来序列化"""
id = serializers.UUIDField(read_only=True) 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) input = serializers.CharField(max_length=128)
output = serializers.CharField(max_length=1024, allow_blank=True) output = serializers.CharField(max_length=1024, allow_blank=True)
session = serializers.CharField(max_length=36) session = serializers.CharField(max_length=36)
org_id = serializers.CharField(max_length=36, required=False, default='', allow_null=True, allow_blank=True) org_id = serializers.CharField(max_length=36, required=False, default='', allow_null=True, allow_blank=True)
timestamp = serializers.IntegerField() timestamp = serializers.IntegerField()
class SessionCommandSerializer(BaseSessionCommandSerializer):
"""使用这个类作为基础Command Log Serializer类, 用来序列化"""
user = serializers.CharField(max_length=64)
asset = serializers.CharField(max_length=128)
system_user = serializers.CharField(max_length=64)
class DatabaseSessionCommandSerializer(BaseSessionCommandSerializer):
user_id = serializers.CharField(max_length=36)
database = serializers.CharField(max_length=128)
database_id = serializers.CharField(max_length=36)
db_host = serializers.CharField(max_length=128)
db_name = serializers.CharField(max_length=128)
db_user = serializers.CharField(max_length=32)
...@@ -71,6 +71,9 @@ class DatabaseSession(BaseSession): ...@@ -71,6 +71,9 @@ class DatabaseSession(BaseSession):
db_name = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Database name')) db_name = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Database name'))
db_user = models.CharField(max_length=32, blank=True, null=True, verbose_name=_('Database user')) db_user = models.CharField(max_length=32, blank=True, null=True, verbose_name=_('Database user'))
def can_replay(self):
return self.has_replay
class Meta: class Meta:
db_table = "terminal_database_session" db_table = "terminal_database_session"
ordering = ["-date_start"] ordering = ["-date_start"]
...@@ -50,7 +50,7 @@ class DatabaseSessionSerializer(BulkOrgResourceModelSerializer): ...@@ -50,7 +50,7 @@ class DatabaseSessionSerializer(BulkOrgResourceModelSerializer):
fields = [ fields = [
'id', 'user', 'database', 'db_host', 'db_name', 'db_user', 'id', 'user', 'database', 'db_host', 'db_name', 'db_user',
'login_from_display', 'remote_addr', 'is_finished', 'has_replay', 'login_from_display', 'remote_addr', 'is_finished', 'has_replay',
'date_start', 'date_end', 'terminal', 'command_amount' 'can_replay', 'date_start', 'date_end', 'terminal', 'command_amount'
] ]
......
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>
\ No newline at end of file
{% extends '_base_list.html' %}
{% load i18n %}
{% load static %}
{% load terminal_tags %}
{% load common_tags %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content_left_head %}
{% endblock %}
{% block table_pagination %}
{% endblock %}
{% block table_search %}
{% endblock %}
{% block table_container %}
<table class="table table-striped table-bordered table-hover" id="session_table" data-page="false" >
<thead>
<tr>
<th class="text-center"></th>
<th class="text-center">{% trans 'ID' %}</th>
<th class="text-center">{% trans 'User' %}</th>
<th class="text-center">{% trans 'Database' %}</th>
<th class="text-center">{% trans 'Database host' %}</th>
<th class="text-center">{% trans 'Database user' %}</th>
<th class="text-center">{% trans 'Database name' %}</th>
<th class="text-center">{% trans 'Remote addr' %}</th>
<th class="text-center">{% trans 'Login from' %}</th>
<th class="text-center">{% trans 'Command' %}</th>
<th class="text-center">{% trans 'Date start' %}</th>
<th class="text-center">{% trans 'Duration' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="hide" id="daterange">
<div class="form-group p-l-5" id="date" style="padding-left: 5px">
<div class="input-daterange input-group p-l-5" id="datepicker">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text" class="input-sm form-control" style="width: 100px;" id="date_from" name="date_from" value="{{ date_from|date:'Y-m-d' }}">
<span class="input-group-addon">to</span>
<input type="text" class="input-sm form-control" style="width: 100px;" id="date_to" name="date_to" value="{{ date_to|date:'Y-m-d' }}">
</div>
</div>
</div>
<ul class="dropdown-menu search-help">
<li><a class="search-item" data-value="user">{% trans 'User' %}</a></li>
<li><a class="search-item" data-value="database">{% trans 'Database' %}</a></li>
<li><a class="search-item" data-value="db_host">{% trans 'Database host' %}</a></li>
<li><a class="search-item" data-value="db_user">{% trans 'Database user' %}</a></li>
<li><a class="search-item" data-value="db_name">{% trans 'Database name' %}</a></li>
<li><a class="search-item" data-value="remote_addr">{% trans 'Remote addr' %}</a></li>
</ul>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.zh-CN.min.js' %}" ></script>
<script>
function terminateSession(data) {
function success() {
window.setTimeout(function () {
window.location.reload()
}, 1000)
}
var success_message = '{% trans "Terminate task send, waiting ..." %}';
var the_url = "{% url 'api-terminal:kill-session' %}";
requestApi({
url: the_url,
method: 'POST',
body: JSON.stringify(data),
success: success,
success_message: success_message
});
}
var sessionListUrl = "{% url 'api-terminal:database-session-list' %}?is_finished={% if type == "online" %}0{% else %}1{% endif %}";
var dateFrom = "{{ date_from.timestamp }}";
var dateTo = "{{ date_to.timestamp }}";
function initTable() {
dateFrom = toSafeDateISOStr(dateFrom * 1000);
dateTo = toSafeDateISOStr(dateTo * 1000);
sessionListUrl = setUrlParam(sessionListUrl, "date_from", dateFrom);
sessionListUrl = setUrlParam(sessionListUrl, "date_to", dateTo);
var options = {
ele: $('#session_table'),
ordering: false,
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData, i) {
var index = i + 1;
var data = '<a href="{% url 'terminal:database-session-detail' pk=DEFAULT_PK %}">'
+ index + "</a>";
data = data.replace("{{ DEFAULT_PK }}", cellData);
$(td).html(data);
}},
{targets: 2, createdCell: function (td, cellData) {
var data = htmlEscape(cellData);
$(td).html(data);
}},
{targets: 3, createdCell: function (td, cellData) {
var data = htmlEscape(cellData);
$(td).html(data);
}},
{targets: 4, createdCell: function (td, cellData) {
var data = htmlEscape(cellData);
$(td).html(data);
}},
{targets: 5, createdCell: function (td, cellData) {
var data = htmlEscape(cellData);
$(td).html(data);
}},
{targets: 6, createdCell: function (td, cellData) {
var data = htmlEscape(cellData);
$(td).html(data);
}},
{targets: 11, createdCell: function (td, cellData, rowData) {
var data = "";
if (cellData && rowData.date_start) {
data = timeOffset(rowData.date_start, cellData)
}
$(td).html(data);
}},
{targets: 12, createdCell: function (td, cellData, rowData) {
var btnGroup = "";
var replayBtn = '<a disabled data-session="sessionID" class="btn btn-xs btn-warning btn-replay" >{% trans "Replay" %}</a>';
replayBtn = replayBtn.replace("sessionID", rowData.id);
if (rowData.can_replay) {
replayBtn = replayBtn.replace("disabled", "")
}
var termBtn = '<a class="btn btn-xs btn-danger btn-term" disabled value="sessionID" terminal="terminalID" >{% trans "Terminate" %}</a>';
if ("{{ request.user.is_org_admin }}" === "True") {
termBtn = termBtn.replace("disabled", "")
.replace("sessionID", cellData)
.replace("terminalID", rowData.terminal)
}
if (rowData.is_finished) {
btnGroup += replayBtn
} else {
btnGroup += termBtn;
}
$(td).html(btnGroup);
}}
],
ajax_url: sessionListUrl,
columns: [
{data: "id"},
{data: "id", orderable:false},
{data: "user"},
{data: "database"},
{data: "db_host"},
{data: "db_user"},
{data: "db_name"},
{data: "remote_addr"},
{data: "login_from_display", orderable:false},
{data: "command_amount", orderable:false},
{data: "date_start"},
{data: "date_end", orderable:false},
{data: "id",orderable:false}
],
op_html: $('#actions').html(),
fb_html: $("#daterange").html()
};
table = jumpserver.initServerSideDataTable(options);
return table
}
var table;
$(document).ready(function() {
table = initTable("#session_table").on('init', function () {
var dateFromRef = $("#date_from");
var dateToRef = $("#date_to");
var options = {
format: "yyyy-mm-dd",
todayBtn: "linked",
keyboardNavigation: false,
forceParse: false,
calendarWeeks: true,
autoclose: true,
language: navigator.language || "en",
};
dateFromRef.datepicker(options).on("changeDate", function () {
if (!$(this).val()) {
return
}
var value = $(this).val() + ' 0:0:0';
var date = toSafeDateISOStr(value);
var url = table.ajax.url();
url = setUrlParam(url, "date_from", date);
table.ajax.url(url);
table.ajax.reload();
});
dateToRef.datepicker(options).on("changeDate", function () {
if (!$(this).val()) {
return
}
var value = $(this).val() + ' 23:59:59';
var date = toSafeDateISOStr(value);
var url = table.ajax.url();
url = setUrlParam(url, "date_to", date);
table.ajax.url(url);
table.ajax.reload();
});
});
}).on('click', '.btn-term', function () {
var $this = $(this);
var session_id = $this.attr('value');
var data = [
session_id
];
terminateSession(data)
}).on('click', '.btn-replay', function () {
var sessionID = $(this).data("session");
var replayUrl = "/luna/replay/" + sessionID;
window.open(replayUrl, "height=600, width=800, top=400, left=400, toolbar=no, menubar=no, scrollbars=no, location=no, status=no");
}).on("click", '#session_table_filter input', function (e) {
e.preventDefault();
e.stopPropagation();
var offset1 = $('#session_table_filter input').offset();
var x = offset1.left;
var y = offset1.top;
var offset = $(".search-help").parent().offset();
x -= offset.left;
y -= offset.top;
x += 18;
y += 80;
$('.search-help').css({"top":y+"px", "left":x+"px", "position": "absolute"});
$('.dropdown-menu.search-help').show();
})
.on('click', '.search-item', function (e) {
e.preventDefault();
e.stopPropagation();
var keyword = $("#session_table_filter input");
var value = $(this).data('value');
var old_value = keyword.val();
var new_value = old_value + ' ' + value + ':';
keyword.val(new_value.trim());
$('.dropdown-menu.search-help').hide();
keyword.focus()
})
.on('click', 'body', function (e) {
$('.dropdown-menu.search-help').hide()
})
</script>
{% endblock %}
...@@ -18,12 +18,18 @@ urlpatterns = [ ...@@ -18,12 +18,18 @@ urlpatterns = [
path('web-terminal/', views.WebTerminalView.as_view(), name='web-terminal'), path('web-terminal/', views.WebTerminalView.as_view(), name='web-terminal'),
path('web-sftp/', views.WebSFTPView.as_view(), name='web-sftp'), path('web-sftp/', views.WebSFTPView.as_view(), name='web-sftp'),
# Session view # Asset session view
path('session-online/', views.SessionOnlineListView.as_view(), name='session-online-list'), path('asset/session-online/', views.SessionOnlineListView.as_view(), name='session-online-list'),
path('session-offline/', views.SessionOfflineListView.as_view(), name='session-offline-list'), path('asset/session-offline/', views.SessionOfflineListView.as_view(), name='session-offline-list'),
path('session/<uuid:pk>/', views.SessionDetailView.as_view(), name='session-detail'), path('asset/session/<uuid:pk>/', views.SessionDetailView.as_view(), name='session-detail'),
# Databases session view
path('database/session-online/', views.DatabaseSessionOnlineListView.as_view(), name='database-session-online-list'),
path('database/session-offline/', views.DatabaseSessionOfflineListView.as_view(), name='database-session-offline-list'),
path('database/session/<uuid:pk>/', views.DatabaseSessionDetailView.as_view(), name='database-session-detail'),
# Command view # Command view
path('command/', views.CommandListView.as_view(), name='command-list'), path('asset/command/', views.CommandListView.as_view(), name='command-list'),
# path('database/command/', views.DatabaseCommandListView.as_view(), name='database-command-list'),
] ]
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from .terminal import * from .terminal import *
from .session import * from .asset_session import *
from .command import * from .asset_command import *
from .database_session import *
...@@ -11,7 +11,7 @@ __all__ = ['CommandListView'] ...@@ -11,7 +11,7 @@ __all__ = ['CommandListView']
class CommandListView(PermissionsMixin, TemplateView): class CommandListView(PermissionsMixin, TemplateView):
template_name = "terminal/command_list.html" template_name = "terminal/asset_command_list.html"
permission_classes = [IsOrgAdmin | IsAuditor] permission_classes = [IsOrgAdmin | IsAuditor]
default_days_ago = 5 default_days_ago = 5
......
...@@ -4,14 +4,11 @@ ...@@ -4,14 +4,11 @@
from django.views.generic import ListView, TemplateView from django.views.generic import ListView, TemplateView
from django.views.generic.edit import SingleObjectMixin from django.views.generic.edit import SingleObjectMixin
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils import timezone
from django.conf import settings
from common.permissions import PermissionsMixin, IsOrgAdmin, IsAuditor from common.permissions import PermissionsMixin, IsOrgAdmin, IsAuditor
from common.mixins import DatetimeSearchMixin from ..models import Session
from ..models import Session, Command, Terminal
from ..backends import get_multi_command_storage from ..backends import get_multi_command_storage
from .. import utils from .base_session import BaseSessionListView
__all__ = [ __all__ = [
...@@ -20,21 +17,9 @@ __all__ = [ ...@@ -20,21 +17,9 @@ __all__ = [
] ]
class SessionListView(PermissionsMixin, TemplateView): class SessionListView(BaseSessionListView):
model = Session model = Session
template_name = 'terminal/session_list.html' template_name = 'terminal/asset_session_list.html'
date_from = date_to = None
permission_classes = [IsOrgAdmin | IsAuditor]
default_days_ago = 5
def get_context_data(self, **kwargs):
now = timezone.now()
context = {
'date_from': now - timezone.timedelta(days=self.default_days_ago),
'date_to': now,
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class SessionOnlineListView(SessionListView): class SessionOnlineListView(SessionListView):
...@@ -60,7 +45,7 @@ class SessionOfflineListView(SessionListView): ...@@ -60,7 +45,7 @@ class SessionOfflineListView(SessionListView):
class SessionDetailView(SingleObjectMixin, PermissionsMixin, ListView): class SessionDetailView(SingleObjectMixin, PermissionsMixin, ListView):
template_name = 'terminal/session_detail.html' template_name = 'terminal/asset_session_detail.html'
model = Session model = Session
object = None object = None
permission_classes = [IsOrgAdmin | IsAuditor] permission_classes = [IsOrgAdmin | IsAuditor]
......
# coding: utf-8
#
from django.views.generic import TemplateView
from django.utils import timezone
from common.permissions import PermissionsMixin, IsOrgAdmin, IsAuditor
__all__ = ['BaseSessionListView']
class BaseSessionListView(PermissionsMixin, TemplateView):
model = None
template_name = None
date_from = date_to = None
permission_classes = [IsOrgAdmin | IsAuditor]
default_days_ago = 5
def get_context_data(self, **kwargs):
now = timezone.now()
context = {
'date_from': now - timezone.timedelta(days=self.default_days_ago),
'date_to': now,
}
kwargs.update(context)
return super().get_context_data(**kwargs)
# -*- coding: utf-8 -*-
#
from django.views.generic import ListView
from django.views.generic.edit import SingleObjectMixin
from common.permissions import PermissionsMixin, IsOrgAdmin, IsAuditor
from ..backends import get_multi_command_storage
from django.utils.translation import ugettext as _
from ..models import DatabaseSession
from .base_session import BaseSessionListView
__all__ = [
'DatabaseSessionOnlineListView', 'DatabaseSessionOfflineListView',
'DatabaseSessionDetailView'
]
class DatabaseSessionListView(BaseSessionListView):
model = DatabaseSession
template_name = 'terminal/database_session_list.html'
class DatabaseSessionOnlineListView(DatabaseSessionListView):
def get_context_data(self, **kwargs):
context = {
'app': _('Sessions'),
'action': _('Session online list'),
'type': 'online',
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class DatabaseSessionOfflineListView(DatabaseSessionListView):
def get_context_data(self, **kwargs):
context = {
'app': _('Sessions'),
'action': _('Session offline'),
'type': 'offline',
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class DatabaseSessionDetailView(SingleObjectMixin, PermissionsMixin, ListView):
template_name = 'terminal/database_session_detail.html'
model = DatabaseSession
object = None
permission_classes = [IsOrgAdmin | IsAuditor]
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=self.model.objects.all())
return super().get(request, *args, **kwargs)
def get_queryset(self):
command_store = get_multi_command_storage()
return command_store.filter(session=self.object.id)
def get_context_data(self, **kwargs):
context = {
'app': _('Sessions'),
'action': _('Session detail'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
\ No newline at end of file
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