Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
J
jumpserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
jumpserver
Commits
b9f82fd0
Commit
b9f82fd0
authored
Jul 02, 2019
by
ibuler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[Update] 优化命令记录列表
parent
1b44172b
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
264 additions
and
208 deletions
+264
-208
_asset_user_list.html
apps/assets/templates/assets/_asset_user_list.html
+2
-2
system_user_list.html
apps/assets/templates/assets/system_user_list.html
+21
-21
apps.py
apps/common/apps.py
+2
-1
models.py
apps/orgs/models.py
+8
-0
signals_handler.py
apps/orgs/signals_handler.py
+5
-0
user_permission.py
apps/perms/api/user_permission.py
+1
-0
asset_permission.py
apps/perms/utils/asset_permission.py
+65
-62
signals_handler.py
apps/settings/signals_handler.py
+1
-1
jumpserver.js
apps/static/js/jumpserver.js
+5
-2
session.py
apps/terminal/api/session.py
+6
-8
multi.py
apps/terminal/backends/command/multi.py
+5
-1
command_list.html
apps/terminal/templates/terminal/command_list.html
+121
-68
utils.py
apps/terminal/utils.py
+1
-1
command.py
apps/terminal/views/command.py
+0
-20
user_group_list.html
apps/users/templates/users/user_group_list.html
+21
-21
No files found.
apps/assets/templates/assets/_asset_user_list.html
View file @
b9f82fd0
...
...
@@ -60,8 +60,8 @@ function initAssetUserTable() {
},
{
targets
:
6
,
createdCell
:
function
(
td
,
cellData
)
{
var
dat
e
=
new
Date
(
cellData
);
$
(
td
).
html
(
dat
e
.
toLocaleString
()
);
var
dat
a
=
formatDateAsCN
(
cellData
);
$
(
td
).
html
(
dat
a
);
},
},
{
...
...
apps/assets/templates/assets/system_user_list.html
View file @
b9f82fd0
...
...
@@ -15,27 +15,27 @@
{% block table_search %}
<div
class=
""
style=
"float: right"
>
<div
class=
" btn-group"
>
<button
data-toggle=
"dropdown"
class=
"btn btn-default btn-sm dropdown-toggle"
>
CSV
<span
class=
"caret"
></span></button>
<ul
class=
"dropdown-menu"
>
<li>
<a
class=
" btn_export"
tabindex=
"0"
>
<span>
{% trans "Export" %}
</span>
</a>
</li>
<li>
<a
class=
" btn_import"
data-toggle=
"modal"
data-target=
"#import_modal"
tabindex=
"0"
>
<span>
{% trans "Import" %}
</span>
</a>
</li>
<li>
<a
class=
" btn_update"
data-toggle=
"modal"
data-target=
"#update_modal"
tabindex=
"0"
>
<span>
{% trans "Update" %}
</span>
</a>
</li>
</ul>
</div>
</div>
<div
class=
" btn-group"
>
<button
data-toggle=
"dropdown"
class=
"btn btn-default btn-sm dropdown-toggle"
>
CSV
<span
class=
"caret"
></span></button>
<ul
class=
"dropdown-menu"
>
<li>
<a
class=
" btn_export"
tabindex=
"0"
>
<span>
{% trans "Export" %}
</span>
</a>
</li>
<li>
<a
class=
" btn_import"
data-toggle=
"modal"
data-target=
"#import_modal"
tabindex=
"0"
>
<span>
{% trans "Import" %}
</span>
</a>
</li>
<li>
<a
class=
" btn_update"
data-toggle=
"modal"
data-target=
"#update_modal"
tabindex=
"0"
>
<span>
{% trans "Update" %}
</span>
</a>
</li>
</ul>
</div>
</div>
{% endblock %}
{% block table_container %}
...
...
apps/common/apps.py
View file @
b9f82fd0
...
...
@@ -6,11 +6,12 @@ from django.dispatch import receiver
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
):
from
.signals
import
django_ready
if
'migrate'
not
in
sys
.
argv
:
django_ready
.
send
(
CommonConfig
)
connection_created
.
disconnect
(
on_db_connection_ready
)
class
CommonConfig
(
AppConfig
):
...
...
apps/orgs/models.py
View file @
b9f82fd0
...
...
@@ -21,6 +21,7 @@ class Organization(models.Model):
ROOT_NAME
=
'ROOT'
DEFAULT_ID
=
'DEFAULT'
DEFAULT_NAME
=
'DEFAULT'
_user_admin_orgs
=
None
class
Meta
:
verbose_name
=
_
(
"Organization"
)
...
...
@@ -92,6 +93,8 @@ class Organization(models.Model):
@classmethod
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
=
[]
if
user
.
is_anonymous
:
return
admin_orgs
...
...
@@ -100,6 +103,11 @@ class Organization(models.Model):
admin_orgs
.
append
(
cls
.
default
())
elif
user
.
is_org_admin
:
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
@classmethod
...
...
apps/orgs/signals_handler.py
View file @
b9f82fd0
...
...
@@ -41,3 +41,8 @@ def on_org_user_changed(sender, instance=None, **kwargs):
for
user_group
in
user_groups
:
user_group
.
users
.
remove
(
user
)
set_current_org
(
old_org
)
@receiver
(
m2m_changed
,
sender
=
Organization
.
admins
.
through
)
def
on_org_admin_change
(
sender
,
**
kwargs
):
Organization
.
_user_admin_orgs
=
None
apps/perms/api/user_permission.py
View file @
b9f82fd0
...
...
@@ -263,6 +263,7 @@ class UserGrantedNodesWithAssetsAsTreeApi(UserPermissionCacheMixin, ListAPIView)
system_users
=
self
.
system_user_id
)
nodes
=
util
.
get_nodes_with_assets
()
print
(
list
(
nodes
.
keys
()))
for
node
,
assets
in
nodes
.
items
():
data
=
parse_node_to_tree_node
(
node
)
queryset
.
append
(
data
)
...
...
apps/perms/utils/asset_permission.py
View file @
b9f82fd0
...
...
@@ -180,7 +180,7 @@ class GenerateTree:
return
dict
(
nodes
)
def
get_nodes
(
self
):
return
self
.
nodes
.
keys
(
)
return
list
(
self
.
nodes
.
keys
()
)
def
get_user_permissions
(
user
,
include_group
=
True
):
...
...
@@ -256,9 +256,13 @@ class AssetPermissionCacheMixin:
)
@property
def
node_key
(
self
):
def
node_
asset_
key
(
self
):
return
self
.
get_cache_key
(
'NODES_WITH_ASSETS'
)
@property
def
node_key
(
self
):
return
self
.
get_cache_key
(
'NODES'
)
@property
def
asset_key
(
self
):
key
=
self
.
get_cache_key
(
'ASSETS'
)
...
...
@@ -268,54 +272,47 @@ class AssetPermissionCacheMixin:
def
system_key
(
self
):
return
self
.
get_cache_key
(
'SYSTEM_USER'
)
def
get_assets_from_cache
(
self
):
cached
=
cache
.
get
(
self
.
asset_key
)
if
not
cached
:
self
.
update_cache
()
cached
=
cache
.
get
(
self
.
asset_key
)
return
cached
def
get_nodes_with_assets_from_cache
(
self
):
cached
=
cache
.
get
(
self
.
node_key
)
def
get_resource_from_cache
(
self
,
resource
):
key_map
=
{
"assets"
:
self
.
asset_key
,
"nodes"
:
self
.
node_key
,
"nodes_with_assets"
:
self
.
node_asset_key
,
"system_users"
:
self
.
system_key
}
key
=
key_map
.
get
(
resource
)
if
not
key
:
raise
ValueError
(
"Not a valid resource: {}"
.
format
(
resource
))
cached
=
cache
.
get
(
key
)
if
not
cached
:
self
.
update_cache
()
cached
=
cache
.
get
(
self
.
node_
key
)
cached
=
cache
.
get
(
key
)
return
cached
def
get_
nodes_with_assets
(
self
):
def
get_
resource
(
self
,
resource
):
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
():
self
.
expire_cache
()
return
self
.
get_nodes_with_assets_from_cache
()
data
=
self
.
get_resource_from_cache
(
resource
)
return
data
else
:
return
self
.
get_
nodes_with_assets_without_cache
(
)
return
self
.
get_
resource_without_cache
(
resource
)
def
get_
system_user_from_cache
(
self
):
cached
=
cache
.
get
(
self
.
system_key
)
if
not
cached
:
self
.
update_cache
()
cached
=
cache
.
get
(
self
.
system_key
)
return
cached
def
get_
resource_without_cache
(
self
,
resource
):
attr
=
'get_{}_without_cache'
.
format
(
resource
)
return
getattr
(
self
,
attr
)()
def
get_nodes_with_assets
(
self
):
return
self
.
get_resource
(
"nodes_with_assets"
)
def
get_assets
(
self
):
if
self
.
_is_using_cache
():
return
self
.
get_assets_from_cache
()
elif
self
.
_is_refresh_cache
():
self
.
expire_cache
()
return
self
.
get_assets_from_cache
()
else
:
self
.
expire_cache
()
return
self
.
get_assets_without_cache
()
return
self
.
get_resource
(
"assets"
)
def
get_nodes
(
self
):
return
self
.
get_resource
(
"nodes"
)
def
get_system_users
(
self
):
if
self
.
_is_using_cache
():
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
()
return
self
.
get_resource
(
"system_users"
)
def
get_meta_cache_key
(
self
):
cache_key
=
self
.
CACHE_META_KEY_PREFIX
+
'{obj_id}_{filter_id}'
...
...
@@ -332,6 +329,17 @@ class AssetPermissionCacheMixin:
# print("Meta id: {}".format(meta["id"]))
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
):
key
=
self
.
get_meta_cache_key
()
meta
=
{
...
...
@@ -348,15 +356,6 @@ class AssetPermissionCacheMixin:
key
=
cache_key
.
format
(
obj_id
=
self
.
obj_id
)
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
):
"""
因为 获取用户的节点,资产,系统用户等都能会缓存,这里会清理所有与该对象有关的
...
...
@@ -378,15 +377,6 @@ class AssetPermissionCacheMixin:
key
=
cls
.
CACHE_KEY_PREFIX
+
'*'
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
):
get_permissions_map
=
{
...
...
@@ -396,8 +386,10 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
"Node"
:
get_node_permissions
,
"SystemUser"
:
get_system_user_permissions
,
}
assets_prefetch
=
(
'id'
,
'hostname'
,
'ip'
,
"platform"
,
"domain_id"
,
"comment"
,
"is_active"
,
"os"
,
"org_id"
)
assets_only
=
(
'id'
,
'hostname'
,
'ip'
,
"platform"
,
"domain_id"
,
'comment'
,
'is_active'
,
'os'
,
'org_id'
)
def
__init__
(
self
,
obj
,
cache_policy
=
'0'
):
self
.
object
=
obj
...
...
@@ -411,6 +403,8 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
self
.
change_org_if_need
()
self
.
nodes
=
None
self
.
_nodes
=
None
self
.
_assets_direct
=
None
self
.
_nodes_direct
=
None
@staticmethod
def
change_org_if_need
():
...
...
@@ -438,6 +432,8 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
返回用户/组授权规则直接关联的节点
:return: {node1: {system_user1: {'actions': set()},}}
"""
if
self
.
_nodes_direct
:
return
self
.
_nodes_direct
nodes
=
defaultdict
(
lambda
:
defaultdict
(
int
))
for
perm
in
self
.
permissions
:
actions
=
[
perm
.
actions
]
...
...
@@ -446,9 +442,10 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
for
node
,
system_user
,
action
in
itertools
.
product
(
_nodes
,
system_users
,
actions
):
nodes
[
node
][
system_user
]
|=
action
self
.
tree
.
add_nodes
(
nodes
)
self
.
_nodes_direct
=
nodes
return
nodes
def
get_nodes
(
self
):
def
get_nodes
_without_cache
(
self
):
self
.
get_assets_direct
()
return
self
.
tree
.
get_nodes
()
...
...
@@ -458,15 +455,18 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
返回用户授权规则直接关联的资产
:return: {asset1: {system_user1: 1,}}
"""
if
self
.
_assets_direct
:
return
self
.
_assets_direct
assets
=
defaultdict
(
lambda
:
defaultdict
(
int
))
for
perm
in
self
.
permissions
:
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
()
iterable
=
itertools
.
product
(
_assets
,
system_users
,
actions
)
for
asset
,
system_user
,
action
in
iterable
:
assets
[
asset
][
system_user
]
|=
action
self
.
tree
.
add_assets
(
assets
)
self
.
_assets_direct
=
assets
return
assets
#@timeit
...
...
@@ -476,6 +476,7 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
"""
if
self
.
_assets
:
return
self
.
_assets
self
.
get_assets_direct
()
nodes
=
self
.
get_nodes_direct
()
pattern
=
set
()
for
node
in
nodes
:
...
...
@@ -484,7 +485,7 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
if
pattern
:
assets
=
Asset
.
objects
.
filter
(
nodes__key__regex
=
pattern
)
\
.
prefetch_related
(
'nodes'
,
"protocols"
)
\
.
only
(
*
self
.
assets_
prefetch
)
\
.
only
(
*
self
.
assets_
only
)
\
.
distinct
()
else
:
assets
=
[]
...
...
@@ -501,9 +502,11 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
:return:
"""
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_user
s
_without_cache
(
self
):
system_users
=
set
()
permissions
=
self
.
permissions
.
prefetch_related
(
'system_users'
)
for
perm
in
permissions
:
...
...
apps/settings/signals_handler.py
View file @
b9f82fd0
...
...
@@ -24,6 +24,7 @@ def refresh_settings_on_changed(sender, instance=None, **kwargs):
@receiver
(
django_ready
,
dispatch_uid
=
"my_unique_identifier"
)
def
monkey_patch_settings
(
sender
,
**
kwargs
):
logger
.
debug
(
"Monkey patch settings"
)
cache_key_prefix
=
'_SETTING_'
custom_need_cache_settings
=
[
'AUTHENTICATION_BACKENDS'
,
'TERMINAL_HOST_KEY'
,
...
...
@@ -77,7 +78,6 @@ def monkey_patch_settings(sender, **kwargs):
@receiver
(
django_ready
)
def
auto_generate_terminal_host_key
(
sender
,
**
kwargs
):
try
:
print
(
"Auto gen host key"
)
if
Setting
.
objects
.
filter
(
name
=
'TERMINAL_HOST_KEY'
)
.
exists
():
return
private_key
,
public_key
=
ssh_key_gen
()
...
...
apps/static/js/jumpserver.js
View file @
b9f82fd0
...
...
@@ -648,8 +648,6 @@ jumpserver.initServerSideDataTable = function (options) {
$
.
each
(
rows
,
function
(
id
,
row
)
{
table
.
selected_rows
.
push
(
row
);
if
(
row
.
id
&&
$
.
inArray
(
row
.
id
,
table
.
selected
)
===
-
1
){
console
.
log
(
table
)
console
.
log
(
table
.
selected
);
table
.
selected
.
push
(
row
.
id
)
}
})
...
...
@@ -1096,3 +1094,8 @@ function objectAttrsIsBool(obj, attrs) {
}
})
}
function
formatDateAsCN
(
d
)
{
var
date
=
new
Date
(
d
);
return
date
.
toISOString
().
replace
(
"T"
,
" "
).
replace
(
/
\.
.*/
,
""
);
}
apps/terminal/api/session.py
View file @
b9f82fd0
...
...
@@ -55,7 +55,7 @@ class SessionViewSet(BulkModelViewSet):
return
super
()
.
perform_create
(
serializer
)
class
CommandViewSet
(
viewsets
.
ViewSet
):
class
CommandViewSet
(
viewsets
.
Model
ViewSet
):
"""接受app发送来的command log, 格式如下
{
"user": "admin",
...
...
@@ -70,10 +70,14 @@ class CommandViewSet(viewsets.ViewSet):
"""
command_store
=
get_command_storage
()
serializer_class
=
SessionCommandSerializer
pagination_class
=
LimitOffsetPagination
permission_classes
=
(
IsOrgAdminOrAppUser
|
IsAuditor
,)
filter_fields
=
(
"asset"
,
"system_user"
,
"user"
,
"input"
)
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
):
serializer
=
self
.
serializer_class
(
data
=
request
.
data
,
many
=
True
)
...
...
@@ -88,12 +92,6 @@ class CommandViewSet(viewsets.ViewSet):
logger
.
error
(
msg
)
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
):
serializer_class
=
serializers
.
ReplaySerializer
...
...
apps/terminal/backends/command/multi.py
View file @
b9f82fd0
...
...
@@ -9,8 +9,12 @@ class CommandStore(CommandBase):
self
.
storage_list
=
storage_list
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
:
queryset
.
extend
(
storage
.
filter
(
**
kwargs
))
return
sorted
(
queryset
,
key
=
lambda
command
:
command
.
timestamp
,
reverse
=
True
)
...
...
apps/terminal/templates/terminal/command_list.html
View file @
b9f82fd0
...
...
@@ -8,109 +8,65 @@
<link
href=
"{% static 'css/plugins/select2/select2.min.css' %}"
rel=
"stylesheet"
>
<script
src=
"{% static 'js/plugins/select2/select2.full.min.js' %}"
></script>
<style>
#search_btn
{
margin-bottom
:
0
;
.toggle
{
cursor
:
pointer
;
}
.detail-key
{
width
:
70px
;
}
</style>
{% endblock %}
{% block
content_left_head
%}
{% block
table_pagination
%}
{% endblock %}
{% 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 %}
{% 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>
<tr>
<th
data-toggle=
"true"
>
ID
</th>
<th
>
</th>
<th>
{% trans 'Command' %}
</th>
<th>
{% trans 'User' %}
</th>
<th>
{% trans 'Asset' %}
</th>
<th>
{% trans 'System user'%}
</th>
<th>
{% trans 'Session' %}
</th>
<th>
{% trans 'Datetime' %}
</th>
<th
data-hide=
"all"
></th>
</tr>
</thead>
<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>
</table>
<div
id=
"actions"
class=
""
>
<div
id=
"actions"
class=
"
hide
"
>
<div
class=
"input-group"
>
<select
class=
"form-control m-b"
style=
"width: auto"
id=
"slct_bulk_update"
>
<option
value=
"export"
>
{% trans 'Export command' %}
</option>
</select>
<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"
>
{% trans 'Submit' %}
</button>
<button
id=
'btn_bulk_update'
style=
"height: 32px;"
class=
"btn btn-sm btn-primary"
>
{% trans 'Submit' %}
</button>
</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 %}
<script
src=
"{% static "
js
/
plugins
/
footable
/
footable
.
all
.
min
.
js
"
%}"
></script>
<script
src=
"{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"
></script>
<script>
$
(
document
).
ready
(
function
()
{
$
(
'.footable'
).
footable
();
$
(
'.select2'
).
select2
({
...
...
@@ -125,6 +81,7 @@ $(document).ready(function () {
calendarWeeks
:
true
,
autoclose
:
true
});
initTable
();
})
.
on
(
'click'
,
'#btn_bulk_update'
,
function
(){
var
action
=
$
(
'#slct_bulk_update'
).
val
();
...
...
@@ -137,7 +94,103 @@ $(document).ready(function () {
var
pathname
=
window
.
location
.
pathname
+
'export/'
;
var
url
=
pathname
+
params
;
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>
{% endblock %}
...
...
apps/terminal/utils.py
View file @
b9f82fd0
...
...
@@ -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
(
'hostname'
,
flat
=
True
)
return
Asset
.
objects
.
values_list
()
def
get_session_user_list
():
...
...
apps/terminal/views/command.py
View file @
b9f82fd0
...
...
@@ -23,26 +23,13 @@ class CommandListView(DatetimeSearchMixin, PermissionsMixin, ListView):
template_name
=
"terminal/command_list.html"
context_object_name
=
'command_list'
paginate_by
=
settings
.
DISPLAY_PER_PAGE
command
=
user
=
asset
=
system_user
=
""
date_from
=
date_to
=
None
permission_classes
=
[
IsOrgAdmin
|
IsAuditor
]
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
[
'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
...
...
@@ -50,15 +37,8 @@ class CommandListView(DatetimeSearchMixin, PermissionsMixin, ListView):
context
=
{
'app'
:
_
(
'Sessions'
),
'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_to'
:
self
.
date_to
,
'user'
:
self
.
user
,
'asset'
:
self
.
asset
,
'system_user'
:
self
.
system_user
,
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
...
...
apps/users/templates/users/user_group_list.html
View file @
b9f82fd0
...
...
@@ -2,27 +2,27 @@
{% load i18n static %}
{% block table_search %}
<div
class=
""
style=
"float: right"
>
<div
class=
" btn-group"
>
<button
data-toggle=
"dropdown"
class=
"btn btn-default btn-sm dropdown-toggle"
>
CSV
<span
class=
"caret"
></span></button>
<ul
class=
"dropdown-menu"
>
<li>
<a
class=
" btn_export"
tabindex=
"0"
>
<span>
{% trans "Export" %}
</span>
</a>
</li>
<li>
<a
class=
" btn_import"
data-toggle=
"modal"
data-target=
"#import_modal"
tabindex=
"0"
>
<span>
{% trans "Import" %}
</span>
</a>
</li>
<li>
<a
class=
" btn_update"
data-toggle=
"modal"
data-target=
"#update_modal"
tabindex=
"0"
>
<span>
{% trans "Update" %}
</span>
</a>
</li>
</ul>
</div>
</div>
<div
class=
" btn-group"
>
<button
data-toggle=
"dropdown"
class=
"btn btn-default btn-sm dropdown-toggle"
>
CSV
<span
class=
"caret"
></span></button>
<ul
class=
"dropdown-menu"
>
<li>
<a
class=
" btn_export"
tabindex=
"0"
>
<span>
{% trans "Export" %}
</span>
</a>
</li>
<li>
<a
class=
" btn_import"
data-toggle=
"modal"
data-target=
"#import_modal"
tabindex=
"0"
>
<span>
{% trans "Import" %}
</span>
</a>
</li>
<li>
<a
class=
" btn_update"
data-toggle=
"modal"
data-target=
"#update_modal"
tabindex=
"0"
>
<span>
{% trans "Update" %}
</span>
</a>
</li>
</ul>
</div>
</div>
{% endblock %}
{% 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>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment