mirror of https://github.com/jumpserver/jumpserver
[Merge] with dev
commit
f450accbf8
|
@ -2,4 +2,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__version__ = "1.2.0"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -1,132 +1,125 @@
|
|||
{% 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() {
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
function initTree2() {
|
||||
var setting = {
|
||||
view: {
|
||||
dblClickExpand: false,
|
||||
showLine: true
|
||||
},
|
||||
data: {
|
||||
simpleData: {
|
||||
enable: true
|
||||
}
|
||||
},
|
||||
callback: {
|
||||
onSelected: onSelected2
|
||||
}
|
||||
};
|
||||
|
||||
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");
|
||||
});
|
||||
var current_node;
|
||||
var nodes = zTree.getSelectedNodes();
|
||||
if (nodes && nodes.length === 1) {
|
||||
current_node = nodes[0]
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var data = {
|
||||
'assets': id_list
|
||||
};
|
||||
|
||||
var success = function () {
|
||||
modal_table.ajax.reload()
|
||||
};
|
||||
|
||||
APIUpdateAttr({
|
||||
'url': '/api/assets/v1/nodes/' + current_node.id + '/assets/add/',
|
||||
'method': 'PUT',
|
||||
'body': JSON.stringify(data),
|
||||
'success': success
|
||||
})
|
||||
$(document).ready(function(){
|
||||
initTable2();
|
||||
initTree2();
|
||||
})
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
{% block modal_confirm_id %}btn_select_assets{% endblock %}
|
||||
|
||||
{% block modal_button %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
||||
{% block modal_confirm_id %}btn_asset_modal_confirm{% endblock %}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -59,57 +59,57 @@
|
|||
<i class="fa fa-angle-left fa-x" id="toggle-icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mail-box-header">
|
||||
<div class="uc pull-left m-r-5"><a class="btn btn-sm btn-primary btn-create-asset"> {% trans "Create asset" %} </a></div>
|
||||
<div class="html5buttons">
|
||||
<div class="dt-buttons btn-group">
|
||||
<a class="btn btn-default btn_import" data-toggle="modal" data-target="#asset_import_modal" tabindex="0">
|
||||
<span>{% trans "Import" %}</span>
|
||||
</a>
|
||||
<a class="btn btn-default btn_export" tabindex="0">
|
||||
<span>{% trans "Export" %}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<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 class="mail-box-header">
|
||||
<div class="uc pull-left m-r-5"><a class="btn btn-sm btn-primary btn-create-asset"> {% trans "Create asset" %} </a></div>
|
||||
<div class="html5buttons">
|
||||
<div class="dt-buttons btn-group">
|
||||
<a class="btn btn-default btn_import" data-toggle="modal" data-target="#asset_import_modal" tabindex="0">
|
||||
<span>{% trans "Import" %}</span>
|
||||
</a>
|
||||
<a class="btn btn-default btn_export" tabindex="0">
|
||||
<span>{% trans "Export" %}</span>
|
||||
</a>
|
||||
</div>
|
||||
<table class="table table-striped table-bordered table-hover " id="asset_list_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>
|
||||
<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="remove">{% trans 'Remove from this node' %}</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>
|
||||
</div>
|
||||
</div>
|
||||
<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_list_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>
|
||||
<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="remove">{% trans 'Remove from this node' %}</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -117,15 +117,16 @@
|
|||
|
||||
<div id="rMenu">
|
||||
<ul class="dropdown-menu">
|
||||
<li id="menu_asset_create" class="btn-create-asset" tabindex="-1"><a>{% trans 'Create asset' %}</a></li>
|
||||
<li id="menu_asset_add" class="btn-add-asset" data-toggle="modal" data-target="#asset_list_modal" tabindex="0"><a>{% trans 'Add asset' %}</a></li>
|
||||
<li id="menu_refresh_hardware_info" class="btn-refresh-hardware" tabindex="-1"><a>{% trans 'Refresh node hardware info' %}</a></li>
|
||||
<li id="menu_test_connective" class="btn-test-connective" tabindex="-1"><a>{% trans 'Test node connective' %}</a></li>
|
||||
<li class="divider"></li>
|
||||
<li id="m_create" tabindex="-1" onclick="addTreeNode();"><a>{% trans 'Add node' %}</a></li>
|
||||
<li id="m_del" tabindex="-1" onclick="editTreeNode();"><a>{% trans 'Rename node' %}</a></li>
|
||||
<li id="m_create" tabindex="-1" onclick="addTreeNode();"><a><i class="fa fa-plus-square-o"></i> {% trans 'Add node' %}</a></li>
|
||||
<li id="m_del" tabindex="-1" onclick="editTreeNode();"><a><i class="fa fa-pencil-square-o"></i> {% trans 'Rename node' %}</a></li>
|
||||
<li id="m_del" tabindex="-1" onclick="removeTreeNode();"><a><i class="fa fa-minus-square"></i> {% trans 'Delete node' %}</a></li>
|
||||
<li class="divider"></li>
|
||||
<li id="m_del" tabindex="-1" onclick="removeTreeNode();"><a>{% trans 'Delete node' %}</a></li>
|
||||
<li id="menu_asset_add" class="btn-add-asset" data-toggle="modal" data-target="#asset_list_modal" tabindex="0"><a><i class="fa fa-copy"></i> {% trans 'Add assets to node' %}</a></li>
|
||||
<li id="menu_asset_move" class="btn-move-asset" data-toggle="modal" data-target="#asset_list_modal" tabindex="0"><a><i class="fa fa-cut"></i> {% trans 'Move assets to node' %}</a></li>
|
||||
<li class="divider"></li>
|
||||
<li id="menu_refresh_hardware_info" class="btn-refresh-hardware" tabindex="-1"><a><i class="fa fa-refresh"></i> {% trans 'Refresh node hardware info' %}</a></li>
|
||||
<li id="menu_test_connective" class="btn-test-connective" tabindex="-1"><a><i class="fa fa-chain"></i> {% trans 'Test node connective' %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
@ -136,6 +137,7 @@
|
|||
{% block custom_foot_js %}
|
||||
<script>
|
||||
var zTree, rMenu, asset_table, show = 0;
|
||||
var update_node_action = "";
|
||||
function initTable() {
|
||||
var options = {
|
||||
ele: $('#asset_list_table'),
|
||||
|
@ -210,10 +212,11 @@ function removeTreeNode() {
|
|||
if (!current_node){
|
||||
return
|
||||
}
|
||||
|
||||
if (current_node.children && current_node.children.length > 0) {
|
||||
alert("{% trans 'Have child node, cancel' %}")
|
||||
} else {
|
||||
toastr.error("{% trans 'Have child node, cancel' %}");
|
||||
} else if (current_node.assets_amount !== 0) {
|
||||
toastr.error("{% trans 'Have assets, cancel' %}");
|
||||
} else {
|
||||
var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id );
|
||||
$.ajax({
|
||||
url: url,
|
||||
|
@ -249,13 +252,6 @@ function OnRightClick(event, treeId, treeNode) {
|
|||
|
||||
function showRMenu(type, x, y) {
|
||||
$("#rMenu ul").show();
|
||||
{#if (type === "root") {#}
|
||||
{# return#}
|
||||
{# } else {#}
|
||||
{# $("#m_del").show();#}
|
||||
{# $("#m_check").show();#}
|
||||
{# $("#m_unCheck").show();#}
|
||||
{# }#}
|
||||
x -= 220;
|
||||
rMenu.css({"top":y+"px", "left":x+"px", "visibility":"visible"});
|
||||
|
||||
|
@ -459,7 +455,8 @@ $(document).ready(function(){
|
|||
var current_node;
|
||||
if (nodes && nodes.length ===1 ){
|
||||
current_node = nodes[0];
|
||||
action += "?node_id=" + current_node.id;
|
||||
action = setUrlParam(action, 'node_id', current_node.id);
|
||||
{#action += "?node_id=" + current_node.id;#}
|
||||
$form.attr("action", action)
|
||||
}
|
||||
$form.find('.help-block').remove();
|
||||
|
@ -673,7 +670,45 @@ $(document).ready(function(){
|
|||
break;
|
||||
}
|
||||
$(".ipt_check_all").prop("checked", false)
|
||||
});
|
||||
})
|
||||
.on('click', '#btn_asset_modal_confirm', function () {
|
||||
var assets_selected = asset_table2.selected;
|
||||
var current_node;
|
||||
var nodes = zTree.getSelectedNodes();
|
||||
if (nodes && nodes.length === 1) {
|
||||
current_node = nodes[0]
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
var data = {
|
||||
'assets': assets_selected
|
||||
};
|
||||
var success = function () {
|
||||
asset_table2.selected = [];
|
||||
asset_table2.ajax.reload()
|
||||
};
|
||||
|
||||
var url = '';
|
||||
if (update_node_action === "move") {
|
||||
url = "{% url 'api-assets:node-replace-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id);
|
||||
} else {
|
||||
url = "{% url 'api-assets:node-add-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id);
|
||||
}
|
||||
|
||||
APIUpdateAttr({
|
||||
'url': url,
|
||||
'method': 'PUT',
|
||||
'body': JSON.stringify(data),
|
||||
'success': success
|
||||
})
|
||||
}).on('hidden.bs.modal', '#asset_list_modal', function () {
|
||||
window.location.reload();
|
||||
}).on('click', '#menu_asset_add', function () {
|
||||
update_node_action = "add"
|
||||
}).on('click', '#menu_asset_move', function () {
|
||||
update_node_action = "move"
|
||||
})
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
|
@ -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 %}
|
|
@ -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);
|
||||
|
|
|
@ -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 %}
|
|
@ -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>
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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 False
|
||||
v = False if v in ['False', 0, 'false'] else True
|
||||
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()
|
||||
|
|
|
@ -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__()
|
||||
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)
|
||||
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
||||
|
||||
]
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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_groups_remain %}
|
||||
<option value="{{ asset_group.id }}" id="opt_{{ asset_group.id }}">{{ asset_group.name }}</option>
|
||||
<select data-placeholder="{% trans 'Select nodes' %}" class="select2" id="node_select2" style="width: 100%" multiple="" tabindex="4">
|
||||
{% for node in nodes_remain %}
|
||||
<option value="{{ node.id }}" id="opt_{{ node.id }}">{{ node }}</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.name }}</b></td>
|
||||
<td ><b class="bdg_group" data-gid={{ node.id }}>{{ node }}</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 updateGroup(groups) {
|
||||
function updateNodes(nodes, success) {
|
||||
var the_url = "{% url 'api-perms:asset-permission-detail' pk=asset_permission.id %}";
|
||||
var body = {
|
||||
asset_groups: groups
|
||||
nodes: nodes
|
||||
};
|
||||
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 groups = $('.bdg_group').map(function() {
|
||||
var nodes = $('.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 %}
|
||||
|
|
|
@ -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 %}
|
|
@ -15,19 +15,19 @@
|
|||
<div class="panel-options">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active">
|
||||
<a href="{% url 'perms:asset-permission-detail' pk=asset_permission.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||
<a href="{% url 'perms:asset-permission-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'perms:asset-permission-user-list' pk=asset_permission.id %}" class="text-center">
|
||||
<a href="{% url 'perms:asset-permission-user-list' pk=object.id %}" class="text-center">
|
||||
<i class="fa fa-bar-chart-o"></i> {% trans 'Users and user groups' %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'perms:asset-permission-asset-list' pk=asset_permission.id %}" class="text-center">
|
||||
<a href="{% url 'perms:asset-permission-asset-list' pk=object.id %}" class="text-center">
|
||||
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets and asset groups' %}</a>
|
||||
</li>
|
||||
<li class="pull-right">
|
||||
<a class="btn btn-outline btn-default" href="{% url 'perms:asset-permission-update' pk=asset_permission.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
|
||||
<a class="btn btn-outline btn-default" href="{% url 'perms:asset-permission-update' pk=object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
|
||||
</li>
|
||||
<li class="pull-right">
|
||||
<a class="btn btn-outline btn-danger btn-delete-perm">
|
||||
|
@ -40,7 +40,7 @@
|
|||
<div class="col-sm-7" style="padding-left: 0;">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span class="label"><b>{{ asset_permission.name }}</b></span>
|
||||
<span class="label"><b>{{ object.name }}</b></span>
|
||||
<div class="ibox-tools">
|
||||
<a class="collapse-link">
|
||||
<i class="fa fa-chevron-up"></i>
|
||||
|
@ -60,43 +60,47 @@
|
|||
<tbody>
|
||||
<tr class="no-borders-tr">
|
||||
<td>{% trans 'Name' %}:</td>
|
||||
<td><b>{{ asset_permission.name }}</b></td>
|
||||
<td><b>{{ object.name }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'User count' %}:</td>
|
||||
<td><b>{{ asset_permission.users.count }}</b></td>
|
||||
<td><b>{{ object.users.count }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'User group count' %}:</td>
|
||||
<td><b>{{ asset_permission.users.count }}</b></td>
|
||||
<td><b>{{ object.users.count }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Asset count' %}:</td>
|
||||
<td><b>{{ asset_permission.assets.count }}</b></td>
|
||||
<td><b>{{ object.assets.count }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Asset group count' %}:</td>
|
||||
<td><b>{{ asset_permission.asset_groups.count }}</b></td>
|
||||
<td>{% trans 'Node count' %}:</td>
|
||||
<td><b>{{ object.nodes.count }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'System user count' %}:</td>
|
||||
<td><b>{{ asset_permission.system_users.count }}</b></td>
|
||||
<td><b>{{ object.system_users.count }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Date start' %}:</td>
|
||||
<td><b>{{ object.date_start }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Date expired' %}:</td>
|
||||
<td><b>{{ asset_permission.date_expired }}</b></td>
|
||||
<td><b>{{ object.date_expired }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Date created' %}:</td>
|
||||
<td><b>{{ asset_permission.date_created }}</b></td>
|
||||
<td><b>{{ object.date_created }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Created by' %}:</td>
|
||||
<td><b>{{ asset_permission.created_by }}</b></td>
|
||||
<td><b>{{ object.created_by }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Comment' %}:</td>
|
||||
<td><b>{{ asset_permission.comment }}</b></td>
|
||||
<td><b>{{ object.comment }}</b></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -117,7 +121,7 @@
|
|||
<td><span style="float: right">
|
||||
<div class="switch">
|
||||
<div class="onoffswitch">
|
||||
<input type="checkbox" {% if asset_permission.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active">
|
||||
<input type="checkbox" {% if object.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active">
|
||||
<label class="onoffswitch-label" for="is_active">
|
||||
<span class="onoffswitch-inner"></span>
|
||||
<span class="onoffswitch-switch"></span>
|
||||
|
@ -143,7 +147,7 @@
|
|||
<td colspan="2">
|
||||
<select data-placeholder="{% trans 'Select system users' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||
{% for system_user in system_users_remain %}
|
||||
<option value="{{ system_user.id }}" id="opt_{{ system_user.id }}">{{ system_user.name }}</option>
|
||||
<option value="{{ system_user.id }}" id="opt_{{ system_user.id }}">{{ system_user }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
|
@ -155,9 +159,9 @@
|
|||
</tr>
|
||||
</form>
|
||||
|
||||
{% for system_user in system_users %}
|
||||
{% for system_user in object.system_users.all %}
|
||||
<tr {% if forloop.counter == 1 %} class="no-borders-tr" {% endif %} >
|
||||
<td ><b class="bdg-system-user" data-uid={{ system_user.id }}>{{ system_user.name }}</b></td>
|
||||
<td ><b class="bdg-system-user" data-uid={{ system_user.id }}>{{ system_user }}</b></td>
|
||||
<td>
|
||||
<button class="btn btn-danger btn-xs btn-remove-user" data-uid="{{ system_user.id }}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
|
||||
</td>
|
||||
|
@ -179,7 +183,7 @@
|
|||
jumpserver.system_users_selected = {};
|
||||
|
||||
function updateSystemUser(system_users) {
|
||||
var the_url = "{% url 'api-perms:asset-permission-detail' pk=asset_permission.id %}";
|
||||
var the_url = "{% url 'api-perms:asset-permission-detail' pk=object.id %}";
|
||||
var body = {
|
||||
system_users: Object.assign([], system_users)
|
||||
};
|
||||
|
@ -203,7 +207,7 @@ $(document).ready(function () {
|
|||
.on('click', '.btn-delete-perm', function () {
|
||||
var $this = $(this);
|
||||
var name = "{{ asset_permission.name }}";
|
||||
var uid = "{{ asset_permission.id }}";
|
||||
var uid = "{{ object.id }}";
|
||||
var the_url = '{% url "api-perms:asset-permission-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
|
||||
var redirect_url = "{% url 'perms:asset-permission-list' %}";
|
||||
objectDelete($this, name, the_url, redirect_url);
|
||||
|
@ -238,7 +242,7 @@ $(document).ready(function () {
|
|||
updateSystemUser(system_users);
|
||||
$tr.remove()
|
||||
}).on('click', '#is_active', function () {
|
||||
var the_url = '{% url "api-perms:asset-permission-detail" pk=asset_permission.id %}';
|
||||
var the_url = '{% url "api-perms:asset-permission-detail" pk=object.id %}';
|
||||
var checked = $(this).prop('checked');
|
||||
var body = {
|
||||
'is_active': checked
|
||||
|
|
|
@ -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.is_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.inherit %} 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>
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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'),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -307,7 +307,7 @@ jumpserver.initDataTable = function (options) {
|
|||
last: "»"
|
||||
}
|
||||
},
|
||||
lengthMenu: [[15, 25, 50, -1], [15, 25, 50, "All"]]
|
||||
lengthMenu: [[10, 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: [[15, 25, 50], [15, 25, 50]]
|
||||
lengthMenu: [[10, 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();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<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>
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{% 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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,))
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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,若与主机上其他端口冲突请自定义一下。
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue