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
f450accb
Commit
f450accb
authored
Apr 18, 2018
by
BaiJiangjie
Browse files
Options
Browse Files
Download
Plain Diff
[Merge] with dev
parents
0bbfc743
48e87857
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
38 changed files
with
586 additions
and
259 deletions
+586
-259
__init__.py
apps/__init__.py
+1
-1
node.py
apps/assets/api/node.py
+14
-0
asset.py
apps/assets/models/asset.py
+2
-2
node.py
apps/assets/models/node.py
+3
-3
user.py
apps/assets/models/user.py
+1
-1
tasks.py
apps/assets/tasks.py
+3
-0
_asset_list_modal.html
apps/assets/templates/assets/_asset_list_modal.html
+98
-105
asset_detail.html
apps/assets/templates/assets/asset_detail.html
+2
-2
asset_list.html
apps/assets/templates/assets/asset_list.html
+0
-0
domain_create_update.html
apps/assets/templates/assets/domain_create_update.html
+16
-4
domain_gateway_list.html
apps/assets/templates/assets/domain_gateway_list.html
+0
-5
label_create_update.html
apps/assets/templates/assets/label_create_update.html
+21
-4
system_user_detail.html
apps/assets/templates/assets/system_user_detail.html
+2
-2
api_urls.py
apps/assets/urls/api_urls.py
+1
-0
asset.py
apps/assets/views/asset.py
+11
-8
fields.py
apps/common/fields.py
+28
-2
django.mo
apps/i18n/zh/LC_MESSAGES/django.mo
+0
-0
django.po
apps/i18n/zh/LC_MESSAGES/django.po
+0
-0
urls.py
apps/jumpserver/urls.py
+4
-3
api.py
apps/perms/api.py
+80
-1
forms.py
apps/perms/forms.py
+13
-0
models.py
apps/perms/models.py
+17
-1
asset_permission_asset.html
apps/perms/templates/perms/asset_permission_asset.html
+48
-61
asset_permission_create_update.html
...perms/templates/perms/asset_permission_create_update.html
+32
-14
asset_permission_detail.html
apps/perms/templates/perms/asset_permission_detail.html
+0
-0
asset_permission_user.html
apps/perms/templates/perms/asset_permission_user.html
+6
-6
api_urls.py
apps/perms/urls/api_urls.py
+42
-12
views_urls.py
apps/perms/urls/views_urls.py
+2
-2
views.py
apps/perms/views.py
+65
-4
jumpserver.js
apps/static/js/jumpserver.js
+39
-5
_footer.html
apps/templates/_footer.html
+1
-1
_modal.html
apps/templates/_modal.html
+7
-0
_nav.html
apps/templates/_nav.html
+1
-1
api.py
apps/terminal/api.py
+1
-1
user.py
apps/users/models/user.py
+10
-2
quickstart.rst
docs/quickstart.rst
+5
-2
step_by_step.rst
docs/step_by_step.rst
+3
-3
2018_04_11_migrate_permissions.sh
utils/2018_04_11_migrate_permissions.sh
+7
-1
No files found.
apps/__init__.py
View file @
f450accb
...
...
@@ -2,4 +2,4 @@
# -*- coding: utf-8 -*-
#
__version__
=
"1.
0
.0"
__version__
=
"1.
2
.0"
apps/assets/api/node.py
View file @
f450accb
...
...
@@ -32,6 +32,7 @@ __all__ = [
'NodeViewSet'
,
'NodeChildrenApi'
,
'NodeAssetsApi'
,
'NodeWithAssetsApi'
,
'NodeAddAssetsApi'
,
'NodeRemoveAssetsApi'
,
'NodeReplaceAssetsApi'
,
'NodeAddChildrenApi'
,
'RefreshNodeHardwareInfoApi'
,
'TestNodeConnectiveApi'
]
...
...
@@ -191,6 +192,19 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
instance
.
assets
.
remove
(
*
tuple
(
assets
))
class
NodeReplaceAssetsApi
(
generics
.
UpdateAPIView
):
serializer_class
=
serializers
.
NodeAssetsSerializer
queryset
=
Node
.
objects
.
all
()
permission_classes
=
(
IsSuperUser
,)
instance
=
None
def
perform_update
(
self
,
serializer
):
assets
=
serializer
.
validated_data
.
get
(
'assets'
)
instance
=
self
.
get_object
()
for
asset
in
assets
:
asset
.
nodes
.
set
([
instance
])
class
RefreshNodeHardwareInfoApi
(
APIView
):
permission_classes
=
(
IsSuperUser
,)
model
=
Node
...
...
apps/assets/models/asset.py
View file @
f450accb
...
...
@@ -49,6 +49,7 @@ class Asset(models.Model):
ip
=
models
.
GenericIPAddressField
(
max_length
=
32
,
verbose_name
=
_
(
'IP'
),
db_index
=
True
)
hostname
=
models
.
CharField
(
max_length
=
128
,
unique
=
True
,
verbose_name
=
_
(
'Hostname'
))
port
=
models
.
IntegerField
(
default
=
22
,
verbose_name
=
_
(
'Port'
))
platform
=
models
.
CharField
(
max_length
=
128
,
choices
=
PLATFORM_CHOICES
,
default
=
'Linux'
,
verbose_name
=
_
(
'Platform'
))
domain
=
models
.
ForeignKey
(
"assets.Domain"
,
null
=
True
,
blank
=
True
,
related_name
=
'assets'
,
verbose_name
=
_
(
"Domain"
),
on_delete
=
models
.
SET_NULL
)
nodes
=
models
.
ManyToManyField
(
'assets.Node'
,
default
=
default_node
,
related_name
=
'assets'
,
verbose_name
=
_
(
"Nodes"
))
is_active
=
models
.
BooleanField
(
default
=
True
,
verbose_name
=
_
(
'Is active'
))
...
...
@@ -72,7 +73,6 @@ class Asset(models.Model):
disk_total
=
models
.
CharField
(
max_length
=
1024
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Disk total'
))
disk_info
=
models
.
CharField
(
max_length
=
1024
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Disk info'
))
platform
=
models
.
CharField
(
max_length
=
128
,
choices
=
PLATFORM_CHOICES
,
default
=
'Linux'
,
verbose_name
=
_
(
'Platform'
))
os
=
models
.
CharField
(
max_length
=
128
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'OS'
))
os_version
=
models
.
CharField
(
max_length
=
16
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'OS version'
))
os_arch
=
models
.
CharField
(
max_length
=
16
,
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'OS arch'
))
...
...
@@ -84,7 +84,7 @@ class Asset(models.Model):
comment
=
models
.
TextField
(
max_length
=
128
,
default
=
''
,
blank
=
True
,
verbose_name
=
_
(
'Comment'
))
def
__str__
(
self
):
return
self
.
hostname
return
'{0.hostname}({0.ip})'
.
format
(
self
)
@property
def
is_valid
(
self
):
...
...
apps/assets/models/node.py
View file @
f450accb
...
...
@@ -19,7 +19,7 @@ class Node(models.Model):
is_asset
=
False
def
__str__
(
self
):
return
self
.
value
return
self
.
full_
value
@property
def
name
(
self
):
...
...
@@ -30,7 +30,7 @@ class Node(models.Model):
if
self
==
self
.
__class__
.
root
():
return
self
.
value
else
:
return
'{}
/{}'
.
format
(
self
.
value
,
self
.
parent
.
full_
value
)
return
'{}
/ {}'
.
format
(
self
.
parent
.
full_value
,
self
.
value
)
@property
def
level
(
self
):
...
...
@@ -72,7 +72,7 @@ class Node(models.Model):
assets
=
Asset
.
objects
.
all
()
else
:
nodes
=
self
.
get_family
()
assets
=
Asset
.
objects
.
filter
(
nodes__in
=
nodes
)
assets
=
Asset
.
objects
.
filter
(
nodes__in
=
nodes
)
.
distinct
()
return
assets
def
has_assets
(
self
):
...
...
apps/assets/models/user.py
View file @
f450accb
...
...
@@ -109,7 +109,7 @@ class SystemUser(AssetUser):
shell
=
models
.
CharField
(
max_length
=
64
,
default
=
'/bin/bash'
,
verbose_name
=
_
(
'Shell'
))
def
__str__
(
self
):
return
self
.
name
return
'{0.name}({0.username})'
.
format
(
self
)
def
to_json
(
self
):
return
{
...
...
apps/assets/tasks.py
View file @
f450accb
...
...
@@ -96,6 +96,9 @@ def update_assets_hardware_info_util(assets, task_name=None):
task_name
=
_
(
"更新资产硬件信息"
)
tasks
=
const
.
UPDATE_ASSETS_HARDWARE_TASKS
hostname_list
=
[
asset
.
hostname
for
asset
in
assets
if
asset
.
is_active
and
asset
.
is_unixlike
()]
if
not
hostname_list
:
logger
.
info
(
"Not hosts get, may be asset is not active or not unixlike platform"
)
return
{}
task
,
created
=
update_or_create_ansible_task
(
task_name
,
hosts
=
hostname_list
,
tasks
=
tasks
,
pattern
=
'all'
,
options
=
const
.
TASK_OPTIONS
,
run_as_admin
=
True
,
created_by
=
'System'
,
...
...
apps/assets/templates/assets/_asset_list_modal.html
View file @
f450accb
{% extends '_modal.html' %}
{% load i18n %}
{% load static %}
{% block modal_class %}modal-lg{% endblock %}
{% block modal_id %}asset_list_modal{% endblock %}
{
#{% block modal_title%}{% trans "Please select assets" %}{% endblock %}#
}
{
% block modal_title%}{% trans "Asset list" %}{% endblock %
}
{% block modal_body %}
{#
<div
class=
"btn-group"
style=
"float: right"
>
#}
{#
<button
data-toggle=
"dropdown"
class=
"btn btn-default btn-sm dropdown-toggle"
>
{% trans 'Label' %}
<span
class=
"caret"
></span></button>
#}
{#
<ul
class=
"dropdown-menu labels"
>
#}
{# {% for label in labels %}#}
{#
<li><a
style=
"font-weight: bolder"
>
{{ label.name }}:{{ label.value }}
</a></li>
#}
{# {% endfor %}#}
{#
</ul>
#}
{#
</div>
#}
<table
class=
"table table-striped table-bordered table-hover "
id=
"asset_modal_table"
width=
"100%"
>
<thead>
<tr>
<th
class=
"text-center"
><input
type=
"checkbox"
class=
"ipt_check_all"
></th>
<th
class=
"text-center"
>
{% trans 'Hostname' %}
</th>
<th
class=
"text-center"
>
{% trans 'IP' %}
</th>
<th
class=
"text-center"
>
{% trans 'Hardware' %}
</th>
<th
class=
"text-center"
>
{% trans 'Active' %}
</th>
<th
class=
"text-center"
>
{% trans 'Reachable' %}
</th>
<th
class=
"text-center"
>
{% trans 'Action' %}
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div
id=
"actions"
class=
"hide"
>
<div
class=
"input-group"
>
<select
class=
"form-control m-b"
style=
"width: auto"
id=
"slct_bulk_update"
>
<option
value=
"delete"
>
{% trans 'Delete selected' %}
</option>
<option
value=
"update"
>
{% trans 'Update selected' %}
</option>
<option
value=
"deactive"
>
{% trans 'Deactive selected' %}
</option>
<option
value=
"active"
>
{% trans 'Active selected' %}
</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>
<link
href=
"{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}"
rel=
"stylesheet"
>
<script
type=
"text/javascript"
src=
"{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"
></script>
<script
src=
"{% static 'js/jquery.form.min.js' %}"
></script>
<style>
.inmodal
.modal-header
{
padding
:
10px
10px
;
text-align
:
center
;
}
#assetTree2
.ztree
*
{
background-color
:
#f8fafb
;
}
#assetTree2
.ztree
{
background-color
:
#f8fafb
;
}
</style>
<div
class=
"wrapper wrapper-content"
>
<div
class=
"row"
>
<div
class=
"col-lg-3"
id=
"split-left"
style=
"padding-left: 3px"
>
<div
class=
"ibox float-e-margins"
>
<div
class=
"ibox-content mailbox-content"
style=
"padding-top: 0;padding-left: 1px"
>
<div
class=
"file-manager "
>
<div
id=
"assetTree2"
class=
"ztree"
>
</div>
<div
class=
"clearfix"
></div>
</div>
</div>
</div>
</div>
<div
class=
"col-lg-9 animated fadeInRight"
id=
"split-right"
>
<div
class=
"mail-box-header"
>
<table
class=
"table table-striped table-bordered table-hover "
id=
"asset_list_modal_table"
style=
"width: 100%"
>
<thead>
<tr>
<th
class=
"text-center"
><input
type=
"checkbox"
class=
"ipt_check_all"
></th>
<th
class=
"text-center"
>
{% trans 'Hostname' %}
</th>
<th
class=
"text-center"
>
{% trans 'IP' %}
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
var
modal_table
;
function
initModalTable
()
{
<script>
var
zTree2
,
asset_table2
=
0
;
function
initTable2
()
{
var
options
=
{
ele
:
$
(
'#asset_modal_table'
),
columnDefs
:
[
{
targets
:
1
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
{
%
url
'assets:asset-detail'
pk
=
DEFAULT_PK
as
the_url
%
}
var
detail_btn
=
'<a href="{{ the_url }}">'
+
cellData
+
'</a>'
;
$
(
td
).
html
(
detail_btn
.
replace
(
'{{ DEFAULT_PK }}'
,
rowData
.
id
));
}},
{
targets
:
3
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
$
(
td
).
html
(
rowData
.
hardware_info
)
}},
{
targets
:
4
,
createdCell
:
function
(
td
,
cellData
)
{
if
(
!
cellData
)
{
$
(
td
).
html
(
'<i class="fa fa-times text-danger"></i>'
)
}
else
{
$
(
td
).
html
(
'<i class="fa fa-check text-navy"></i>'
)
}
}},
{
targets
:
5
,
createdCell
:
function
(
td
,
cellData
)
{
if
(
cellData
===
'Unknown'
){
$
(
td
).
html
(
'<i class="fa fa-circle text-warning"></i>'
)
}
else
if
(
!
cellData
)
{
$
(
td
).
html
(
'<i class="fa fa-circle text-danger"></i>'
)
}
else
{
$
(
td
).
html
(
'<i class="fa fa-circle text-navy"></i>'
)
}
}},
{
targets
:
6
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
var
update_btn
=
'<a href="{% url "assets:asset-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'
.
replace
(
"{{ DEFAULT_PK }}"
,
cellData
);
var
del_btn
=
'<a class="btn btn-xs btn-danger m-l-xs btn_asset_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'
.
replace
(
'{{ DEFAULT_PK }}'
,
cellData
);
$
(
td
).
html
(
update_btn
+
del_btn
)
}}
],
ele
:
$
(
'#asset_list_modal_table'
),
ajax_url
:
'{% url "api-assets:asset-list" %}'
,
columns
:
[
{
data
:
"id"
},
{
data
:
"hostname"
},
{
data
:
"ip"
},
{
data
:
"cpu_cores"
},
{
data
:
"is_active"
,
orderable
:
false
},
{
data
:
"is_connective"
,
orderable
:
false
},
{
data
:
"id"
,
orderable
:
false
}
{
data
:
"id"
},
{
data
:
"hostname"
},
{
data
:
"ip"
}
],
op_html
:
$
(
'#actions'
).
html
()
pageLength
:
10
};
modal_table
=
jumpserver
.
initServerSideDataTable
(
options
);
return
modal_table
;
asset_table2
=
jumpserver
.
initServerSideDataTable
(
options
);
return
asset_table2
}
$
(
document
).
ready
(
function
(){
initModalTable
();
}).
on
(
'click'
,
'#btn_select_assets'
,
function
()
{
var
data_table
=
$
(
'#asset_modal_table'
).
DataTable
();
var
id_list
=
[];
data_table
.
rows
({
selected
:
true
}).
every
(
function
(){
id_list
.
push
(
this
.
data
().
id
);
});
var
current_node
;
var
nodes
=
zTree
.
getSelectedNodes
();
if
(
nodes
&&
nodes
.
length
===
1
)
{
current_node
=
nodes
[
0
]
}
else
{
return
}
function
onSelected2
(
event
,
treeNode
)
{
var
url
=
asset_table2
.
ajax
.
url
();
url
=
setUrlParam
(
url
,
"node_id"
,
treeNode
.
id
);
setCookie
(
'node_selected'
,
treeNode
.
id
);
asset_table2
.
ajax
.
url
(
url
);
asset_table2
.
ajax
.
reload
();
}
var
data
=
{
'assets'
:
id_list
};
var
success
=
function
()
{
modal_table
.
ajax
.
reload
()
function
initTree2
()
{
var
setting
=
{
view
:
{
dblClickExpand
:
false
,
showLine
:
true
},
data
:
{
simpleData
:
{
enable
:
true
}
},
callback
:
{
onSelected
:
onSelected2
}
};
APIUpdateAttr
({
'url'
:
'/api/assets/v1/nodes/'
+
current_node
.
id
+
'/assets/add/'
,
'method'
:
'PUT'
,
'body'
:
JSON
.
stringify
(
data
),
'success'
:
success
})
var
zNodes
=
[];
$
.
get
(
"{% url 'api-assets:node-list' %}"
,
function
(
data
,
status
){
$
.
each
(
data
,
function
(
index
,
value
)
{
value
[
"pId"
]
=
value
[
"parent"
];
value
[
"open"
]
=
true
;
value
[
"name"
]
=
value
[
"value"
]
+
' ('
+
value
[
'assets_amount'
]
+
')'
;
value
[
'value'
]
=
value
[
'value'
];
});
zNodes
=
data
;
$
.
fn
.
zTree
.
init
(
$
(
"#assetTree2"
),
setting
,
zNodes
);
zTree2
=
$
.
fn
.
zTree
.
getZTreeObj
(
"assetTree2"
);
});
}
$
(
document
).
ready
(
function
(){
initTable2
();
initTree2
();
})
</script>
{% endblock %}
{% block modal_button %}
{{ block.super }}
{% endblock %}
{% block modal_confirm_id %}btn_select_assets{% endblock %}
{% block modal_confirm_id %}btn_asset_modal_confirm{% endblock %}
apps/assets/templates/assets/asset_detail.html
View file @
f450accb
...
...
@@ -305,9 +305,9 @@ $(document).ready(function () {
success_message
:
success
});
if
(
status
===
"False"
)
{
$
(
".ibox-content > table > tbody > tr:nth-child(13) > td:last >b"
).
html
(
'True'
);
$
(
".ibox-content > table > tbody > tr:nth-child(13) > td:last >b"
).
html
(
'True'
);
}
else
{
$
(
".ibox-content > table > tbody > tr:nth-child(13) > td:last >b"
).
html
(
'False'
);
$
(
".ibox-content > table > tbody > tr:nth-child(13) > td:last >b"
).
html
(
'False'
);
}
}).
on
(
'click'
,
'#btn-update-nodes'
,
function
()
{
if
(
Object
.
keys
(
jumpserver
.
nodes_selected
).
length
===
0
)
{
...
...
apps/assets/templates/assets/asset_list.html
View file @
f450accb
This diff is collapsed.
Click to expand it.
apps/assets/templates/assets/domain_create_update.html
View file @
f450accb
...
...
@@ -18,14 +18,25 @@
</div>
</div>
</form>
{% include 'assets/_asset_list_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
<script
type=
"text/javascript"
>
$
(
document
).
ready
(
function
()
{
$
(
'.select2'
).
select2
({
closeOnSelect
:
false
});
$
(
document
).
ready
(
function
()
{
console
.
log
(
$
.
fn
.
select2
.
defaults
);
$
(
'.select2'
).
select2
().
off
(
"select2:open"
);
}).
on
(
'click'
,
'.select2-selection__rendered'
,
function
(
e
)
{
e
.
preventDefault
();
$
(
"#asset_list_modal"
).
modal
();
})
.
on
(
'click'
,
'#btn_asset_modal_confirm'
,
function
()
{
var
assets
=
asset_table2
.
selected
;
$
.
each
(
assets
,
function
(
id
,
data
)
{
$
(
'.select2'
).
val
(
assets
).
trigger
(
'change'
);
});
$
(
"#asset_list_modal"
).
modal
(
'hide'
);
})
</script>
{% endblock %}
\ No newline at end of file
apps/assets/templates/assets/domain_gateway_list.html
View file @
f450accb
...
...
@@ -81,11 +81,6 @@ function initTable() {
var
options
=
{
ele
:
$
(
'#domain_list_table'
),
columnDefs
:
[
{
targets
:
1
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
var
detail_btn
=
'<a href="{% url "assets:domain-detail" pk=DEFAULT_PK %}">'
+
cellData
+
'</a>'
;
$
(
td
).
html
(
detail_btn
.
replace
(
'{{ DEFAULT_PK }}'
,
rowData
.
id
));
}},
{
targets
:
7
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
var
update_btn
=
'<a href="{% url "assets:domain-gateway-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'
.
replace
(
'{{ DEFAULT_PK }}'
,
cellData
);
var
del_btn
=
'<a class="btn btn-xs btn-danger m-l-xs btn-delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'
.
replace
(
'{{ DEFAULT_PK }}'
,
cellData
);
...
...
apps/assets/templates/assets/label_create_update.html
View file @
f450accb
...
...
@@ -3,6 +3,8 @@
{% load bootstrap3 %}
{% load i18n %}
{% block form %}
<form
id=
"groupForm"
method=
"post"
class=
"form-horizontal"
>
{% csrf_token %}
...
...
@@ -18,14 +20,28 @@
</div>
</div>
</form>
{% include 'assets/_asset_list_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
<script
type=
"text/javascript"
>
$
(
document
).
ready
(
function
()
{
$
(
'.select2'
).
select2
({
closeOnSelect
:
false
});
$
(
document
).
ready
(
function
()
{
$
(
'.select2'
).
select2
({
closeOnSelect
:
false
})
}).
on
(
'click'
,
'.select2-selection__rendered'
,
function
(
e
)
{
e
.
preventDefault
();
$
(
"#asset_list_modal"
).
modal
();
})
.
on
(
'click'
,
'#btn_asset_modal_confirm'
,
function
()
{
var
assets
=
asset_table2
.
selected
;
$
(
'.select2 option:selected'
).
each
(
function
(
i
,
data
)
{
assets
.
push
(
$
(
data
).
attr
(
'value'
))
});
$
.
each
(
assets
,
function
(
id
,
data
)
{
$
(
'.select2'
).
val
(
assets
).
trigger
(
'change'
);
});
$
(
"#asset_list_modal"
).
modal
(
'hide'
);
})
</script>
{% endblock %}
\ No newline at end of file
apps/assets/templates/assets/system_user_detail.html
View file @
f450accb
...
...
@@ -173,7 +173,7 @@
<td
colspan=
"2"
class=
"no-borders"
>
<select
data-placeholder=
"{% trans 'Add to node' %}"
id=
"node_selected"
class=
"select2"
style=
"width: 100%"
multiple=
""
tabindex=
"4"
>
{% for node in nodes_remain %}
<option
value=
"{{ node.id }}"
id=
"opt_{{ node.id }}"
>
{{ node
.name
}}
</option>
<option
value=
"{{ node.id }}"
id=
"opt_{{ node.id }}"
>
{{ node }}
</option>
{% endfor %}
</select>
</td>
...
...
@@ -187,7 +187,7 @@
{% for node in system_user.nodes.all %}
<tr>
<td
><b
class=
"bdg_node"
data-gid=
{{
node
.
id
}}
>
{{ node
.name
}}
</b></td>
<td
><b
class=
"bdg_node"
data-gid=
{{
node
.
id
}}
>
{{ node }}
</b></td>
<td>
<button
class=
"btn btn-danger pull-right btn-xs btn-remove-from-node"
type=
"button"
><i
class=
"fa fa-minus"
></i></button>
</td>
...
...
apps/assets/urls/api_urls.py
View file @
f450accb
...
...
@@ -40,6 +40,7 @@ urlpatterns = [
url
(
r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/children/add/$'
,
api
.
NodeAddChildrenApi
.
as_view
(),
name
=
'node-add-children'
),
url
(
r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$'
,
api
.
NodeAssetsApi
.
as_view
(),
name
=
'node-assets'
),
url
(
r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/add/$'
,
api
.
NodeAddAssetsApi
.
as_view
(),
name
=
'node-add-assets'
),
url
(
r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/replace/$'
,
api
.
NodeReplaceAssetsApi
.
as_view
(),
name
=
'node-replace-assets'
),
url
(
r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/remove/$'
,
api
.
NodeRemoveAssetsApi
.
as_view
(),
name
=
'node-remove-assets'
),
url
(
r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/refresh-hardware-info/$'
,
api
.
RefreshNodeHardwareInfoApi
.
as_view
(),
name
=
'node-refresh-hardware-info'
),
url
(
r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/test-connective/$'
,
api
.
TestNodeConnectiveApi
.
as_view
(),
name
=
'node-test-connective'
),
...
...
apps/assets/views/asset.py
View file @
f450accb
...
...
@@ -49,6 +49,7 @@ class AssetListView(AdminUserRequiredMixin, TemplateView):
'app'
:
_
(
'Assets'
),
'action'
:
_
(
'Asset list'
),
'labels'
:
Label
.
objects
.
all
()
.
order_by
(
'name'
),
'nodes'
:
Node
.
objects
.
all
()
.
order_by
(
'-key'
),
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
...
...
@@ -284,24 +285,26 @@ class BulkImportAssetView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
if
set
(
row
)
==
{
''
}:
continue
asset_dict
=
dict
(
zip
(
attr
,
row
))
id_
=
asset_dict
.
pop
(
'id'
,
0
)
for
k
,
v
in
asset_dict
.
items
():
asset_dict
_raw
=
dict
(
zip
(
attr
,
row
))
asset_dict
=
dict
(
)
for
k
,
v
in
asset_dict
_raw
.
items
():
v
=
v
.
strip
()
if
k
==
'is_active'
:
v
=
True
if
v
in
[
'TRUE'
,
1
,
'true'
]
else
Fals
e
v
=
False
if
v
in
[
'False'
,
0
,
'false'
]
else
Tru
e
elif
k
==
'admin_user'
:
v
=
get_object_or_none
(
AdminUser
,
name
=
v
)
elif
k
in
[
'port'
,
'cpu_count'
,
'cpu_cores'
]:
try
:
v
=
int
(
v
)
except
ValueError
:
v
=
0
v
=
''
elif
k
==
'domain'
:
v
=
get_object_or_none
(
Domain
,
name
=
v
)
asset_dict
[
k
]
=
v
asset
=
get_object_or_none
(
Asset
,
id
=
id_
)
if
is_uuid
(
id_
)
else
None
if
v
!=
''
:
asset_dict
[
k
]
=
v
asset
=
get_object_or_none
(
Asset
,
id
=
asset_dict
.
pop
(
'id'
,
0
))
if
not
asset
:
try
:
if
len
(
Asset
.
objects
.
filter
(
hostname
=
asset_dict
.
get
(
'hostname'
))):
...
...
@@ -316,7 +319,7 @@ class BulkImportAssetView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
failed
.
append
(
'
%
s:
%
s'
%
(
asset_dict
[
'hostname'
],
str
(
e
)))
else
:
for
k
,
v
in
asset_dict
.
items
():
if
v
:
if
v
!=
''
:
setattr
(
asset
,
k
,
v
)
try
:
asset
.
save
()
...
...
apps/common/fields.py
View file @
f450accb
...
...
@@ -2,11 +2,15 @@
#
import
json
from
django.db
import
models
from
django
import
forms
from
django.utils
import
six
from
django.core.exceptions
import
ValidationError
from
django.utils.translation
import
ugettext
as
_
from
rest_framework
import
serializers
from
.utils
import
get_signer
signer
=
get_signer
()
class
DictField
(
forms
.
Field
):
...
...
@@ -46,4 +50,27 @@ class StringIDField(serializers.Field):
class
StringManyToManyField
(
serializers
.
RelatedField
):
def
to_representation
(
self
,
value
):
return
value
.
__str__
()
\ No newline at end of file
return
value
.
__str__
()
class
EncryptMixin
:
def
from_db_value
(
self
,
value
,
expression
,
connection
,
context
):
if
value
is
not
None
:
return
signer
.
unsign
(
value
)
return
super
()
.
from_db_value
(
self
,
value
,
expression
,
connection
,
context
)
def
get_prep_value
(
self
,
value
):
if
value
is
None
:
return
value
return
signer
.
sign
(
value
)
.
decode
(
'utf-8'
)
class
EncryptTextField
(
EncryptMixin
,
models
.
TextField
):
description
=
_
(
"Encrypt field using Secret Key"
)
class
EncryptCharField
(
EncryptMixin
,
models
.
CharField
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
kwargs
[
'max_length'
]
=
2048
super
()
.
__init__
(
*
args
,
**
kwargs
)
apps/i18n/zh/LC_MESSAGES/django.mo
View file @
f450accb
No preview for this file type
apps/i18n/zh/LC_MESSAGES/django.po
View file @
f450accb
This diff is collapsed.
Click to expand it.
apps/jumpserver/urls.py
View file @
f450accb
...
...
@@ -36,9 +36,10 @@ urlpatterns = [
url
(
r'^captcha/'
,
include
(
'captcha.urls'
)),
]
urlpatterns
+=
static
(
settings
.
MEDIA_URL
,
document_root
=
settings
.
MEDIA_ROOT
)
\
+
static
(
settings
.
STATIC_URL
,
document_root
=
settings
.
STATIC_ROOT
)
if
settings
.
DEBUG
:
urlpatterns
+=
[
url
(
r'^docs/'
,
schema_view
,
name
=
"docs"
),
]
+
static
(
settings
.
STATIC_URL
,
document_root
=
settings
.
STATIC_ROOT
)
\
+
static
(
settings
.
MEDIA_URL
,
document_root
=
settings
.
MEDIA_ROOT
)
]
apps/perms/api.py
View file @
f450accb
...
...
@@ -3,7 +3,7 @@
from
django.shortcuts
import
get_object_or_404
from
rest_framework.views
import
APIView
,
Response
from
rest_framework.generics
import
ListAPIView
,
get_object_or_404
from
rest_framework.generics
import
ListAPIView
,
get_object_or_404
,
RetrieveUpdateAPIView
from
rest_framework
import
viewsets
from
common.utils
import
set_or_append_attr_bulk
...
...
@@ -98,6 +98,11 @@ class UserGrantedNodesApi(ListAPIView):
nodes
=
AssetPermissionUtil
.
get_user_nodes_with_assets
(
user
)
return
nodes
.
keys
()
def
get_permissions
(
self
):
if
self
.
kwargs
.
get
(
'pk'
)
is
None
:
self
.
permission_classes
=
(
IsValidUser
,)
return
super
()
.
get_permissions
()
class
UserGrantedNodesWithAssetsApi
(
ListAPIView
):
permission_classes
=
(
IsSuperUserOrAppUser
,)
...
...
@@ -246,3 +251,77 @@ class ValidateUserAssetPermissionView(APIView):
return
Response
({
'msg'
:
True
},
status
=
200
)
else
:
return
Response
({
'msg'
:
False
},
status
=
403
)
class
AssetPermissionRemoveUserApi
(
RetrieveUpdateAPIView
):
"""
将用户从授权中移除,Detail页面会调用
"""
permission_classes
=
(
IsSuperUser
,)
serializer_class
=
serializers
.
AssetPermissionUpdateUserSerializer
queryset
=
AssetPermission
.
objects
.
all
()
def
update
(
self
,
request
,
*
args
,
**
kwargs
):
perm
=
self
.
get_object
()
serializer
=
self
.
serializer_class
(
data
=
request
.
data
)
if
serializer
.
is_valid
():
users
=
serializer
.
validated_data
.
get
(
'users'
)
if
users
:
perm
.
users
.
remove
(
*
tuple
(
users
))
return
Response
({
"msg"
:
"ok"
})
else
:
return
Response
({
"error"
:
serializer
.
errors
})
class
AssetPermissionAddUserApi
(
RetrieveUpdateAPIView
):
permission_classes
=
(
IsSuperUser
,)
serializer_class
=
serializers
.
AssetPermissionUpdateUserSerializer
queryset
=
AssetPermission
.
objects
.
all
()
def
update
(
self
,
request
,
*
args
,
**
kwargs
):
perm
=
self
.
get_object
()
serializer
=
self
.
serializer_class
(
data
=
request
.
data
)
if
serializer
.
is_valid
():
users
=
serializer
.
validated_data
.
get
(
'users'
)
if
users
:
perm
.
users
.
add
(
*
tuple
(
users
))
return
Response
({
"msg"
:
"ok"
})
else
:
return
Response
({
"error"
:
serializer
.
errors
})
class
AssetPermissionRemoveAssetApi
(
RetrieveUpdateAPIView
):
"""
将用户从授权中移除,Detail页面会调用
"""
permission_classes
=
(
IsSuperUser
,)
serializer_class
=
serializers
.
AssetPermissionUpdateAssetSerializer
queryset
=
AssetPermission
.
objects
.
all
()
def
update
(
self
,
request
,
*
args
,
**
kwargs
):
perm
=
self
.
get_object
()
serializer
=
self
.
serializer_class
(
data
=
request
.
data
)
if
serializer
.
is_valid
():
assets
=
serializer
.
validated_data
.
get
(
'assets'
)
if
assets
:
perm
.
assets
.
remove
(
*
tuple
(
assets
))
return
Response
({
"msg"
:
"ok"
})
else
:
return
Response
({
"error"
:
serializer
.
errors
})
class
AssetPermissionAddAssetApi
(
RetrieveUpdateAPIView
):
permission_classes
=
(
IsSuperUser
,)
serializer_class
=
serializers
.
AssetPermissionUpdateAssetSerializer
queryset
=
AssetPermission
.
objects
.
all
()
def
update
(
self
,
request
,
*
args
,
**
kwargs
):
perm
=
self
.
get_object
()
serializer
=
self
.
serializer_class
(
data
=
request
.
data
)
if
serializer
.
is_valid
():
assets
=
serializer
.
validated_data
.
get
(
'assets'
)
if
assets
:
perm
.
assets
.
add
(
*
tuple
(
assets
))
return
Response
({
"msg"
:
"ok"
})
else
:
return
Response
({
"error"
:
serializer
.
errors
})
apps/perms/forms.py
View file @
f450accb
...
...
@@ -4,10 +4,23 @@ from __future__ import absolute_import, unicode_literals
from
django
import
forms
from
django.utils.translation
import
ugettext_lazy
as
_
from
.hands
import
User
from
.models
import
AssetPermission
class
AssetPermissionForm
(
forms
.
ModelForm
):
users
=
forms
.
ModelMultipleChoiceField
(
queryset
=
User
.
objects
.
exclude
(
role
=
User
.
ROLE_APP
),
label
=
_
(
"User"
),
widget
=
forms
.
SelectMultiple
(
attrs
=
{
'class'
:
'select2'
,
'data-placeholder'
:
_
(
'Select users'
)
}
),
required
=
False
,
)
class
Meta
:
model
=
AssetPermission
exclude
=
(
...
...
apps/perms/models.py
View file @
f450accb
...
...
@@ -4,7 +4,7 @@ from django.db import models
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils
import
timezone
from
common.utils
import
date_expired_default
from
common.utils
import
date_expired_default
,
set_or_append_attr_bulk
class
ValidManager
(
models
.
Manager
):
...
...
@@ -45,6 +45,22 @@ class AssetPermission(models.Model):
return
True
return
False
def
get_all_users
(
self
):
users
=
set
(
self
.
users
.
all
())
for
group
in
self
.
user_groups
.
all
():
_users
=
group
.
users
.
all
()
set_or_append_attr_bulk
(
_users
,
'inherit'
,
group
.
name
)
users
.
update
(
set
(
_users
))
return
users
def
get_all_assets
(
self
):
assets
=
set
(
self
.
assets
.
all
())
for
node
in
self
.
nodes
.
all
():
_assets
=
node
.
get_all_assets
()
set_or_append_attr_bulk
(
_assets
,
'inherit'
,
node
.
value
)
assets
.
update
(
set
(
_assets
))
return
assets
class
NodePermission
(
models
.
Model
):
id
=
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
primary_key
=
True
)
...
...
apps/perms/templates/perms/asset_permission_asset.html
View file @
f450accb
...
...
@@ -57,12 +57,12 @@
</tr>
</thead>
<tbody>
{% for asset in
page_obj
%}
{% for asset in
object_list
%}
<tr>
<td>
{{ asset.hostname }}
</td>
<td>
{{ asset.ip }}
</td>
<td>
<button
title=
"{{ asset.inherit
_from_asset_groups }}"
data-gid=
"{{ asset.id }}"
class=
"btn btn-danger btn-xs btn-remove-asset {% if asset.is_inherit_from_asset_groups
%} disabled {% endif %}"
type=
"button"
style=
"float: right;"
><i
class=
"fa fa-minus"
></i></button>
<button
title=
"{{ asset.inherit
}}"
data-gid=
"{{ asset.id }}"
class=
"btn btn-danger btn-xs btn-remove-asset {% if asset.inherit
%} disabled {% endif %}"
type=
"button"
style=
"float: right;"
><i
class=
"fa fa-minus"
></i></button>
</td>
</tr>
{% endfor %}
...
...
@@ -85,9 +85,9 @@
<form>
<tr
class=
"no-borders-tr"
>
<td
colspan=
"2"
>
<select
data-placeholder=
"{% trans 'Select assets' %}"
class=
"select2
asset
"
style=
"width: 100%"
multiple=
""
tabindex=
"4"
>
<select
data-placeholder=
"{% trans 'Select assets' %}"
class=
"select2
"
id=
"asset_select2
"
style=
"width: 100%"
multiple=
""
tabindex=
"4"
>
{% for asset in assets_remain %}
<option
value=
"{{ asset.id }}"
>
{{ asset
.hostname
}}
</option>
<option
value=
"{{ asset.id }}"
>
{{ asset }}
</option>
{% endfor %}
</select>
</td>
...
...
@@ -105,7 +105,7 @@
<div
class=
"panel panel-info"
>
<div
class=
"panel-heading"
>
<i
class=
"fa fa-info-circle"
></i>
{% trans 'Add
asset group
to this permission' %}
<i
class=
"fa fa-info-circle"
></i>
{% trans 'Add
node
to this permission' %}
</div>
<div
class=
"panel-body"
>
<table
class=
"table group_edit"
>
...
...
@@ -113,25 +113,25 @@
<form>
<tr>
<td
colspan=
"2"
class=
"no-borders"
>
<select
data-placeholder=
"{% trans 'Select
asset groups' %}"
class=
"select2 group
"
style=
"width: 100%"
multiple=
""
tabindex=
"4"
>
{% for
asset_group in asset_group
s_remain %}
<option
value=
"{{
asset_group.id }}"
id=
"opt_{{ asset_group.id }}"
>
{{ asset_group.nam
e }}
</option>
<select
data-placeholder=
"{% trans 'Select
nodes' %}"
class=
"select2"
id=
"node_select2
"
style=
"width: 100%"
multiple=
""
tabindex=
"4"
>
{% for
node in node
s_remain %}
<option
value=
"{{
node.id }}"
id=
"opt_{{ node.id }}"
>
{{ nod
e }}
</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td
colspan=
"2"
class=
"no-borders"
>
<button
type=
"button"
class=
"btn btn-info btn-sm"
id=
"btn-add-
group
"
>
{% trans 'Join' %}
</button>
<button
type=
"button"
class=
"btn btn-info btn-sm"
id=
"btn-add-
node
"
>
{% trans 'Join' %}
</button>
</td>
</tr>
</form>
{% for
asset_group in asset_groups
%}
{% for
node in asset_permission.nodes.all
%}
<tr>
<td
><b
class=
"bdg_
user_group"
data-gid=
{{
asset_group
.
id
}}
>
{{ asset_group.nam
e }}
</b></td>
<td
><b
class=
"bdg_
group"
data-gid=
{{
node
.
id
}}
>
{{ nod
e }}
</b></td>
<td>
<button
class=
"btn btn-danger btn-xs btn-remove-
group"
type=
"button
"
style=
"float: right;"
><i
class=
"fa fa-minus"
></i></button>
<button
class=
"btn btn-danger btn-xs btn-remove-
node"
type=
"button"
data-gid=
"{{ node.id }}
"
style=
"float: right;"
><i
class=
"fa fa-minus"
></i></button>
</td>
</tr>
{% endfor %}
...
...
@@ -179,49 +179,30 @@ function removeAssets(assets) {
});
}
function
update
Group
(
group
s
)
{
function
update
Nodes
(
nodes
,
succes
s
)
{
var
the_url
=
"{% url 'api-perms:asset-permission-detail' pk=asset_permission.id %}"
;
var
body
=
{
asset_groups
:
group
s
nodes
:
node
s
};
APIUpdateAttr
({
url
:
the_url
,
body
:
JSON
.
stringify
(
body
)
body
:
JSON
.
stringify
(
body
),
success
:
success
});
}
jumpserver
.
assets_selected
=
{};
jumpserver
.
nodes_selected
=
{};
$
(
document
).
ready
(
function
()
{
$
(
'.select2.asset'
).
select2
()
.
on
(
'select2:select'
,
function
(
evt
)
{
var
data
=
evt
.
params
.
data
;
jumpserver
.
assets_selected
[
data
.
id
]
=
data
.
text
;
})
.
on
(
'select2:unselect'
,
function
(
evt
)
{
var
data
=
evt
.
params
.
data
;
delete
jumpserver
.
assets_selected
[
data
.
id
]
});
$
(
'.select2.group'
).
select2
()
.
on
(
'select2:select'
,
function
(
evt
)
{
var
data
=
evt
.
params
.
data
;
jumpserver
.
nodes_selected
[
data
.
id
]
=
data
.
text
;
})
.
on
(
'select2:unselect'
,
function
(
evt
)
{
var
data
=
evt
.
params
.
data
;
delete
jumpserver
.
nodes_selected
[
data
.
id
]
})
$
(
'.select2'
).
select2
();
})
.
on
(
'click'
,
'.btn-add-assets'
,
function
()
{
if
(
Object
.
keys
(
jumpserver
.
assets_selected
).
length
===
0
)
{
var
assets_selected
=
$
(
"#asset_select2 option:selected"
).
map
(
function
()
{
return
$
(
this
).
attr
(
'value'
);
}).
get
();
if
(
assets_selected
.
length
===
0
)
{
return
false
;
}
var
assets
=
[];
$
.
map
(
jumpserver
.
assets_selected
,
function
(
value
,
index
)
{
assets
.
push
(
index
);
});
addAssets
(
assets
);
addAssets
(
assets_selected
);
})
.
on
(
'click'
,
'.btn-remove-asset'
,
function
()
{
var
asset_id
=
$
(
this
).
data
(
"gid"
);
...
...
@@ -231,38 +212,44 @@ $(document).ready(function () {
var
assets
=
[
asset_id
];
removeAssets
(
assets
)
})
.
on
(
'click'
,
'#btn-add-group'
,
function
()
{
if
(
Object
.
keys
(
jumpserver
.
nodes_selected
).
length
===
0
)
{
.
on
(
'click'
,
'#btn-add-node'
,
function
()
{
var
nodes_selected
=
{};
$
(
"#node_select2 option:selected"
).
each
(
function
(
i
,
data
)
{
nodes_selected
[
$
(
data
).
attr
(
'value'
)]
=
$
(
data
).
text
();
});
if
(
Object
.
keys
(
nodes_selected
).
length
===
0
)
{
return
false
;
}
var
groups
=
$
(
'.bdg_group'
).
map
(
function
()
{
var
nodes_origin
=
$
(
'.bdg_group'
).
map
(
function
()
{
return
$
(
this
).
data
(
'gid'
);
}).
get
();
$
.
map
(
jumpserver
.
nodes_selected
,
function
(
group_name
,
index
)
{
groups
.
push
(
index
);
$
(
'#opt_'
+
index
).
remove
();
$
(
'.group_edit tbody'
).
append
(
'<tr>'
+
'<td><b class="bdg_group" data-gid="'
+
index
+
'">'
+
group_name
+
'</b></td>'
+
'<td><button class="btn btn-danger btn-xs pull-right btn-leave-group" type="button"><i class="fa fa-minus"></i></button></td>'
+
'</tr>'
)
});
updateGroup
(
groups
);
var
nodes
=
nodes_origin
.
concat
(
Object
.
keys
(
nodes_selected
));
var
success
=
function
()
{
$
.
map
(
nodes_selected
,
function
(
name
,
id
)
{
$
(
'#opt_'
+
id
).
remove
();
$
(
'.group_edit tbody'
).
append
(
'<tr>'
+
'<td><b class="bdg_group" data-gid="'
+
id
+
'">'
+
name
+
'</b></td>'
+
'<td><button class="btn btn-danger btn-xs pull-right btn-leave-group" type="button"><i class="fa fa-minus"></i></button></td>'
+
'</tr>'
)
});
};
updateNodes
(
nodes
,
success
);
})
.
on
(
'click'
,
'.btn-remove-
group
'
,
function
()
{
.
on
(
'click'
,
'.btn-remove-
node
'
,
function
()
{
var
$this
=
$
(
this
);
var
$tr
=
$this
.
closest
(
'tr'
);
var
group
s
=
$
(
'.bdg_group'
).
map
(
function
()
{
var
node
s
=
$
(
'.bdg_group'
).
map
(
function
()
{
if
(
$
(
this
).
data
(
'gid'
)
!==
$this
.
data
(
'gid'
)){
return
$
(
this
).
data
(
'gid'
);
}
}).
get
();
updateGroup
(
groups
);
$tr
.
remove
()
var
success
=
function
()
{
$tr
.
remove
()
};
updateNodes
(
nodes
,
success
);
})
</script>
{% endblock %}
apps/perms/templates/perms/asset_permission_create_update.html
View file @
f450accb
...
...
@@ -76,20 +76,37 @@
</div>
</div>
</div>
{% include 'assets/_asset_list_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
<script
src=
"{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"
></script>
<script>
$
(
document
).
ready
(
function
()
{
$
(
'.select2'
).
select2
();
$
(
'#datepicker'
).
datepicker
({
format
:
"yyyy-mm-dd"
,
todayBtn
:
"linked"
,
keyboardNavigation
:
false
,
forceParse
:
false
,
calendarWeeks
:
true
,
autoclose
:
true
});
})
</script>
<script
src=
"{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"
></script>
<script>
$
(
document
).
ready
(
function
()
{
$
(
'.select2'
).
select2
({
closeOnSelect
:
false
});
$
(
'#datepicker'
).
datepicker
({
format
:
"yyyy-mm-dd"
,
todayBtn
:
"linked"
,
keyboardNavigation
:
false
,
forceParse
:
false
,
calendarWeeks
:
true
,
autoclose
:
true
});
$
(
"#id_assets"
).
parent
().
find
(
".select2-selection"
).
on
(
'click'
,
function
(
e
)
{
e
.
preventDefault
();
$
(
"#asset_list_modal"
).
modal
();
})
})
.
on
(
'click'
,
'#btn_asset_modal_confirm'
,
function
()
{
var
assets
=
asset_table2
.
selected
;
$
(
'.select2 option:selected'
).
each
(
function
(
i
,
data
)
{
assets
.
push
(
$
(
data
).
attr
(
'value'
))
});
$
.
each
(
assets
,
function
(
id
,
data
)
{
$
(
'.select2'
).
val
(
assets
).
trigger
(
'change'
);
});
$
(
"#asset_list_modal"
).
modal
(
'hide'
);
})
</script>
{% endblock %}
\ No newline at end of file
apps/perms/templates/perms/asset_permission_detail.html
View file @
f450accb
This diff is collapsed.
Click to expand it.
apps/perms/templates/perms/asset_permission_user.html
View file @
f450accb
...
...
@@ -57,12 +57,12 @@
</tr>
</thead>
<tbody>
{% for user in
page_obj
%}
{% for user in
object_list
%}
<tr>
<td>
{{ user.name }}
</td>
<td>
{{ user.username }}
</td>
<td>
<button
class=
"btn btn-danger btn-xs btn-remove-user {% if user.i
s_inherit_from_user_groups
%} disabled {% endif %}"
data-gid=
"{{ user.id }}"
type=
"button"
style=
"float: right;"
><i
class=
"fa fa-minus"
></i></button>
<button
class=
"btn btn-danger btn-xs btn-remove-user {% if user.i
nherit
%} disabled {% endif %}"
data-gid=
"{{ user.id }}"
type=
"button"
style=
"float: right;"
><i
class=
"fa fa-minus"
></i></button>
</td>
</tr>
{% endfor %}
...
...
@@ -87,7 +87,7 @@
<td
colspan=
"2"
>
<select
data-placeholder=
"{% trans 'Select user' %}"
class=
"select2 user"
style=
"width: 100%"
multiple=
""
tabindex=
"4"
>
{% for user in users_remain %}
<option
value=
"{{ user.id }}"
>
{{ user
.name
}}
</option>
<option
value=
"{{ user.id }}"
>
{{ user }}
</option>
{% endfor %}
</select>
</td>
...
...
@@ -115,7 +115,7 @@
<td
colspan=
"2"
class=
"no-borders"
>
<select
data-placeholder=
"{% trans 'Select user groups' %}"
class=
"select2 user-group"
style=
"width: 100%"
multiple=
""
tabindex=
"4"
>
{% for user_group in user_groups_remain %}
<option
value=
"{{ user_group.id }}"
id=
"opt_{{ user_group.id }}"
>
{{ user_group
.name
}}
</option>
<option
value=
"{{ user_group.id }}"
id=
"opt_{{ user_group.id }}"
>
{{ user_group }}
</option>
{% endfor %}
</select>
</td>
...
...
@@ -127,9 +127,9 @@
</tr>
</form>
{% for user_group in
user_groups
%}
{% for user_group in
asset_permission.user_groups.all
%}
<tr>
<td
><b
class=
"bdg_group"
data-gid=
{{
user_group
.
id
}}
>
{{ user_group
.name
}}
</b></td>
<td
><b
class=
"bdg_group"
data-gid=
{{
user_group
.
id
}}
>
{{ user_group }}
</b></td>
<td>
<button
class=
"btn btn-danger btn-xs btn-remove-group"
type=
"button"
data-gid=
"{{ user_group.id }}"
style=
"float: right;"
><i
class=
"fa fa-minus"
></i></button>
</td>
...
...
apps/perms/urls/api_urls.py
View file @
f450accb
...
...
@@ -11,20 +11,50 @@ router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'asset-permi
urlpatterns
=
[
# 查询某个用户授权的资产和资产组
url
(
r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$'
,
api
.
UserGrantedAssetsApi
.
as_view
(),
name
=
'user-assets'
),
url
(
r'^v1/user/assets/$'
,
api
.
UserGrantedAssetsApi
.
as_view
(),
name
=
'my-assets'
),
url
(
r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$'
,
api
.
UserGrantedNodesApi
.
as_view
(),
name
=
'user-nodes'
),
url
(
r'^v1/user/nodes/$'
,
api
.
UserGrantedNodesApi
.
as_view
(),
name
=
'my-nodes'
),
url
(
r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/(?P<node_id>[0-9a-zA-Z\-]{36})/assets/$'
,
api
.
UserGrantedNodeAssetsApi
.
as_view
(),
name
=
'user-node-assets'
),
url
(
r'^v1/user/nodes/(?P<node_id>[0-9a-zA-Z\-]{36})/assets/$'
,
api
.
UserGrantedNodeAssetsApi
.
as_view
(),
name
=
'my-node-assets'
),
url
(
r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes-assets/$'
,
api
.
UserGrantedNodesWithAssetsApi
.
as_view
(),
name
=
'user-nodes-assets'
),
url
(
r'^v1/user/nodes-assets/$'
,
api
.
UserGrantedNodesWithAssetsApi
.
as_view
(),
name
=
'my-nodes-assets'
),
url
(
r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$'
,
api
.
UserGrantedAssetsApi
.
as_view
(),
name
=
'user-assets'
),
url
(
r'^v1/user/assets/$'
,
api
.
UserGrantedAssetsApi
.
as_view
(),
name
=
'my-assets'
),
url
(
r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$'
,
api
.
UserGrantedNodesApi
.
as_view
(),
name
=
'user-nodes'
),
url
(
r'^v1/user/nodes/$'
,
api
.
UserGrantedNodesApi
.
as_view
(),
name
=
'my-nodes'
),
url
(
r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/(?P<node_id>[0-9a-zA-Z\-]{36})/assets/$'
,
api
.
UserGrantedNodeAssetsApi
.
as_view
(),
name
=
'user-node-assets'
),
url
(
r'^v1/user/nodes/(?P<node_id>[0-9a-zA-Z\-]{36})/assets/$'
,
api
.
UserGrantedNodeAssetsApi
.
as_view
(),
name
=
'my-node-assets'
),
url
(
r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes-assets/$'
,
api
.
UserGrantedNodesWithAssetsApi
.
as_view
(),
name
=
'user-nodes-assets'
),
url
(
r'^v1/user/nodes-assets/$'
,
api
.
UserGrantedNodesWithAssetsApi
.
as_view
(),
name
=
'my-nodes-assets'
),
# 查询某个用户组授权的资产和资产组
url
(
r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$'
,
api
.
UserGroupGrantedAssetsApi
.
as_view
(),
name
=
'user-group-assets'
),
url
(
r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$'
,
api
.
UserGroupGrantedNodesApi
.
as_view
(),
name
=
'user-group-nodes'
),
url
(
r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes-assets/$'
,
api
.
UserGroupGrantedNodesWithAssetsApi
.
as_view
(),
name
=
'user-group-nodes-assets'
),
url
(
r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/(?P<node_id>[0-9a-zA-Z\-]{36})/assets/$'
,
api
.
UserGroupGrantedNodeAssetsApi
.
as_view
(),
name
=
'user-group-node-assets'
),
url
(
r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$'
,
api
.
UserGroupGrantedAssetsApi
.
as_view
(),
name
=
'user-group-assets'
),
url
(
r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$'
,
api
.
UserGroupGrantedNodesApi
.
as_view
(),
name
=
'user-group-nodes'
),
url
(
r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes-assets/$'
,
api
.
UserGroupGrantedNodesWithAssetsApi
.
as_view
(),
name
=
'user-group-nodes-assets'
),
url
(
r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/(?P<node_id>[0-9a-zA-Z\-]{36})/assets/$'
,
api
.
UserGroupGrantedNodeAssetsApi
.
as_view
(),
name
=
'user-group-node-assets'
),
# 用户和资产授权变更
url
(
r'^v1/asset-permissions/(?P<pk>[0-9a-zA-Z\-]{36})/user/remove/$'
,
api
.
AssetPermissionRemoveUserApi
.
as_view
(),
name
=
'asset-permission-remove-user'
),
url
(
r'^v1/asset-permissions/(?P<pk>[0-9a-zA-Z\-]{36})/user/add/$'
,
api
.
AssetPermissionAddUserApi
.
as_view
(),
name
=
'asset-permission-add-user'
),
url
(
r'^v1/asset-permissions/(?P<pk>[0-9a-zA-Z\-]{36})/asset/remove/$'
,
api
.
AssetPermissionRemoveAssetApi
.
as_view
(),
name
=
'asset-permission-remove-asset'
),
url
(
r'^v1/asset-permissions/(?P<pk>[0-9a-zA-Z\-]{36})/asset/add/$'
,
api
.
AssetPermissionAddAssetApi
.
as_view
(),
name
=
'asset-permission-add-asset'
),
# 验证用户是否有某个资产和系统用户的权限
url
(
r'v1/asset-permission/user/validate/$'
,
api
.
ValidateUserAssetPermissionView
.
as_view
(),
name
=
'validate-user-asset-permission'
),
...
...
apps/perms/urls/views_urls.py
View file @
f450accb
...
...
@@ -11,8 +11,8 @@ urlpatterns = [
url
(
r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/update$'
,
views
.
AssetPermissionUpdateView
.
as_view
(),
name
=
'asset-permission-update'
),
url
(
r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})$'
,
views
.
AssetPermissionDetailView
.
as_view
(),
name
=
'asset-permission-detail'
),
url
(
r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/delete$'
,
views
.
AssetPermissionDeleteView
.
as_view
(),
name
=
'asset-permission-delete'
),
#
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/user$', views.AssetPermissionUserView.as_view(), name='asset-permission-user-list'),
#
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/asset$', views.AssetPermissionAssetView.as_view(), name='asset-permission-asset-list'),
url
(
r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/user$'
,
views
.
AssetPermissionUserView
.
as_view
(),
name
=
'asset-permission-user-list'
),
url
(
r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/asset$'
,
views
.
AssetPermissionAssetView
.
as_view
(),
name
=
'asset-permission-asset-list'
),
]
apps/perms/views.py
View file @
f450accb
...
...
@@ -4,12 +4,12 @@ from __future__ import unicode_literals, absolute_import
from
django.utils.translation
import
ugettext
as
_
from
django.views.generic
import
ListView
,
CreateView
,
UpdateView
,
DetailView
from
django.views.generic.edit
import
DeleteView
from
django.views.generic.edit
import
DeleteView
,
SingleObjectMixin
from
django.urls
import
reverse_lazy
from
django.conf
import
settings
from
common.
utils
import
is_uuid
from
.hands
import
AdminUserRequiredMixin
,
Node
,
Asset
from
common.
mixins
import
AdminUserRequiredMixin
from
.hands
import
Node
,
Asset
,
SystemUser
,
User
,
UserGroup
from
.models
import
AssetPermission
from
.forms
import
AssetPermissionForm
...
...
@@ -83,7 +83,11 @@ class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView):
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'app'
:
_
(
'Perms'
),
'action'
:
_
(
'Update asset permission'
)
'action'
:
_
(
'Update asset permission'
),
'system_users_remain'
:
SystemUser
.
objects
.
exclude
(
granted_by_permissions
=
self
.
object
),
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
...
...
@@ -95,3 +99,59 @@ class AssetPermissionDeleteView(AdminUserRequiredMixin, DeleteView):
success_url
=
reverse_lazy
(
'perms:asset-permission-list'
)
class
AssetPermissionUserView
(
AdminUserRequiredMixin
,
SingleObjectMixin
,
ListView
):
template_name
=
'perms/asset_permission_user.html'
context_object_name
=
'asset_permission'
paginate_by
=
settings
.
CONFIG
.
DISPLAY_PER_PAGE
object
=
None
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
self
.
object
=
self
.
get_object
(
queryset
=
AssetPermission
.
objects
.
all
())
return
super
()
.
get
(
request
,
*
args
,
**
kwargs
)
def
get_queryset
(
self
):
queryset
=
self
.
object
.
get_all_users
()
return
queryset
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'app'
:
_
(
'Perms'
),
'action'
:
_
(
'Asset permission user list'
),
'users_remain'
:
User
.
objects
.
exclude
(
asset_permissions
=
self
.
object
)
.
exclude
(
role
=
User
.
ROLE_APP
),
'user_groups_remain'
:
UserGroup
.
objects
.
exclude
(
asset_permissions
=
self
.
object
)
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
class
AssetPermissionAssetView
(
AdminUserRequiredMixin
,
SingleObjectMixin
,
ListView
):
template_name
=
'perms/asset_permission_asset.html'
context_object_name
=
'asset_permission'
paginate_by
=
settings
.
CONFIG
.
DISPLAY_PER_PAGE
object
=
None
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
self
.
object
=
self
.
get_object
(
queryset
=
AssetPermission
.
objects
.
all
())
return
super
()
.
get
(
request
,
*
args
,
**
kwargs
)
def
get_queryset
(
self
):
queryset
=
self
.
object
.
get_all_assets
()
return
queryset
def
get_context_data
(
self
,
**
kwargs
):
assets_granted
=
self
.
get_queryset
()
context
=
{
'app'
:
_
(
'Perms'
),
'action'
:
_
(
'Asset permission asset list'
),
'assets_remain'
:
Asset
.
objects
.
exclude
(
id__in
=
[
a
.
id
for
a
in
assets_granted
]),
'nodes_remain'
:
Node
.
objects
.
exclude
(
granted_by_permissions
=
self
.
object
),
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
\ No newline at end of file
apps/static/js/jumpserver.js
View file @
f450accb
...
...
@@ -307,7 +307,7 @@ jumpserver.initDataTable = function (options) {
last
:
"»"
}
},
lengthMenu
:
[[
1
5
,
25
,
50
,
-
1
],
[
15
,
25
,
50
,
"All"
]]
lengthMenu
:
[[
1
0
,
15
,
25
,
50
,
-
1
],
[
10
,
15
,
25
,
50
,
"All"
]]
});
table
.
on
(
'select'
,
function
(
e
,
dt
,
type
,
indexes
)
{
var
$node
=
table
[
type
](
indexes
).
nodes
().
to$
();
...
...
@@ -446,22 +446,56 @@ jumpserver.initServerSideDataTable = function (options) {
last
:
"»"
}
},
lengthMenu
:
[[
1
5
,
25
,
50
],
[
15
,
25
,
50
]]
lengthMenu
:
[[
1
0
,
15
,
25
,
50
],
[
10
,
15
,
25
,
50
]]
});
table
.
selected
=
[];
table
.
on
(
'select'
,
function
(
e
,
dt
,
type
,
indexes
)
{
var
$node
=
table
[
type
](
indexes
).
nodes
().
to$
();
$node
.
find
(
'input.ipt_check'
).
prop
(
'checked'
,
true
);
jumpserver
.
selected
[
$node
.
find
(
'input.ipt_check'
).
prop
(
'id'
)]
=
true
jumpserver
.
selected
[
$node
.
find
(
'input.ipt_check'
).
prop
(
'id'
)]
=
true
;
if
(
type
===
'row'
)
{
var
rows
=
table
.
rows
(
indexes
).
data
();
$
.
each
(
rows
,
function
(
id
,
row
)
{
if
(
row
.
id
){
table
.
selected
.
push
(
row
.
id
)
}
})
}
}).
on
(
'deselect'
,
function
(
e
,
dt
,
type
,
indexes
)
{
var
$node
=
table
[
type
](
indexes
).
nodes
().
to$
();
$node
.
find
(
'input.ipt_check'
).
prop
(
'checked'
,
false
);
jumpserver
.
selected
[
$node
.
find
(
'input.ipt_check'
).
prop
(
'id'
)]
=
false
jumpserver
.
selected
[
$node
.
find
(
'input.ipt_check'
).
prop
(
'id'
)]
=
false
;
if
(
type
===
'row'
)
{
var
rows
=
table
.
rows
(
indexes
).
data
();
$
.
each
(
rows
,
function
(
id
,
row
)
{
if
(
row
.
id
){
var
index
=
table
.
selected
.
indexOf
(
row
.
id
);
if
(
index
>
-
1
){
table
.
selected
.
splice
(
index
,
1
)
}
}
})
}
}).
on
(
'draw'
,
function
(){
$
(
'#op'
).
html
(
options
.
op_html
||
''
);
$
(
'#uc'
).
html
(
options
.
uc_html
||
''
);
var
table_data
=
[];
$
.
each
(
table
.
rows
().
data
(),
function
(
id
,
row
)
{
if
(
row
.
id
)
{
table_data
.
push
(
row
.
id
)
}
});
$
.
each
(
table
.
selected
,
function
(
id
,
data
)
{
var
index
=
table_data
.
indexOf
(
data
);
if
(
index
>
-
1
){
table
.
rows
(
index
).
select
()
}
});
});
$
(
'.ipt_check_all'
).
on
(
'click'
,
function
()
{
var
table_id
=
table
.
settings
()[
0
].
sTableId
;
$
(
'#'
+
table_id
+
' .ipt_check_all'
).
on
(
'click'
,
function
()
{
if
(
$
(
this
).
prop
(
"checked"
))
{
$
(
this
).
closest
(
'table'
).
find
(
'.ipt_check'
).
prop
(
'checked'
,
true
);
table
.
rows
({
search
:
'applied'
,
page
:
'current'
}).
select
();
...
...
apps/templates/_footer.html
View file @
f450accb
<div
class=
"footer fixed"
>
<div
class=
"pull-right"
>
Version
<strong>
1.
0
.0-{% include '_build.html' %}
</strong>
GPLv2.
Version
<strong>
1.
2
.0-{% include '_build.html' %}
</strong>
GPLv2.
<img
style=
"display: none"
src=
"http://www.jumpserver.org/img/evaluate_avatar1.jpg"
>
</div>
<div>
...
...
apps/templates/_modal.html
View file @
f450accb
{% load i18n %}
<style>
.modal-body
{
padding
:
0px
20px
0px
20px
;
}
</style>
<div
aria-hidden=
"true"
role=
"dialog"
id=
"{% block modal_id %}{% endblock %}"
class=
"modal inmodal"
>
<div
class=
"modal-dialog {% block modal_class %}{% endblock %}"
>
<div
class=
"modal-content animated fadeIn"
>
...
...
@@ -12,8 +17,10 @@
{% endblock %}
</div>
<div
class=
"modal-footer"
>
{% block modal_button %}
<button
data-dismiss=
"modal"
class=
"btn btn-white"
type=
"button"
>
{% trans "Close" %}
</button>
<button
class=
"btn btn-primary"
type=
"button"
id=
"{% block modal_confirm_id %}{% endblock %}"
>
{% trans 'Confirm' %}
</button>
{% endblock %}
</div>
</div>
</div>
...
...
apps/templates/_nav.html
View file @
f450accb
...
...
@@ -61,7 +61,7 @@
</li>
<li
id=
"audits"
>
<a>
<i
class=
"fa fa-
coffee
"
style=
"width: 14px"
></i>
<span
class=
"nav-label"
>
{% trans 'Audits' %}
</span><span
class=
"fa arrow"
></span>
<i
class=
"fa fa-
history
"
style=
"width: 14px"
></i>
<span
class=
"nav-label"
>
{% trans 'Audits' %}
</span><span
class=
"fa arrow"
></span>
</a>
<ul
class=
"nav nav-second-level"
>
<li
id=
"ftp-log"
><a
href=
"{% url 'audits:ftp-log-list' %}"
>
{% trans 'FTP log' %}
</a></li>
...
...
apps/terminal/api.py
View file @
f450accb
...
...
@@ -248,7 +248,7 @@ class CommandViewSet(viewsets.ViewSet):
class
SessionReplayViewSet
(
viewsets
.
ViewSet
):
serializer_class
=
ReplaySerializer
permission_classes
=
()
permission_classes
=
(
IsSuperUserOrAppUser
,
)
session
=
None
def
gen_session_path
(
self
):
...
...
apps/users/models/user.py
View file @
f450accb
...
...
@@ -45,7 +45,7 @@ class User(AbstractUser):
wechat
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
verbose_name
=
_
(
'Wechat'
))
phone
=
models
.
CharField
(
max_length
=
20
,
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'Phone'
))
otp_level
=
models
.
SmallIntegerField
(
default
=
0
,
choices
=
OTP_LEVEL_CHOICES
,
verbose_name
=
_
(
'Enable OTP'
))
otp_secret_key
=
models
.
CharField
(
max_length
=
16
,
blank
=
True
,
null
=
True
)
_otp_secret_key
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
null
=
True
)
# Todo: Auto generate key, let user download
_private_key
=
models
.
CharField
(
max_length
=
5000
,
blank
=
True
,
verbose_name
=
_
(
'Private key'
))
_public_key
=
models
.
CharField
(
max_length
=
5000
,
blank
=
True
,
verbose_name
=
_
(
'Public key'
))
...
...
@@ -55,7 +55,7 @@ class User(AbstractUser):
created_by
=
models
.
CharField
(
max_length
=
30
,
default
=
''
,
verbose_name
=
_
(
'Created by'
))
def
__str__
(
self
):
return
self
.
username
return
'{0.name}({0.username})'
.
format
(
self
)
@property
def
password_raw
(
self
):
...
...
@@ -70,6 +70,14 @@ class User(AbstractUser):
def
password_raw
(
self
,
password_raw_
):
self
.
set_password
(
password_raw_
)
@property
def
otp_secret_key
(
self
):
return
signer
.
unsign
(
self
.
_otp_secret_key
)
@otp_secret_key.setter
def
otp_secret_key
(
self
,
item
):
self
.
_otp_secret_key
=
signer
.
sign
(
item
)
.
decode
(
'utf-8'
)
def
get_absolute_url
(
self
):
return
reverse
(
'users:user-detail'
,
args
=
(
self
.
id
,))
...
...
docs/quickstart.rst
View file @
f450accb
...
...
@@ -13,7 +13,7 @@ Docker 安装见: `Docker官方安装文档 <https://docs.docker.com/install/>`_
```````````````
使用 root 命令行输入::
$ docker run -d -p 8080:80 -p 2222:2222 registry.jumpserver.org/public/jumpserver:
latest
$ docker run -d -p 8080:80 -p 2222:2222 registry.jumpserver.org/public/jumpserver:
1.0.0
访问
```````````````
...
...
@@ -42,10 +42,13 @@ XShell等工具请添加connection连接
::
docker run -d -p 8080:80 -p 2222:2222 -e DB_ENGINE=mysql -e DB_HOST=192.168.1.1 -e DB_PORT=3306 -e DB_USER=root -e DB_PASSWORD=xxx -e DB_NAME=jumpserver registry.jumpserver.org/public/jumpserver:
latest
docker run -d -p 8080:80 -p 2222:2222 -e DB_ENGINE=mysql -e DB_HOST=192.168.1.1 -e DB_PORT=3306 -e DB_USER=root -e DB_PASSWORD=xxx -e DB_NAME=jumpserver registry.jumpserver.org/public/jumpserver:
1.0.0
仓库地址
```````````````
https://github.com/jumpserver/Dockerfile
docs/step_by_step.rst
View file @
f450accb
...
...
@@ -49,7 +49,7 @@
# 看到下面的提示符代表成功,以后运行 Jumpserver 都要先运行以上 source 命令,以下所有命令均在该虚拟环境中运行
(py3) [root@localhost py3]
二. 安装 Jumpserver
0.5
.0
二. 安装 Jumpserver
1.0
.0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**2.1 下载或 Clone 项目**
...
...
@@ -201,7 +201,7 @@
Luna 已改为纯前端,需要 Nginx 来运行访问
访问(https://github.com/jumpserver/luna/releases)下载对应 release 包,直接解压,不需要编译
访问(https://github.com/jumpserver/luna/releases)下载对应
版本的
release 包,直接解压,不需要编译
4.1 解压 Luna
...
...
@@ -228,7 +228,7 @@ Luna 已改为纯前端,需要 Nginx 来运行访问
-p 8081:8080 -v /opt/guacamole/key:/config/guacamole/key \
-e JUMPSERVER_KEY_DIR=/config/guacamole/key \
-e JUMPSERVER_SERVER=http://<填写本机的IP地址>:8080 \
registry.jumpserver.org/public/guacamole:
latest
registry.jumpserver.org/public/guacamole:
1.0.0
这里所需要注意的是 guacamole 暴露出来的端口是 8081,若与主机上其他端口冲突请自定义一下。
...
...
utils/2018_04_11_migrate_permissions.sh
View file @
f450accb
...
...
@@ -3,9 +3,10 @@
python ../apps/manage.py shell
<<
EOF
from perms.models import *
from assets.models import SystemUser
for old in NodePermission.objects.all():
perm =
asset_perm_model.objects.using(db_alias)
.create(
perm =
AssetPermission.objects
.create(
name="{}-{}-{}".format(
old.node.value,
old.user_group.name,
...
...
@@ -20,5 +21,10 @@ for old in NodePermission.objects.all():
perm.user_groups.add(old.user_group)
perm.nodes.add(old.node)
perm.system_users.add(old.system_user)
for s in SystemUser.objects.all():
nodes = list(s.nodes.all())
s.nodes.set([])
s.nodes.set(nodes)
EOF
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