Commit b9f82fd0 authored by ibuler's avatar ibuler

[Update] 优化命令记录列表

parent 1b44172b
...@@ -60,8 +60,8 @@ function initAssetUserTable() { ...@@ -60,8 +60,8 @@ function initAssetUserTable() {
}, },
{ {
targets: 6, createdCell: function (td, cellData) { targets: 6, createdCell: function (td, cellData) {
var date = new Date(cellData); var data = formatDateAsCN(cellData);
$(td).html(date.toLocaleString()); $(td).html(data);
}, },
}, },
{ {
......
...@@ -15,27 +15,27 @@ ...@@ -15,27 +15,27 @@
{% block table_search %} {% block table_search %}
<div class="" style="float: right"> <div class="" style="float: right">
<div class=" btn-group"> <div class=" btn-group">
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">CSV <span class="caret"></span></button> <button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">CSV <span class="caret"></span></button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li> <li>
<a class=" btn_export" tabindex="0"> <a class=" btn_export" tabindex="0">
<span>{% trans "Export" %}</span> <span>{% trans "Export" %}</span>
</a> </a>
</li> </li>
<li> <li>
<a class=" btn_import" data-toggle="modal" data-target="#import_modal" tabindex="0"> <a class=" btn_import" data-toggle="modal" data-target="#import_modal" tabindex="0">
<span>{% trans "Import" %}</span> <span>{% trans "Import" %}</span>
</a> </a>
</li> </li>
<li> <li>
<a class=" btn_update" data-toggle="modal" data-target="#update_modal" tabindex="0"> <a class=" btn_update" data-toggle="modal" data-target="#update_modal" tabindex="0">
<span>{% trans "Update" %}</span> <span>{% trans "Update" %}</span>
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block table_container %} {% block table_container %}
......
...@@ -6,11 +6,12 @@ from django.dispatch import receiver ...@@ -6,11 +6,12 @@ from django.dispatch import receiver
from django.db.backends.signals import connection_created from django.db.backends.signals import connection_created
@receiver(connection_created, dispatch_uid="my_unique_identifier") @receiver(connection_created)
def on_db_connection_ready(sender, **kwargs): def on_db_connection_ready(sender, **kwargs):
from .signals import django_ready from .signals import django_ready
if 'migrate' not in sys.argv: if 'migrate' not in sys.argv:
django_ready.send(CommonConfig) django_ready.send(CommonConfig)
connection_created.disconnect(on_db_connection_ready)
class CommonConfig(AppConfig): class CommonConfig(AppConfig):
......
...@@ -21,6 +21,7 @@ class Organization(models.Model): ...@@ -21,6 +21,7 @@ class Organization(models.Model):
ROOT_NAME = 'ROOT' ROOT_NAME = 'ROOT'
DEFAULT_ID = 'DEFAULT' DEFAULT_ID = 'DEFAULT'
DEFAULT_NAME = 'DEFAULT' DEFAULT_NAME = 'DEFAULT'
_user_admin_orgs = None
class Meta: class Meta:
verbose_name = _("Organization") verbose_name = _("Organization")
...@@ -92,6 +93,8 @@ class Organization(models.Model): ...@@ -92,6 +93,8 @@ class Organization(models.Model):
@classmethod @classmethod
def get_user_admin_orgs(cls, user): def get_user_admin_orgs(cls, user):
if cls._user_admin_orgs and user.id in cls._user_admin_orgs:
return cls._user_admin_orgs[user.id]
admin_orgs = [] admin_orgs = []
if user.is_anonymous: if user.is_anonymous:
return admin_orgs return admin_orgs
...@@ -100,6 +103,11 @@ class Organization(models.Model): ...@@ -100,6 +103,11 @@ class Organization(models.Model):
admin_orgs.append(cls.default()) admin_orgs.append(cls.default())
elif user.is_org_admin: elif user.is_org_admin:
admin_orgs = user.admin_orgs.all() admin_orgs = user.admin_orgs.all()
if cls._user_admin_orgs is None:
cls._user_admin_orgs = {user.id: admin_orgs}
else:
cls._user_admin_orgs[user.id] = admin_orgs
return admin_orgs return admin_orgs
@classmethod @classmethod
......
...@@ -41,3 +41,8 @@ def on_org_user_changed(sender, instance=None, **kwargs): ...@@ -41,3 +41,8 @@ def on_org_user_changed(sender, instance=None, **kwargs):
for user_group in user_groups: for user_group in user_groups:
user_group.users.remove(user) user_group.users.remove(user)
set_current_org(old_org) set_current_org(old_org)
@receiver(m2m_changed, sender=Organization.admins.through)
def on_org_admin_change(sender, **kwargs):
Organization._user_admin_orgs = None
...@@ -263,6 +263,7 @@ class UserGrantedNodesWithAssetsAsTreeApi(UserPermissionCacheMixin, ListAPIView) ...@@ -263,6 +263,7 @@ class UserGrantedNodesWithAssetsAsTreeApi(UserPermissionCacheMixin, ListAPIView)
system_users=self.system_user_id system_users=self.system_user_id
) )
nodes = util.get_nodes_with_assets() nodes = util.get_nodes_with_assets()
print(list(nodes.keys()))
for node, assets in nodes.items(): for node, assets in nodes.items():
data = parse_node_to_tree_node(node) data = parse_node_to_tree_node(node)
queryset.append(data) queryset.append(data)
......
...@@ -180,7 +180,7 @@ class GenerateTree: ...@@ -180,7 +180,7 @@ class GenerateTree:
return dict(nodes) return dict(nodes)
def get_nodes(self): def get_nodes(self):
return self.nodes.keys() return list(self.nodes.keys())
def get_user_permissions(user, include_group=True): def get_user_permissions(user, include_group=True):
...@@ -256,9 +256,13 @@ class AssetPermissionCacheMixin: ...@@ -256,9 +256,13 @@ class AssetPermissionCacheMixin:
) )
@property @property
def node_key(self): def node_asset_key(self):
return self.get_cache_key('NODES_WITH_ASSETS') return self.get_cache_key('NODES_WITH_ASSETS')
@property
def node_key(self):
return self.get_cache_key('NODES')
@property @property
def asset_key(self): def asset_key(self):
key = self.get_cache_key('ASSETS') key = self.get_cache_key('ASSETS')
...@@ -268,54 +272,47 @@ class AssetPermissionCacheMixin: ...@@ -268,54 +272,47 @@ class AssetPermissionCacheMixin:
def system_key(self): def system_key(self):
return self.get_cache_key('SYSTEM_USER') return self.get_cache_key('SYSTEM_USER')
def get_assets_from_cache(self): def get_resource_from_cache(self, resource):
cached = cache.get(self.asset_key) key_map = {
if not cached: "assets": self.asset_key,
self.update_cache() "nodes": self.node_key,
cached = cache.get(self.asset_key) "nodes_with_assets": self.node_asset_key,
return cached "system_users": self.system_key
}
def get_nodes_with_assets_from_cache(self): key = key_map.get(resource)
cached = cache.get(self.node_key) if not key:
raise ValueError("Not a valid resource: {}".format(resource))
cached = cache.get(key)
if not cached: if not cached:
self.update_cache() self.update_cache()
cached = cache.get(self.node_key) cached = cache.get(key)
return cached return cached
def get_nodes_with_assets(self): def get_resource(self, resource):
if self._is_using_cache(): if self._is_using_cache():
return self.get_nodes_with_assets_from_cache() return self.get_resource_from_cache(resource)
elif self._is_refresh_cache(): elif self._is_refresh_cache():
self.expire_cache() self.expire_cache()
return self.get_nodes_with_assets_from_cache() data = self.get_resource_from_cache(resource)
return data
else: else:
return self.get_nodes_with_assets_without_cache() return self.get_resource_without_cache(resource)
def get_system_user_from_cache(self): def get_resource_without_cache(self, resource):
cached = cache.get(self.system_key) attr = 'get_{}_without_cache'.format(resource)
if not cached: return getattr(self, attr)()
self.update_cache()
cached = cache.get(self.system_key) def get_nodes_with_assets(self):
return cached return self.get_resource("nodes_with_assets")
def get_assets(self): def get_assets(self):
if self._is_using_cache(): return self.get_resource("assets")
return self.get_assets_from_cache()
elif self._is_refresh_cache(): def get_nodes(self):
self.expire_cache() return self.get_resource("nodes")
return self.get_assets_from_cache()
else:
self.expire_cache()
return self.get_assets_without_cache()
def get_system_users(self): def get_system_users(self):
if self._is_using_cache(): return self.get_resource("system_users")
return self.get_system_user_from_cache()
elif self._is_refresh_cache():
self.expire_cache()
return self.get_system_user_from_cache()
else:
return self.get_system_user_without_cache()
def get_meta_cache_key(self): def get_meta_cache_key(self):
cache_key = self.CACHE_META_KEY_PREFIX + '{obj_id}_{filter_id}' cache_key = self.CACHE_META_KEY_PREFIX + '{obj_id}_{filter_id}'
...@@ -332,6 +329,17 @@ class AssetPermissionCacheMixin: ...@@ -332,6 +329,17 @@ class AssetPermissionCacheMixin:
# print("Meta id: {}".format(meta["id"])) # print("Meta id: {}".format(meta["id"]))
return meta return meta
def update_cache(self):
assets = self.get_resource_without_cache("assets")
nodes_with_assets = self.get_resource_without_cache("nodes_with_assets")
system_users = self.get_resource_without_cache("system_users")
nodes = self.get_resource_without_cache("nodes")
cache.set(self.asset_key, assets, self.CACHE_TIME)
cache.set(self.node_asset_key, nodes_with_assets, self.CACHE_TIME)
cache.set(self.system_key, system_users, self.CACHE_TIME)
cache.set(self.node_key, nodes, self.CACHE_TIME)
self.set_meta_to_cache()
def set_meta_to_cache(self): def set_meta_to_cache(self):
key = self.get_meta_cache_key() key = self.get_meta_cache_key()
meta = { meta = {
...@@ -348,15 +356,6 @@ class AssetPermissionCacheMixin: ...@@ -348,15 +356,6 @@ class AssetPermissionCacheMixin:
key = cache_key.format(obj_id=self.obj_id) key = cache_key.format(obj_id=self.obj_id)
cache.delete_pattern(key) cache.delete_pattern(key)
def update_cache(self):
assets = self.get_assets_without_cache()
nodes = self.get_nodes_with_assets_without_cache()
system_users = self.get_system_user_without_cache()
cache.set(self.asset_key, assets, self.CACHE_TIME)
cache.set(self.node_key, nodes, self.CACHE_TIME)
cache.set(self.system_key, system_users, self.CACHE_TIME)
self.set_meta_to_cache()
def expire_cache(self): def expire_cache(self):
""" """
因为 获取用户的节点,资产,系统用户等都能会缓存,这里会清理所有与该对象有关的 因为 获取用户的节点,资产,系统用户等都能会缓存,这里会清理所有与该对象有关的
...@@ -378,15 +377,6 @@ class AssetPermissionCacheMixin: ...@@ -378,15 +377,6 @@ class AssetPermissionCacheMixin:
key = cls.CACHE_KEY_PREFIX + '*' key = cls.CACHE_KEY_PREFIX + '*'
cache.delete_pattern(key) cache.delete_pattern(key)
def get_assets_without_cache(self):
raise NotImplementedError()
def get_nodes_with_assets_without_cache(self):
raise NotImplementedError()
def get_system_user_without_cache(self):
raise NotImplementedError()
class AssetPermissionUtil(AssetPermissionCacheMixin): class AssetPermissionUtil(AssetPermissionCacheMixin):
get_permissions_map = { get_permissions_map = {
...@@ -396,8 +386,10 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): ...@@ -396,8 +386,10 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
"Node": get_node_permissions, "Node": get_node_permissions,
"SystemUser": get_system_user_permissions, "SystemUser": get_system_user_permissions,
} }
assets_prefetch = ('id', 'hostname', 'ip', "platform", "domain_id", assets_only = (
"comment", "is_active", "os", "org_id") 'id', 'hostname', 'ip', "platform", "domain_id",
'comment', 'is_active', 'os', 'org_id'
)
def __init__(self, obj, cache_policy='0'): def __init__(self, obj, cache_policy='0'):
self.object = obj self.object = obj
...@@ -411,6 +403,8 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): ...@@ -411,6 +403,8 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
self.change_org_if_need() self.change_org_if_need()
self.nodes = None self.nodes = None
self._nodes = None self._nodes = None
self._assets_direct = None
self._nodes_direct = None
@staticmethod @staticmethod
def change_org_if_need(): def change_org_if_need():
...@@ -438,6 +432,8 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): ...@@ -438,6 +432,8 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
返回用户/组授权规则直接关联的节点 返回用户/组授权规则直接关联的节点
:return: {node1: {system_user1: {'actions': set()},}} :return: {node1: {system_user1: {'actions': set()},}}
""" """
if self._nodes_direct:
return self._nodes_direct
nodes = defaultdict(lambda: defaultdict(int)) nodes = defaultdict(lambda: defaultdict(int))
for perm in self.permissions: for perm in self.permissions:
actions = [perm.actions] actions = [perm.actions]
...@@ -446,9 +442,10 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): ...@@ -446,9 +442,10 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
for node, system_user, action in itertools.product(_nodes, system_users, actions): for node, system_user, action in itertools.product(_nodes, system_users, actions):
nodes[node][system_user] |= action nodes[node][system_user] |= action
self.tree.add_nodes(nodes) self.tree.add_nodes(nodes)
self._nodes_direct = nodes
return nodes return nodes
def get_nodes(self): def get_nodes_without_cache(self):
self.get_assets_direct() self.get_assets_direct()
return self.tree.get_nodes() return self.tree.get_nodes()
...@@ -458,15 +455,18 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): ...@@ -458,15 +455,18 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
返回用户授权规则直接关联的资产 返回用户授权规则直接关联的资产
:return: {asset1: {system_user1: 1,}} :return: {asset1: {system_user1: 1,}}
""" """
if self._assets_direct:
return self._assets_direct
assets = defaultdict(lambda: defaultdict(int)) assets = defaultdict(lambda: defaultdict(int))
for perm in self.permissions: for perm in self.permissions:
actions = [perm.actions] actions = [perm.actions]
_assets = perm.assets.all().prefetch_related(*self.assets_prefetch) _assets = perm.assets.all().only(*self.assets_only)
system_users = perm.system_users.all() system_users = perm.system_users.all()
iterable = itertools.product(_assets, system_users, actions) iterable = itertools.product(_assets, system_users, actions)
for asset, system_user, action in iterable: for asset, system_user, action in iterable:
assets[asset][system_user] |= action assets[asset][system_user] |= action
self.tree.add_assets(assets) self.tree.add_assets(assets)
self._assets_direct = assets
return assets return assets
#@timeit #@timeit
...@@ -476,6 +476,7 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): ...@@ -476,6 +476,7 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
""" """
if self._assets: if self._assets:
return self._assets return self._assets
self.get_assets_direct()
nodes = self.get_nodes_direct() nodes = self.get_nodes_direct()
pattern = set() pattern = set()
for node in nodes: for node in nodes:
...@@ -484,7 +485,7 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): ...@@ -484,7 +485,7 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
if pattern: if pattern:
assets = Asset.objects.filter(nodes__key__regex=pattern)\ assets = Asset.objects.filter(nodes__key__regex=pattern)\
.prefetch_related('nodes', "protocols")\ .prefetch_related('nodes', "protocols")\
.only(*self.assets_prefetch)\ .only(*self.assets_only)\
.distinct() .distinct()
else: else:
assets = [] assets = []
...@@ -501,9 +502,11 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): ...@@ -501,9 +502,11 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
:return: :return:
""" """
self.get_assets_without_cache() self.get_assets_without_cache()
return self.tree.get_nodes_with_assets() nodes_assets = self.tree.get_nodes_with_assets()
print(nodes_assets.keys())
return nodes_assets
def get_system_user_without_cache(self): def get_system_users_without_cache(self):
system_users = set() system_users = set()
permissions = self.permissions.prefetch_related('system_users') permissions = self.permissions.prefetch_related('system_users')
for perm in permissions: for perm in permissions:
......
...@@ -24,6 +24,7 @@ def refresh_settings_on_changed(sender, instance=None, **kwargs): ...@@ -24,6 +24,7 @@ def refresh_settings_on_changed(sender, instance=None, **kwargs):
@receiver(django_ready, dispatch_uid="my_unique_identifier") @receiver(django_ready, dispatch_uid="my_unique_identifier")
def monkey_patch_settings(sender, **kwargs): def monkey_patch_settings(sender, **kwargs):
logger.debug("Monkey patch settings")
cache_key_prefix = '_SETTING_' cache_key_prefix = '_SETTING_'
custom_need_cache_settings = [ custom_need_cache_settings = [
'AUTHENTICATION_BACKENDS', 'TERMINAL_HOST_KEY', 'AUTHENTICATION_BACKENDS', 'TERMINAL_HOST_KEY',
...@@ -77,7 +78,6 @@ def monkey_patch_settings(sender, **kwargs): ...@@ -77,7 +78,6 @@ def monkey_patch_settings(sender, **kwargs):
@receiver(django_ready) @receiver(django_ready)
def auto_generate_terminal_host_key(sender, **kwargs): def auto_generate_terminal_host_key(sender, **kwargs):
try: try:
print("Auto gen host key")
if Setting.objects.filter(name='TERMINAL_HOST_KEY').exists(): if Setting.objects.filter(name='TERMINAL_HOST_KEY').exists():
return return
private_key, public_key = ssh_key_gen() private_key, public_key = ssh_key_gen()
......
...@@ -648,8 +648,6 @@ jumpserver.initServerSideDataTable = function (options) { ...@@ -648,8 +648,6 @@ jumpserver.initServerSideDataTable = function (options) {
$.each(rows, function (id, row) { $.each(rows, function (id, row) {
table.selected_rows.push(row); table.selected_rows.push(row);
if (row.id && $.inArray(row.id, table.selected) === -1){ if (row.id && $.inArray(row.id, table.selected) === -1){
console.log(table)
console.log(table.selected);
table.selected.push(row.id) table.selected.push(row.id)
} }
}) })
...@@ -1096,3 +1094,8 @@ function objectAttrsIsBool(obj, attrs) { ...@@ -1096,3 +1094,8 @@ function objectAttrsIsBool(obj, attrs) {
} }
}) })
} }
function formatDateAsCN(d) {
var date = new Date(d);
return date.toISOString().replace("T", " ").replace(/\..*/, "");
}
...@@ -55,7 +55,7 @@ class SessionViewSet(BulkModelViewSet): ...@@ -55,7 +55,7 @@ class SessionViewSet(BulkModelViewSet):
return super().perform_create(serializer) return super().perform_create(serializer)
class CommandViewSet(viewsets.ViewSet): class CommandViewSet(viewsets.ModelViewSet):
"""接受app发送来的command log, 格式如下 """接受app发送来的command log, 格式如下
{ {
"user": "admin", "user": "admin",
...@@ -70,10 +70,14 @@ class CommandViewSet(viewsets.ViewSet): ...@@ -70,10 +70,14 @@ class CommandViewSet(viewsets.ViewSet):
""" """
command_store = get_command_storage() command_store = get_command_storage()
serializer_class = SessionCommandSerializer serializer_class = SessionCommandSerializer
pagination_class = LimitOffsetPagination
permission_classes = (IsOrgAdminOrAppUser | IsAuditor,) permission_classes = (IsOrgAdminOrAppUser | IsAuditor,)
filter_fields = ("asset", "system_user", "user", "input")
def get_queryset(self): def get_queryset(self):
self.command_store.filter(**dict(self.request.query_params)) multi_command_storage = get_multi_command_storage()
queryset = multi_command_storage.filter()
return queryset
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)
...@@ -88,12 +92,6 @@ class CommandViewSet(viewsets.ViewSet): ...@@ -88,12 +92,6 @@ class CommandViewSet(viewsets.ViewSet):
logger.error(msg) logger.error(msg)
return Response({"msg": msg}, status=401) return Response({"msg": msg}, status=401)
def list(self, request, *args, **kwargs):
multi_command_storage = get_multi_command_storage()
queryset = multi_command_storage.filter()
serializer = self.serializer_class(queryset, many=True)
return Response(serializer.data)
class SessionReplayViewSet(viewsets.ViewSet): class SessionReplayViewSet(viewsets.ViewSet):
serializer_class = serializers.ReplaySerializer serializer_class = serializers.ReplaySerializer
......
...@@ -9,8 +9,12 @@ class CommandStore(CommandBase): ...@@ -9,8 +9,12 @@ class CommandStore(CommandBase):
self.storage_list = storage_list self.storage_list = storage_list
def filter(self, **kwargs): def filter(self, **kwargs):
queryset = [] if len(self.storage_list) == 1:
storage = list(self.storage_list)[0]
queryset = storage.filter(**kwargs)
return queryset
queryset = []
for storage in self.storage_list: for storage in self.storage_list:
queryset.extend(storage.filter(**kwargs)) queryset.extend(storage.filter(**kwargs))
return sorted(queryset, key=lambda command: command.timestamp, reverse=True) return sorted(queryset, key=lambda command: command.timestamp, reverse=True)
......
...@@ -8,109 +8,65 @@ ...@@ -8,109 +8,65 @@
<link href="{% static 'css/plugins/select2/select2.min.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> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<style> <style>
#search_btn { .toggle {
margin-bottom: 0; cursor: pointer;
}
.detail-key {
width: 70px;
} }
</style> </style>
{% endblock %} {% endblock %}
{% block content_left_head %} {% block table_pagination %}
{% endblock %} {% endblock %}
{% block table_search %} {% block table_search %}
<form id="search_form" method="get" action="" class="pull-right form-inline" style="padding-bottom: 8px">
<div class="form-group" id="date">
<div class="input-daterange input-group" 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;" 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;" name="date_to" value="{{ date_to|date:'Y-m-d' }}">
</div>
</div>
<div class="input-group">
<select class="select2 form-control" name="user">
<option value="">{% trans 'User' %}</option>
{% for u in user_list %}
<option value="{{ u }}" {% if u == user %} selected {% endif %}>{{ u }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<select class="select2 form-control" name="asset">
<option value="">{% trans 'Asset' %}</option>
{% for a in asset_list %}
<option value="{{ a }}" {% if a == asset %} selected {% endif %}>{{ a }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<select class="select2 form-control" name="system_user">
<option value="">{% trans 'System user' %}</option>
{% for s in system_user_list %}
<option value="{{ s }}" {% if s == system_user %} selected {% endif %}>{{ s }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<input type="text" class="form-control input-sm" name="command" placeholder="{% trans 'Command' %}" value="{{ command }}">
</div>
<div class="input-group">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
{% trans 'Search' %}
</button>
</div>
</div>
</form>
{% endblock %} {% endblock %}
{% block table_container %} {% block table_container %}
<table class="footable table table-stripped table-bordered toggle-arrow-tiny" data-page="false" > <table class="table table-striped table-bordered table-hover" id="command_table" data-page="false" >
<thead> <thead>
<tr> <tr>
<th data-toggle="true">ID</th> <th></th>
<th>{% trans 'Command' %}</th> <th>{% trans 'Command' %}</th>
<th>{% trans 'User' %}</th> <th>{% trans 'User' %}</th>
<th>{% trans 'Asset' %}</th> <th>{% trans 'Asset' %}</th>
<th>{% trans 'System user'%}</th> <th>{% trans 'System user'%}</th>
<th>{% trans 'Session' %}</th> <th>{% trans 'Session' %}</th>
<th>{% trans 'Datetime' %}</th> <th>{% trans 'Datetime' %}</th>
<th data-hide="all"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for command in command_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ command.input }}</td>
<td>{{ command.user }}</td>
<td>{{ command.asset }}</td>
<td>{{ command.system_user }}</td>
<td><a href="{% url 'terminal:session-detail' pk=command.session %}">{% trans "Goto" %}</a></td>
<td>{{ command.timestamp|ts_to_date }}</td>
<td><pre style="border: none; background: none">{{ command.output }}</pre></td>
</tr>
{% endfor %}
</tbody> </tbody>
</table> </table>
<div id="actions" class=""> <div id="actions" class="hide">
<div class="input-group"> <div class="input-group">
<select class="form-control m-b" style="width: auto" id="slct_bulk_update"> <select class="form-control m-b" style="width: auto" id="slct_bulk_update">
<option value="export">{% trans 'Export command' %}</option> <option value="export">{% trans 'Export command' %}</option>
</select> </select>
<div class="input-group-btn pull-left" style="padding-left: 5px;"> <div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary"> <button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
{% trans 'Submit' %} {% trans 'Submit' %}
</button> </button>
</div> </div>
</div> </div>
</div> </div>
{% endblock %}
<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="asset">{% trans 'Asset' %}</a></li>
<li><a class="search-item" data-value="system_user">{% trans 'System user' %}</a></li>
<li><a class="search-item" data-value="command">{% trans 'Command' %}</a></li>
</ul>
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script src="{% static "js/plugins/footable/footable.all.min.js" %}"></script> <script src="{% static "js/plugins/footable/footable.all.min.js" %}"></script>
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script> <script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
<script> <script>
$(document).ready(function () { $(document).ready(function () {
$('.footable').footable(); $('.footable').footable();
$('.select2').select2({ $('.select2').select2({
...@@ -125,6 +81,7 @@ $(document).ready(function () { ...@@ -125,6 +81,7 @@ $(document).ready(function () {
calendarWeeks: true, calendarWeeks: true,
autoclose: true autoclose: true
}); });
initTable();
}) })
.on('click', '#btn_bulk_update', function(){ .on('click', '#btn_bulk_update', function(){
var action = $('#slct_bulk_update').val(); var action = $('#slct_bulk_update').val();
...@@ -137,7 +94,103 @@ $(document).ready(function () { ...@@ -137,7 +94,103 @@ $(document).ready(function () {
var pathname = window.location.pathname + 'export/'; var pathname = window.location.pathname + 'export/';
var url = pathname + params; var url = pathname + params;
window.open(url); window.open(url);
}); }).on("click", '#command_table_filter input', function (e) {
e.preventDefault();
e.stopPropagation();
var offset1 = $('#command_table_filter input').offset();
var x = offset1.left;
var y = offset1.top;
console.log(x, y)
var offset = $(".search-help").parent().offset();
x -= offset.left;
y -= offset.top;
x += 18;
y += 80;
console.log(x, y)
$('.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 = $("#command_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", "document", function () {
$('.dropdown-menu.search-help').hide();
})
.on('click', '.toggle', function (e) {
e.preventDefault();
var detailRows = [];
var tr = $(this).closest('tr');
var row = table.row(tr);
var idx = $.inArray(tr.attr('id'), detailRows);
if (row.child.isShown()) {
tr.removeClass('details');
$(this).children('i:first-child').removeClass('fa-angle-down').addClass('fa-angle-right');
row.child.hide();
// Remove from the 'open' array
detailRows.splice(idx, 1);
} else {
tr.addClass('details');
$(this).children('i:first-child').removeClass('fa-angle-right').addClass('fa-angle-down');
row.child(format(row.data())).show();
// Add to the 'open' array
if (idx === -1) {
detailRows.push(tr.attr('id'));
}
}
}).on('click', 'body', function (e) {
$('.dropdown-menu.search-help').hide()
})
function format(d) {
var output = $("<pre style='border: none; background: none'></pre>");
output.append(d.output);
return output
}
function initTable() {
var options = {
ele: $('#command_table'),
columnDefs: [
{targets: 0, createdCell: function (td, cellData, rowData) {
$(td).addClass("toggle");
$(td).html("<i class='fa fa-angle-right'></i>");
}},
{targets: 5, createdCell: function (td, cellData) {
var data = '<a href="{% url "terminal:session-detail" pk=DEFAULT_PK %}">{% trans "Goto" %}</a>'
.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(data);
}},
{targets: 6, createdCell: function (td, cellData) {
var data = formatDateAsCN(cellData*1000);
$(td).html(data);
}},
],
toggle: true,
ajax_url: '{% url "api-terminal:command-list" %}',
columns: [
{data: "id"}, {data: "input", orderable: false}, {data: "user", orderable: false},
{data: "asset"}, {data: "system_user"},
{data: "session"}, {data: "timestamp", width: "160px"},
],
select: {},
op_html: $('#actions').html()
};
table = jumpserver.initServerSideDataTable(options);
return table
}
</script> </script>
{% endblock %} {% endblock %}
......
...@@ -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('hostname', flat=True) return Asset.objects.values_list()
def get_session_user_list(): def get_session_user_list():
......
...@@ -23,26 +23,13 @@ class CommandListView(DatetimeSearchMixin, PermissionsMixin, ListView): ...@@ -23,26 +23,13 @@ class CommandListView(DatetimeSearchMixin, PermissionsMixin, ListView):
template_name = "terminal/command_list.html" template_name = "terminal/command_list.html"
context_object_name = 'command_list' context_object_name = 'command_list'
paginate_by = settings.DISPLAY_PER_PAGE paginate_by = settings.DISPLAY_PER_PAGE
command = user = asset = system_user = ""
date_from = date_to = None date_from = date_to = None
permission_classes = [IsOrgAdmin | IsAuditor] permission_classes = [IsOrgAdmin | IsAuditor]
def get_queryset(self): def get_queryset(self):
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 = dict()
filter_kwargs['date_from'] = self.date_from filter_kwargs['date_from'] = self.date_from
filter_kwargs['date_to'] = self.date_to 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) queryset = common_storage.filter(**filter_kwargs)
return queryset return queryset
...@@ -50,15 +37,8 @@ class CommandListView(DatetimeSearchMixin, PermissionsMixin, ListView): ...@@ -50,15 +37,8 @@ class CommandListView(DatetimeSearchMixin, PermissionsMixin, ListView):
context = { context = {
'app': _('Sessions'), 'app': _('Sessions'),
'action': _('Command list'), 'action': _('Command list'),
'user_list': utils.get_session_user_list(),
'asset_list': utils.get_session_asset_list(),
'system_user_list': utils.get_session_system_user_list(),
'command': self.command,
'date_from': self.date_from, 'date_from': self.date_from,
'date_to': self.date_to, 'date_to': self.date_to,
'user': self.user,
'asset': self.asset,
'system_user': self.system_user,
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
......
...@@ -2,27 +2,27 @@ ...@@ -2,27 +2,27 @@
{% load i18n static %} {% load i18n static %}
{% block table_search %} {% block table_search %}
<div class="" style="float: right"> <div class="" style="float: right">
<div class=" btn-group"> <div class=" btn-group">
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">CSV <span class="caret"></span></button> <button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">CSV <span class="caret"></span></button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li> <li>
<a class=" btn_export" tabindex="0"> <a class=" btn_export" tabindex="0">
<span>{% trans "Export" %}</span> <span>{% trans "Export" %}</span>
</a> </a>
</li> </li>
<li> <li>
<a class=" btn_import" data-toggle="modal" data-target="#import_modal" tabindex="0"> <a class=" btn_import" data-toggle="modal" data-target="#import_modal" tabindex="0">
<span>{% trans "Import" %}</span> <span>{% trans "Import" %}</span>
</a> </a>
</li> </li>
<li> <li>
<a class=" btn_update" data-toggle="modal" data-target="#update_modal" tabindex="0"> <a class=" btn_update" data-toggle="modal" data-target="#update_modal" tabindex="0">
<span>{% trans "Update" %}</span> <span>{% trans "Update" %}</span>
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block table_container %} {% block table_container %}
<div class="pull-left m-r-5"><a href="{% url 'users:user-group-create' %}" class="btn btn-sm btn-primary ">{% trans "Create user group" %}</a></div> <div class="pull-left m-r-5"><a href="{% url 'users:user-group-create' %}" class="btn btn-sm btn-primary ">{% trans "Create user group" %}</a></div>
......
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