mirror of https://github.com/jumpserver/jumpserver
Bugfix (#2938)
* [Update] 修改assets * [Update] 修复用户组页面权限 * [Update] 统一授权资产页面,修改apiUpdateAttr函数名称pull/2942/head
parent
e5bdceed58
commit
55b049c86a
|
@ -13,11 +13,11 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
|
||||||
from rest_framework.pagination import LimitOffsetPagination
|
from rest_framework.pagination import LimitOffsetPagination
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
from orgs.mixins import OrgBulkModelViewSet
|
||||||
from ..hands import IsOrgAdmin
|
from ..hands import IsOrgAdmin
|
||||||
from ..models import Label
|
from ..models import Label
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
|
@ -27,7 +27,7 @@ logger = get_logger(__file__)
|
||||||
__all__ = ['LabelViewSet']
|
__all__ = ['LabelViewSet']
|
||||||
|
|
||||||
|
|
||||||
class LabelViewSet(BulkModelViewSet):
|
class LabelViewSet(OrgBulkModelViewSet):
|
||||||
filter_fields = ("name", "value")
|
filter_fields = ("name", "value")
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Generated by Django 2.1.7 on 2019-07-11 12:18
|
||||||
|
|
||||||
|
import common.fields.model
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0034_auto_20190705_1348'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='private_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='authbook',
|
||||||
|
name='private_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='gateway',
|
||||||
|
name='private_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='private_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -28,7 +28,7 @@ class AssetUser(OrgModelMixin):
|
||||||
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||||
username = models.CharField(max_length=32, blank=True, verbose_name=_('Username'), validators=[alphanumeric])
|
username = models.CharField(max_length=32, blank=True, verbose_name=_('Username'), validators=[alphanumeric])
|
||||||
password = fields.EncryptCharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
|
password = fields.EncryptCharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
|
||||||
private_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
|
private_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH private key'))
|
||||||
public_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH public key'))
|
public_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH public key'))
|
||||||
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
||||||
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_("Date created"))
|
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_("Date created"))
|
||||||
|
|
|
@ -21,17 +21,14 @@ class AdminUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
||||||
model = AdminUser
|
model = AdminUser
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'name', 'username', 'password', 'private_key', 'public_key',
|
'id', 'name', 'username', 'password', 'private_key', 'public_key',
|
||||||
'comment', 'assets_amount',
|
'comment', 'assets_amount', 'date_created', 'date_updated', 'created_by',
|
||||||
'date_created', 'date_updated', 'created_by',
|
|
||||||
]
|
]
|
||||||
|
read_only_fields = ['date_created', 'date_updated', 'created_by', 'assets_amount']
|
||||||
|
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'password': {"write_only": True},
|
'password': {"write_only": True},
|
||||||
'private_key': {"write_only": True},
|
'private_key': {"write_only": True},
|
||||||
'public_key': {"write_only": True},
|
'public_key': {"write_only": True},
|
||||||
'date_created': {'read_only': True},
|
|
||||||
'date_updated': {'read_only': True},
|
|
||||||
'created_by': {'read_only': True},
|
|
||||||
'assets_amount': {'label': _('Asset')},
|
'assets_amount': {'label': _('Asset')},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ function showAuth() {
|
||||||
var msg = "{% trans 'Get auth info error' %}";
|
var msg = "{% trans 'Get auth info error' %}";
|
||||||
toastr.error(msg)
|
toastr.error(msg)
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: url,
|
url: url,
|
||||||
method: "GET",
|
method: "GET",
|
||||||
success: success,
|
success: success,
|
||||||
|
|
|
@ -141,7 +141,7 @@ $(document).ready(function(){
|
||||||
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
||||||
window.open(url, '', 'width=800,height=600,left=400,top=400')
|
window.open(url, '', 'width=800,height=600,left=400,top=400')
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
success: success,
|
success: success,
|
||||||
|
|
|
@ -235,7 +235,7 @@ function onRename(event, treeId, treeNode, isCancel){
|
||||||
if (isCancel){
|
if (isCancel){
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: url,
|
url: url,
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
@ -274,7 +274,7 @@ function onDrop(event, treeId, treeNodes, targetNode, moveType) {
|
||||||
|
|
||||||
var the_url = "{% url 'api-assets:node-add-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", targetNode.meta.node.id);
|
var the_url = "{% url 'api-assets:node-add-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", targetNode.meta.node.id);
|
||||||
var body = {nodes: treeNodesIds};
|
var body = {nodes: treeNodesIds};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: JSON.stringify(body)
|
body: JSON.stringify(body)
|
||||||
|
|
|
@ -88,7 +88,7 @@ $(document).ready(function () {
|
||||||
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
||||||
window.open(url, '', 'width=800,height=600,left=400,top=400')
|
window.open(url, '', 'width=800,height=600,left=400,top=400')
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
success: success,
|
success: success,
|
||||||
|
|
|
@ -131,7 +131,7 @@ function replaceNodeAssetsAdminUser(nodes) {
|
||||||
// clear jumpserver.groups_selected
|
// clear jumpserver.groups_selected
|
||||||
jumpserver.nodes_selected = {};
|
jumpserver.nodes_selected = {};
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
|
|
@ -84,7 +84,7 @@ $(document).ready(function () {
|
||||||
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
||||||
window.open(url, '', 'width=800,height=600,left=400,top=400')
|
window.open(url, '', 'width=800,height=600,left=400,top=400')
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
success: success,
|
success: success,
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% trans 'Protocol' %}</td>
|
<td>{% trans 'Protocol' %}</td>
|
||||||
<td>{{ asset.protocols }}</td>
|
<td><b>{{ asset.protocols }}</b></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% trans 'Admin user' %}:</td>
|
<td>{% trans 'Admin user' %}:</td>
|
||||||
|
@ -267,7 +267,7 @@ function updateAssetNodes(nodes) {
|
||||||
// clear jumpserver.groups_selected
|
// clear jumpserver.groups_selected
|
||||||
jumpserver.nodes_selected = {};
|
jumpserver.nodes_selected = {};
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -282,7 +282,7 @@ function refreshAssetHardware() {
|
||||||
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
||||||
window.open(url, '', 'width=800,height=600')
|
window.open(url, '', 'width=800,height=600')
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
success: success,
|
success: success,
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
|
@ -306,7 +306,7 @@ $(document).ready(function () {
|
||||||
};
|
};
|
||||||
var success = '{% trans "Update successfully!" %}';
|
var success = '{% trans "Update successfully!" %}';
|
||||||
var status = $(".ibox-content > table > tbody > tr:nth-child(13) > td:last >b").text();
|
var status = $(".ibox-content > table > tbody > tr:nth-child(13) > td:last >b").text();
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success_message: success
|
success_message: success
|
||||||
|
@ -360,7 +360,7 @@ $(document).ready(function () {
|
||||||
window.open(url, '', 'width=800,height=600')
|
window.open(url, '', 'width=800,height=600')
|
||||||
};
|
};
|
||||||
|
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
success: success
|
success: success
|
||||||
|
|
|
@ -360,7 +360,7 @@ $(document).ready(function(){
|
||||||
setTimeout( function () {
|
setTimeout( function () {
|
||||||
window.location.reload();}, 500);
|
window.location.reload();}, 500);
|
||||||
}
|
}
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
|
@ -377,7 +377,7 @@ $(document).ready(function(){
|
||||||
setTimeout( function () {
|
setTimeout( function () {
|
||||||
window.location.reload();}, 300);
|
window.location.reload();}, 300);
|
||||||
}
|
}
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
|
@ -397,7 +397,7 @@ $(document).ready(function(){
|
||||||
},function () {
|
},function () {
|
||||||
function success(data) {
|
function success(data) {
|
||||||
url = setUrlParam(the_url, 'spm', data.spm);
|
url = setUrlParam(the_url, 'spm', data.spm);
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url:url,
|
url:url,
|
||||||
method:'DELETE',
|
method:'DELETE',
|
||||||
success:refreshTag,
|
success:refreshTag,
|
||||||
|
@ -410,7 +410,7 @@ $(document).ready(function(){
|
||||||
var msg = "{% trans 'Asset Deleting failed.' %}";
|
var msg = "{% trans 'Asset Deleting failed.' %}";
|
||||||
swal("{% trans 'Asset Delete' %}", msg, "error");
|
swal("{% trans 'Asset Delete' %}", msg, "error");
|
||||||
}
|
}
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: "{% url 'api-common:resources-cache' %}",
|
url: "{% url 'api-common:resources-cache' %}",
|
||||||
method:'POST',
|
method:'POST',
|
||||||
body:JSON.stringify(data),
|
body:JSON.stringify(data),
|
||||||
|
@ -428,7 +428,7 @@ $(document).ready(function(){
|
||||||
var url = "{% url 'assets:asset-bulk-update' %}";
|
var url = "{% url 'assets:asset-bulk-update' %}";
|
||||||
location.href= setUrlParam(url, 'spm', data.spm);
|
location.href= setUrlParam(url, 'spm', data.spm);
|
||||||
}
|
}
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: "{% url 'api-common:resources-cache' %}",
|
url: "{% url 'api-common:resources-cache' %}",
|
||||||
method:'POST',
|
method:'POST',
|
||||||
body:JSON.stringify(data),
|
body:JSON.stringify(data),
|
||||||
|
@ -452,7 +452,7 @@ $(document).ready(function(){
|
||||||
asset_table.ajax.reload()
|
asset_table.ajax.reload()
|
||||||
};
|
};
|
||||||
|
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
'url': '/api/assets/v1/nodes/' + current_node_id + '/assets/remove/',
|
'url': '/api/assets/v1/nodes/' + current_node_id + '/assets/remove/',
|
||||||
'method': 'PUT',
|
'method': 'PUT',
|
||||||
'body': JSON.stringify(data),
|
'body': JSON.stringify(data),
|
||||||
|
@ -500,7 +500,7 @@ $(document).ready(function(){
|
||||||
url = "{% url 'api-assets:node-add-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node_id);
|
url = "{% url 'api-assets:node-add-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
'url': url,
|
'url': url,
|
||||||
'method': 'PUT',
|
'method': 'PUT',
|
||||||
'body': JSON.stringify(data),
|
'body': JSON.stringify(data),
|
||||||
|
@ -524,7 +524,7 @@ $(document).ready(function(){
|
||||||
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
||||||
window.open(url, '', 'width=800,height=600')
|
window.open(url, '', 'width=800,height=600')
|
||||||
}
|
}
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: "GET",
|
method: "GET",
|
||||||
success: success,
|
success: success,
|
||||||
|
@ -539,7 +539,7 @@ $(document).ready(function(){
|
||||||
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
||||||
window.open(url, '', 'width=800,height=600')
|
window.open(url, '', 'width=800,height=600')
|
||||||
}
|
}
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: "GET",
|
method: "GET",
|
||||||
success: success,
|
success: success,
|
||||||
|
|
|
@ -136,7 +136,7 @@ function updateCMDFilterSystemUsers(system_users) {
|
||||||
var success = function(data) {
|
var success = function(data) {
|
||||||
location.reload();
|
location.reload();
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
|
|
|
@ -134,7 +134,7 @@ $(document).ready(function(){
|
||||||
var data = $("#test_gateway_form").serializeObject();
|
var data = $("#test_gateway_form").serializeObject();
|
||||||
var uid = data.gateway_id;
|
var uid = data.gateway_id;
|
||||||
var the_url = '{% url "api-assets:test-gateway-connective" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
|
var the_url = '{% url "api-assets:test-gateway-connective" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({'port': parseInt(data.port)}),
|
body: JSON.stringify({'port': parseInt(data.port)}),
|
||||||
|
|
|
@ -146,7 +146,7 @@ function updateSystemUserNode(nodes) {
|
||||||
// clear jumpserver.nodes_selected
|
// clear jumpserver.nodes_selected
|
||||||
jumpserver.nodes_selected = {};
|
jumpserver.nodes_selected = {};
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -206,7 +206,7 @@ $(document).ready(function () {
|
||||||
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
||||||
window.open(url, '', 'width=800,height=600,left=400,top=400')
|
window.open(url, '', 'width=800,height=600,left=400,top=400')
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
error: error,
|
error: error,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
@ -226,7 +226,7 @@ $(document).ready(function () {
|
||||||
var error = function (data) {
|
var error = function (data) {
|
||||||
alert(data)
|
alert(data)
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
success: success,
|
success: success,
|
||||||
|
@ -243,7 +243,7 @@ $(document).ready(function () {
|
||||||
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
||||||
window.open(url, '', 'width=800,height=600,left=400,top=400')
|
window.open(url, '', 'width=800,height=600,left=400,top=400')
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
error: error,
|
error: error,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
|
|
@ -212,7 +212,7 @@ function updateCommandFilters(command_filters) {
|
||||||
var success = function(data) {
|
var success = function(data) {
|
||||||
location.reload();
|
location.reload();
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -235,7 +235,7 @@ $(document).ready(function () {
|
||||||
var body = {
|
var body = {
|
||||||
'auto_push': checked
|
'auto_push': checked
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body)
|
body: JSON.stringify(body)
|
||||||
});
|
});
|
||||||
|
@ -254,7 +254,7 @@ $(document).ready(function () {
|
||||||
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
||||||
window.open(url, '', 'width=800,height=600,left=400,top=400')
|
window.open(url, '', 'width=800,height=600,left=400,top=400')
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
success: success,
|
success: success,
|
||||||
|
@ -268,7 +268,7 @@ $(document).ready(function () {
|
||||||
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
||||||
window.open(url, '', 'width=800,height=600')
|
window.open(url, '', 'width=800,height=600')
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
success: success,
|
success: success,
|
||||||
|
|
|
@ -182,7 +182,7 @@ $(document).ready(function(){
|
||||||
swal("{% trans 'System Users Delete' %}", msg, "error");
|
swal("{% trans 'System Users Delete' %}", msg, "error");
|
||||||
};
|
};
|
||||||
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
|
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
|
||||||
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
|
requestApi({url: url_delete, method: 'DELETE', success: success, error: fail});
|
||||||
$data_table.ajax.reload();
|
$data_table.ajax.reload();
|
||||||
jumpserver.checked = false;
|
jumpserver.checked = false;
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,47 +11,7 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content">
|
<div class="wrapper wrapper-content">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-3" id="split-left" style="padding-left: 3px">
|
{% include 'users/_granted_assets.html' %}
|
||||||
<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="assetTree" class="ztree">
|
|
||||||
</div>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-9 animated fadeInRight" id="split-right">
|
|
||||||
<div class="tree-toggle">
|
|
||||||
<div class="btn btn-sm btn-primary tree-toggle-btn" onclick="toggle()">
|
|
||||||
<i class="fa fa-angle-left fa-x" id="toggle-icon"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mail-box-header">
|
|
||||||
<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="user_assets_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 'System users' %}</th>
|
|
||||||
<th class="text-center">{% trans 'Action' %}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -62,121 +22,51 @@
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
var treeUrl = "{% url 'api-perms:my-nodes-as-tree' %}?&cache_policy=1";
|
var treeUrl = "{% url 'api-perms:my-nodes-as-tree' %}?&cache_policy=1";
|
||||||
var zTree, asset_table, show=0;
|
var assetTableUrl = "{% url 'api-perms:my-assets' %}?cache_policy=1";
|
||||||
var inited = false;
|
var selectUrl = '{% url "api-perms:my-node-assets" node_id=DEFAULT_PK %}?cache_policy=1';
|
||||||
var url;
|
var showAssetHref = false; // Need input default true
|
||||||
|
var actions = {
|
||||||
|
targets: 4, createdCell: function (td, cellData) {
|
||||||
function initTable() {
|
var conn_btn = '<a href="{% url "luna-view" %}?login_to=' + cellData +
|
||||||
if (inited){
|
'" class="btn btn-xs btn-primary">{% trans "Connect" %}</a>';
|
||||||
return
|
$(td).html(conn_btn)
|
||||||
} else {
|
}};
|
||||||
inited = true;
|
|
||||||
}
|
|
||||||
url = "{% url 'api-perms:my-assets' %}?cache_policy=1";
|
|
||||||
var options = {
|
|
||||||
ele: $('#user_assets_table'),
|
|
||||||
columnDefs: [
|
|
||||||
{targets: 1, createdCell: function (td, cellData, rowData) {
|
|
||||||
var detail_btn = '<a class="asset_detail" asset-id="rowData_id" data-toggle="modal" data-target="#user_asset_detail_modal" tabindex="0">'+ cellData +'</a>'
|
|
||||||
$(td).html(detail_btn.replace("rowData_id", rowData.id));
|
|
||||||
}},
|
|
||||||
{targets: 3, createdCell: function (td, cellData) {
|
|
||||||
var users = [];
|
|
||||||
$.each(cellData, function (id, data) {
|
|
||||||
users.push(data.name);
|
|
||||||
});
|
|
||||||
$(td).html(users.join(', '))
|
|
||||||
}},
|
|
||||||
{targets: 4, createdCell: function (td, cellData) {
|
|
||||||
var conn_btn = '<a href="{% url "luna-view" %}?login_to=' + cellData +'" class="btn btn-xs btn-primary">{% trans "Connect" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
|
|
||||||
$(td).html(conn_btn)
|
|
||||||
}}
|
|
||||||
],
|
|
||||||
ajax_url: url,
|
|
||||||
columns: [
|
|
||||||
{data: "id"}, {data: "hostname" }, {data: "ip" },
|
|
||||||
{data: "system_users_granted", orderable: false},
|
|
||||||
{data: "id", orderable: false}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
asset_table = jumpserver.initServerSideDataTable(options);
|
|
||||||
return asset_table
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSelected(event, treeNode) {
|
|
||||||
url = '{% url "api-perms:my-node-assets" node_id=DEFAULT_PK %}?cache_policy=1';
|
|
||||||
var node_id = treeNode.meta.node.id;
|
|
||||||
url = url.replace("{{ DEFAULT_PK }}", node_id);
|
|
||||||
setCookie('node_selected', treeNode.id);
|
|
||||||
asset_table.ajax.url(url);
|
|
||||||
asset_table.ajax.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
function initTree() {
|
|
||||||
var setting = {
|
|
||||||
view: {
|
|
||||||
dblClickExpand: false,
|
|
||||||
showLine: true,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
simpleData: {
|
|
||||||
enable: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
callback: {
|
|
||||||
onSelected: onSelected
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var zNodes = [];
|
|
||||||
$.get(treeUrl, function(data, status){
|
|
||||||
zNodes = data;
|
|
||||||
$.fn.zTree.init($("#assetTree"), setting, zNodes);
|
|
||||||
zTree = $.fn.zTree.getZTreeObj("assetTree");
|
|
||||||
rootNodeAddDom(zTree, function () {
|
|
||||||
treeUrl = treeUrl.replace('cache_policy=1', 'cache_policy=2');
|
|
||||||
initTree();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
initTree();
|
initTree();
|
||||||
initTable();
|
initTable();
|
||||||
}).on('click', '.labels li', function () {
|
}).on('click', '.labels li', function () {
|
||||||
var val = $(this).text();
|
var val = $(this).text();
|
||||||
$("#user_assets_table_filter input").val(val);
|
$("#user_assets_table_filter input").val(val);
|
||||||
asset_table.search(val).draw();
|
assetTable.search(val).draw();
|
||||||
})
|
})
|
||||||
.on('click', '.asset_detail', function() {
|
.on('click', '.asset-detail', function(e) {
|
||||||
var data = asset_table.ajax.json();
|
e.preventDefault();
|
||||||
var asset_id = this.getAttribute("asset-id");
|
var data = assetTable.ajax.json();
|
||||||
|
var assetId = $(this).data("asset");
|
||||||
var trs = '';
|
var trs = '';
|
||||||
var desc = {
|
var desc = {
|
||||||
'hostname': "{% trans 'Hostname' %}",
|
'hostname': "{% trans 'Hostname' %}",
|
||||||
'ip': "{% trans 'IP' %}",
|
'ip': "{% trans 'IP' %}",
|
||||||
'port': "{% trans 'Port' %}",
|
'protocols': "{% trans 'Protocols' %}",
|
||||||
'protocol': "{% trans 'Protocol' %}",
|
|
||||||
'platform': "{% trans 'Platform' %}",
|
'platform': "{% trans 'Platform' %}",
|
||||||
'os': "{% trans 'OS' %}",
|
|
||||||
'system_users_join': "{% trans 'System user' %}",
|
'system_users_join': "{% trans 'System user' %}",
|
||||||
'domain': "{% trans 'Domain' %}",
|
'domain': "{% trans 'Domain' %}",
|
||||||
'is_active': "{% trans 'Is active' %}",
|
|
||||||
'comment': "{% trans 'Comment' %}"
|
|
||||||
{#'date_joined': "{% trans 'Date joined' %}",#}
|
|
||||||
};
|
};
|
||||||
$.each(data.results, function(index, value){
|
var value;
|
||||||
if(value.id === asset_id){
|
for (var i = 0; i < data.results.length; i++) {
|
||||||
|
value = data.results[i];
|
||||||
|
if(value.id === assetId){
|
||||||
for(var i in desc){
|
for(var i in desc){
|
||||||
trs += "<tr class='no-borders-tr'>\n" +
|
trs += "<tr class='no-borders-tr'>\n" +
|
||||||
"<td>"+ desc[i] + ":</td>"+
|
"<td>"+ desc[i] + ":</td>"+
|
||||||
"<td><b>"+ (value[i] === null?'':value[i]) + "</b></td>\n" +
|
"<td><b>"+ (value[i] === null?'':value[i]) + "</b></td>\n" +
|
||||||
"</tr>";
|
"</tr>";
|
||||||
}
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
$('#asset_detail_tbody').html(trs)
|
$('#asset_detail_tbody').html(trs)
|
||||||
|
$('#user_asset_detail_modal').modal();
|
||||||
});
|
});
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
|
@ -195,5 +85,4 @@ function toggle() {
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -1,7 +1,8 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
#
|
#
|
||||||
import time
|
import time
|
||||||
from django.db.models import Prefetch
|
from functools import reduce
|
||||||
|
from django.db.models import Prefetch, Q
|
||||||
|
|
||||||
from common.utils import get_object_or_none, get_logger
|
from common.utils import get_object_or_none, get_logger
|
||||||
from common.struct import Stack
|
from common.struct import Stack
|
||||||
|
@ -21,24 +22,34 @@ def get_system_user_by_id(id):
|
||||||
return system_user
|
return system_user
|
||||||
|
|
||||||
|
|
||||||
class LabelFilter:
|
class LabelFilterMixin:
|
||||||
def filter_queryset(self, queryset):
|
def get_filter_labels_ids(self):
|
||||||
queryset = super().filter_queryset(queryset)
|
query_params = self.request.query_params
|
||||||
query_keys = self.request.query_params.keys()
|
query_keys = query_params.keys()
|
||||||
all_label_keys = Label.objects.values_list('name', flat=True)
|
all_label_keys = Label.objects.values_list('name', flat=True)
|
||||||
valid_keys = set(all_label_keys) & set(query_keys)
|
valid_keys = set(all_label_keys) & set(query_keys)
|
||||||
labels_query = {}
|
|
||||||
for key in valid_keys:
|
|
||||||
labels_query[key] = self.request.query_params.get(key)
|
|
||||||
|
|
||||||
conditions = []
|
if not valid_keys:
|
||||||
for k, v in labels_query.items():
|
return []
|
||||||
query = {'labels__name': k, 'labels__value': v}
|
|
||||||
conditions.append(query)
|
|
||||||
|
|
||||||
if conditions:
|
labels_query = [
|
||||||
for kwargs in conditions:
|
{"name": key, "value": query_params[key]}
|
||||||
queryset = queryset.filter(**kwargs)
|
for key in valid_keys
|
||||||
|
]
|
||||||
|
args = [Q(**kwargs) for kwargs in labels_query]
|
||||||
|
args = reduce(lambda x, y: x | y, args)
|
||||||
|
labels_id = Label.objects.filter(args).values_list('id', flat=True)
|
||||||
|
return labels_id
|
||||||
|
|
||||||
|
|
||||||
|
class LabelFilter(LabelFilterMixin):
|
||||||
|
def filter_queryset(self, queryset):
|
||||||
|
queryset = super().filter_queryset(queryset)
|
||||||
|
labels_ids = self.get_filter_labels_ids()
|
||||||
|
if not labels_ids:
|
||||||
|
return queryset
|
||||||
|
for labels_id in labels_ids:
|
||||||
|
queryset = queryset.filter(labels=labels_id)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ class UserAssetListView(PermissionsMixin, TemplateView):
|
||||||
context = {
|
context = {
|
||||||
'action': _('My assets'),
|
'action': _('My assets'),
|
||||||
'labels': Label.objects.all().order_by('name'),
|
'labels': Label.objects.all().order_by('name'),
|
||||||
'system_users': SystemUser.objects.all(),
|
'show_actions': True
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
|
@ -38,7 +38,7 @@ $(document).ready(function () {
|
||||||
var error = function () {
|
var error = function () {
|
||||||
$("#mfa_error").addClass("text-danger").html(codeError);
|
$("#mfa_error").addClass("text-danger").html(codeError);
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: url,
|
url: url,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
|
|
|
@ -145,13 +145,13 @@ class NeedMFAVerify(permissions.BasePermission):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class CanUpdateSuperUser(permissions.BasePermission):
|
class CanUpdateDeleteSuperUser(permissions.BasePermission):
|
||||||
def has_object_permission(self, request, view, obj):
|
def has_object_permission(self, request, view, obj):
|
||||||
if request.method in ['GET', 'OPTIONS']:
|
if request.method in ['GET', 'OPTIONS']:
|
||||||
return True
|
return True
|
||||||
if str(request.user.id) == str(obj.id):
|
elif request.method == 'DELETE' and str(request.user.id) == str(obj.id):
|
||||||
return False
|
return False
|
||||||
if request.user.is_superuser:
|
elif request.user.is_superuser:
|
||||||
return True
|
return True
|
||||||
if hasattr(obj, 'is_superuser') and obj.is_superuser:
|
if hasattr(obj, 'is_superuser') and obj.is_superuser:
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -255,7 +255,7 @@ function execute() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: url,
|
url: url,
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
|
@ -109,7 +109,7 @@ $(document).ready(function() {
|
||||||
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
|
||||||
window.open(url, '', 'width=800,height=600,left=400,top=400')
|
window.open(url, '', 'width=800,height=600,left=400,top=400')
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
error: error,
|
error: error,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from functools import reduce
|
||||||
|
from hashlib import md5
|
||||||
|
from django.core.cache import cache
|
||||||
|
from django.db.models import Q
|
||||||
|
from django.conf import settings
|
||||||
|
from rest_framework.views import Response
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
from common.utils import get_logger
|
||||||
|
from assets.utils import LabelFilterMixin
|
||||||
|
from ..utils import (
|
||||||
|
AssetPermissionUtil
|
||||||
|
)
|
||||||
|
from .. import const
|
||||||
|
from ..hands import Asset, Node, SystemUser, Label
|
||||||
|
from .. import serializers
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
__all__ = ['UserPermissionCacheMixin', 'GrantAssetsMixin', 'NodesWithUngroupMixin']
|
||||||
|
|
||||||
|
|
||||||
|
class UserPermissionCacheMixin:
|
||||||
|
cache_policy = '0'
|
||||||
|
RESP_CACHE_KEY = '_PERMISSION_RESPONSE_CACHE_V2_{}'
|
||||||
|
CACHE_TIME = settings.ASSETS_PERM_CACHE_TIME
|
||||||
|
_object = None
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 内部使用可控制缓存
|
||||||
|
def _get_object(self):
|
||||||
|
if not self._object:
|
||||||
|
self._object = self.get_object()
|
||||||
|
return self._object
|
||||||
|
|
||||||
|
def get_object_id(self):
|
||||||
|
obj = self._get_object()
|
||||||
|
if obj:
|
||||||
|
return str(obj.id)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_request_md5(self):
|
||||||
|
path = self.request.path
|
||||||
|
query = {k: v for k, v in self.request.GET.items()}
|
||||||
|
query.pop("_", None)
|
||||||
|
query = "&".join(["{}={}".format(k, v) for k, v in query.items()])
|
||||||
|
full_path = "{}?{}".format(path, query)
|
||||||
|
return md5(full_path.encode()).hexdigest()
|
||||||
|
|
||||||
|
def get_meta_cache_id(self):
|
||||||
|
obj = self._get_object()
|
||||||
|
util = AssetPermissionUtil(obj, cache_policy=self.cache_policy)
|
||||||
|
meta_cache_id = util.cache_meta.get('id')
|
||||||
|
return meta_cache_id
|
||||||
|
|
||||||
|
def get_response_cache_id(self):
|
||||||
|
obj_id = self.get_object_id()
|
||||||
|
request_md5 = self.get_request_md5()
|
||||||
|
meta_cache_id = self.get_meta_cache_id()
|
||||||
|
resp_cache_id = '{}_{}_{}'.format(obj_id, request_md5, meta_cache_id)
|
||||||
|
return resp_cache_id
|
||||||
|
|
||||||
|
def get_response_from_cache(self):
|
||||||
|
# 没有数据缓冲
|
||||||
|
meta_cache_id = self.get_meta_cache_id()
|
||||||
|
if not meta_cache_id:
|
||||||
|
logger.debug("Not get meta id: {}".format(meta_cache_id))
|
||||||
|
return None
|
||||||
|
# 从响应缓冲里获取响应
|
||||||
|
key = self.get_response_key()
|
||||||
|
data = cache.get(key)
|
||||||
|
if not data:
|
||||||
|
logger.debug("Not get response from cache: {}".format(key))
|
||||||
|
return None
|
||||||
|
logger.debug("Get user permission from cache: {}".format(self.get_object()))
|
||||||
|
response = Response(data)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def expire_response_cache(self):
|
||||||
|
obj_id = self.get_object_id()
|
||||||
|
expire_cache_id = '{}_{}'.format(obj_id, '*')
|
||||||
|
key = self.RESP_CACHE_KEY.format(expire_cache_id)
|
||||||
|
cache.delete_pattern(key)
|
||||||
|
|
||||||
|
def get_response_key(self):
|
||||||
|
resp_cache_id = self.get_response_cache_id()
|
||||||
|
key = self.RESP_CACHE_KEY.format(resp_cache_id)
|
||||||
|
return key
|
||||||
|
|
||||||
|
def set_response_to_cache(self, response):
|
||||||
|
key = self.get_response_key()
|
||||||
|
cache.set(key, response.data, self.CACHE_TIME)
|
||||||
|
logger.debug("Set response to cache: {}".format(key))
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self.cache_policy = request.GET.get('cache_policy', '0')
|
||||||
|
|
||||||
|
obj = self._get_object()
|
||||||
|
if obj is None:
|
||||||
|
logger.debug("Not get response from cache: obj is none")
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
if AssetPermissionUtil.is_not_using_cache(self.cache_policy):
|
||||||
|
logger.debug("Not get resp from cache: {}".format(self.cache_policy))
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
elif AssetPermissionUtil.is_refresh_cache(self.cache_policy):
|
||||||
|
logger.debug("Not get resp from cache: {}".format(self.cache_policy))
|
||||||
|
self.expire_response_cache()
|
||||||
|
|
||||||
|
logger.debug("Try get response from cache")
|
||||||
|
resp = self.get_response_from_cache()
|
||||||
|
if not resp:
|
||||||
|
resp = super().get(request, *args, **kwargs)
|
||||||
|
self.set_response_to_cache(resp)
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
class NodesWithUngroupMixin:
|
||||||
|
util = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_ungrouped_node(ungroup_key):
|
||||||
|
return Node(key=ungroup_key, id=const.UNGROUPED_NODE_ID,
|
||||||
|
value=_("ungrouped"))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_empty_node():
|
||||||
|
return Node(key=const.EMPTY_NODE_KEY, id=const.EMPTY_NODE_ID,
|
||||||
|
value=_("empty"))
|
||||||
|
|
||||||
|
def add_ungrouped_nodes(self, node_map, node_keys):
|
||||||
|
ungroup_key = '1:-1'
|
||||||
|
for key in node_keys:
|
||||||
|
if key.endswith('-1'):
|
||||||
|
ungroup_key = key
|
||||||
|
break
|
||||||
|
ungroup_node = self.get_ungrouped_node(ungroup_key)
|
||||||
|
empty_node = self.get_empty_node()
|
||||||
|
node_map[ungroup_key] = ungroup_node
|
||||||
|
node_map[const.EMPTY_NODE_KEY] = empty_node
|
||||||
|
|
||||||
|
|
||||||
|
class GrantAssetsMixin(LabelFilterMixin):
|
||||||
|
serializer_class = serializers.AssetGrantedSerializer
|
||||||
|
|
||||||
|
def get_serializer_queryset(self, queryset):
|
||||||
|
assets_ids = []
|
||||||
|
system_users_ids = set()
|
||||||
|
for asset in queryset:
|
||||||
|
assets_ids.append(asset["id"])
|
||||||
|
system_users_ids.update(set(asset["system_users"]))
|
||||||
|
assets = Asset.objects.filter(id__in=assets_ids).only(
|
||||||
|
*self.serializer_class.Meta.only_fields
|
||||||
|
)
|
||||||
|
assets_map = {asset.id: asset for asset in assets}
|
||||||
|
system_users = SystemUser.objects.filter(id__in=system_users_ids).only(
|
||||||
|
*self.serializer_class.system_users_only_fields
|
||||||
|
)
|
||||||
|
system_users_map = {s.id: s for s in system_users}
|
||||||
|
data = []
|
||||||
|
for item in queryset:
|
||||||
|
i = item["id"]
|
||||||
|
asset = assets_map.get(i)
|
||||||
|
if not asset:
|
||||||
|
continue
|
||||||
|
|
||||||
|
_system_users = item["system_users"]
|
||||||
|
system_users_granted = []
|
||||||
|
for sid, action in _system_users.items():
|
||||||
|
system_user = system_users_map.get(sid)
|
||||||
|
if not system_user:
|
||||||
|
continue
|
||||||
|
system_user.actions = action
|
||||||
|
system_users_granted.append(system_user)
|
||||||
|
asset.system_users_granted = system_users_granted
|
||||||
|
data.append(asset)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_serializer(self, queryset_list, many=True):
|
||||||
|
data = self.get_serializer_queryset(queryset_list)
|
||||||
|
return super().get_serializer(data, many=True)
|
||||||
|
|
||||||
|
def search_queryset(self, assets_items):
|
||||||
|
search = self.request.query_params.get("search")
|
||||||
|
if not search:
|
||||||
|
return assets_items
|
||||||
|
assets_map = {asset['id']: asset for asset in assets_items}
|
||||||
|
assets_ids = set(assets_map.keys())
|
||||||
|
assets_ids_search = Asset.objects.filter(id__in=assets_ids).filter(
|
||||||
|
Q(hostname__icontains=search) | Q(ip__icontains=search)
|
||||||
|
).values_list('id', flat=True)
|
||||||
|
return [assets_map.get(asset_id) for asset_id in assets_ids_search]
|
||||||
|
|
||||||
|
def filter_queryset_by_label(self, assets_items):
|
||||||
|
labels_id = self.get_filter_labels_ids()
|
||||||
|
if not labels_id:
|
||||||
|
return assets_items
|
||||||
|
|
||||||
|
assets_map = {asset['id']: asset for asset in assets_items}
|
||||||
|
assets_matched = Asset.objects.filter(id__in=assets_map.keys())
|
||||||
|
for label_id in labels_id:
|
||||||
|
assets_matched = assets_matched.filter(labels=label_id)
|
||||||
|
assets_ids_matched = assets_matched.values_list('id', flat=True)
|
||||||
|
return [assets_map.get(asset_id) for asset_id in assets_ids_matched]
|
||||||
|
|
||||||
|
def sort_queryset(self, assets_items):
|
||||||
|
order_by = self.request.query_params.get('order', 'hostname')
|
||||||
|
|
||||||
|
if order_by not in ['hostname', '-hostname', 'ip', '-ip']:
|
||||||
|
order_by = 'hostname'
|
||||||
|
assets_map = {asset['id']: asset for asset in assets_items}
|
||||||
|
assets_ids_search = Asset.objects.filter(id__in=assets_map.keys())\
|
||||||
|
.order_by(order_by)\
|
||||||
|
.values_list('id', flat=True)
|
||||||
|
return [assets_map.get(asset_id) for asset_id in assets_ids_search]
|
||||||
|
|
||||||
|
def filter_queryset(self, assets_items):
|
||||||
|
assets_items = self.search_queryset(assets_items)
|
||||||
|
assets_items = self.filter_queryset_by_label(assets_items)
|
||||||
|
assets_items = self.sort_queryset(assets_items)
|
||||||
|
return assets_items
|
|
@ -2,23 +2,21 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from rest_framework.generics import (
|
|
||||||
ListAPIView, get_object_or_404,
|
|
||||||
)
|
|
||||||
|
|
||||||
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
||||||
from ..hands import UserGroup
|
from ..hands import UserGroup
|
||||||
from .. import serializers, const
|
from .. import serializers
|
||||||
|
|
||||||
from .user_permission import (
|
from .user_permission import (
|
||||||
UserGrantedAssetsApi, UserGrantedNodesApi, UserGrantedNodesWithAssetsApi,
|
UserGrantedAssetsApi, UserGrantedNodesApi, UserGrantedNodesWithAssetsApi,
|
||||||
UserGrantedNodesWithAssetsAsTreeApi, UserGrantedNodeAssetsApi,
|
UserGrantedNodesWithAssetsAsTreeApi, UserGrantedNodeAssetsApi,
|
||||||
|
UserGrantedNodesAsTreeApi,
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'UserGroupGrantedAssetsApi', 'UserGroupGrantedNodesApi',
|
'UserGroupGrantedAssetsApi', 'UserGroupGrantedNodesApi',
|
||||||
'UserGroupGrantedNodesWithAssetsApi', 'UserGroupGrantedNodeAssetsApi',
|
'UserGroupGrantedNodesWithAssetsApi', 'UserGroupGrantedNodeAssetsApi',
|
||||||
'UserGroupGrantedNodesWithAssetsAsTreeApi',
|
'UserGroupGrantedNodesWithAssetsAsTreeApi', 'UserGroupGrantedNodesAsTreeApi',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,6 +34,13 @@ class UserGroupGrantedNodesApi(UserGrantedNodesApi):
|
||||||
return user_group
|
return user_group
|
||||||
|
|
||||||
|
|
||||||
|
class UserGroupGrantedNodesAsTreeApi(UserGrantedNodesAsTreeApi):
|
||||||
|
def get_object(self):
|
||||||
|
user_group_id = self.kwargs.get('pk', '')
|
||||||
|
user_group = get_object_or_404(UserGroup, id=user_group_id)
|
||||||
|
return user_group
|
||||||
|
|
||||||
|
|
||||||
class UserGroupGrantedNodesWithAssetsApi(UserGrantedNodesWithAssetsApi):
|
class UserGroupGrantedNodesWithAssetsApi(UserGrantedNodesWithAssetsApi):
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.NodeGrantedSerializer
|
serializer_class = serializers.NodeGrantedSerializer
|
||||||
|
|
|
@ -2,25 +2,23 @@
|
||||||
#
|
#
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
from functools import reduce
|
||||||
import uuid
|
import uuid
|
||||||
from hashlib import md5
|
|
||||||
from django.core.cache import cache
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from rest_framework.views import APIView, Response
|
from rest_framework.views import APIView, Response
|
||||||
from rest_framework.generics import (
|
from rest_framework.generics import (
|
||||||
ListAPIView, get_object_or_404, RetrieveAPIView
|
ListAPIView, get_object_or_404, RetrieveAPIView
|
||||||
)
|
)
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
from rest_framework.pagination import LimitOffsetPagination
|
from rest_framework.pagination import LimitOffsetPagination
|
||||||
|
|
||||||
from common.permissions import IsValidUser, IsOrgAdminOrAppUser
|
from common.permissions import IsValidUser, IsOrgAdminOrAppUser
|
||||||
from common.tree import TreeNodeSerializer
|
from common.tree import TreeNodeSerializer
|
||||||
from common.utils import get_logger, get_object_or_none
|
from common.utils import get_logger
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
AssetPermissionUtil, ParserNode,
|
AssetPermissionUtil, ParserNode,
|
||||||
)
|
)
|
||||||
|
from .mixin import UserPermissionCacheMixin, GrantAssetsMixin, NodesWithUngroupMixin
|
||||||
from .. import const
|
from .. import const
|
||||||
from ..hands import User, Asset, Node, SystemUser, NodeSerializer
|
from ..hands import User, Asset, Node, SystemUser, NodeSerializer
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
|
@ -37,153 +35,6 @@ __all__ = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class UserPermissionCacheMixin:
|
|
||||||
cache_policy = '0'
|
|
||||||
RESP_CACHE_KEY = '_PERMISSION_RESPONSE_CACHE_V2_{}'
|
|
||||||
CACHE_TIME = settings.ASSETS_PERM_CACHE_TIME
|
|
||||||
_object = None
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
return None
|
|
||||||
|
|
||||||
# 内部使用可控制缓存
|
|
||||||
def _get_object(self):
|
|
||||||
if not self._object:
|
|
||||||
self._object = self.get_object()
|
|
||||||
return self._object
|
|
||||||
|
|
||||||
def get_object_id(self):
|
|
||||||
obj = self._get_object()
|
|
||||||
if obj:
|
|
||||||
return str(obj.id)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_request_md5(self):
|
|
||||||
path = self.request.path
|
|
||||||
query = {k: v for k, v in self.request.GET.items()}
|
|
||||||
query.pop("_", None)
|
|
||||||
query = "&".join(["{}={}".format(k, v) for k, v in query.items()])
|
|
||||||
full_path = "{}?{}".format(path, query)
|
|
||||||
return md5(full_path.encode()).hexdigest()
|
|
||||||
|
|
||||||
def get_meta_cache_id(self):
|
|
||||||
obj = self._get_object()
|
|
||||||
util = AssetPermissionUtil(obj, cache_policy=self.cache_policy)
|
|
||||||
meta_cache_id = util.cache_meta.get('id')
|
|
||||||
return meta_cache_id
|
|
||||||
|
|
||||||
def get_response_cache_id(self):
|
|
||||||
obj_id = self.get_object_id()
|
|
||||||
request_md5 = self.get_request_md5()
|
|
||||||
meta_cache_id = self.get_meta_cache_id()
|
|
||||||
resp_cache_id = '{}_{}_{}'.format(obj_id, request_md5, meta_cache_id)
|
|
||||||
return resp_cache_id
|
|
||||||
|
|
||||||
def get_response_from_cache(self):
|
|
||||||
# 没有数据缓冲
|
|
||||||
meta_cache_id = self.get_meta_cache_id()
|
|
||||||
if not meta_cache_id:
|
|
||||||
logger.debug("Not get meta id: {}".format(meta_cache_id))
|
|
||||||
return None
|
|
||||||
# 从响应缓冲里获取响应
|
|
||||||
key = self.get_response_key()
|
|
||||||
data = cache.get(key)
|
|
||||||
if not data:
|
|
||||||
logger.debug("Not get response from cache: {}".format(key))
|
|
||||||
return None
|
|
||||||
logger.debug("Get user permission from cache: {}".format(self.get_object()))
|
|
||||||
response = Response(data)
|
|
||||||
return response
|
|
||||||
|
|
||||||
def expire_response_cache(self):
|
|
||||||
obj_id = self.get_object_id()
|
|
||||||
expire_cache_id = '{}_{}'.format(obj_id, '*')
|
|
||||||
key = self.RESP_CACHE_KEY.format(expire_cache_id)
|
|
||||||
cache.delete_pattern(key)
|
|
||||||
|
|
||||||
def get_response_key(self):
|
|
||||||
resp_cache_id = self.get_response_cache_id()
|
|
||||||
key = self.RESP_CACHE_KEY.format(resp_cache_id)
|
|
||||||
return key
|
|
||||||
|
|
||||||
def set_response_to_cache(self, response):
|
|
||||||
key = self.get_response_key()
|
|
||||||
cache.set(key, response.data, self.CACHE_TIME)
|
|
||||||
logger.debug("Set response to cache: {}".format(key))
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
self.cache_policy = request.GET.get('cache_policy', '0')
|
|
||||||
|
|
||||||
obj = self._get_object()
|
|
||||||
if obj is None:
|
|
||||||
logger.debug("Not get response from cache: obj is none")
|
|
||||||
return super().get(request, *args, **kwargs)
|
|
||||||
|
|
||||||
if AssetPermissionUtil.is_not_using_cache(self.cache_policy):
|
|
||||||
logger.debug("Not get resp from cache: {}".format(self.cache_policy))
|
|
||||||
return super().get(request, *args, **kwargs)
|
|
||||||
elif AssetPermissionUtil.is_refresh_cache(self.cache_policy):
|
|
||||||
logger.debug("Not get resp from cache: {}".format(self.cache_policy))
|
|
||||||
self.expire_response_cache()
|
|
||||||
|
|
||||||
logger.debug("Try get response from cache")
|
|
||||||
resp = self.get_response_from_cache()
|
|
||||||
if not resp:
|
|
||||||
resp = super().get(request, *args, **kwargs)
|
|
||||||
self.set_response_to_cache(resp)
|
|
||||||
return resp
|
|
||||||
|
|
||||||
|
|
||||||
class GrantAssetsMixin:
|
|
||||||
serializer_class = serializers.AssetGrantedSerializer
|
|
||||||
|
|
||||||
def get_serializer(self, queryset, many=True):
|
|
||||||
assets_ids = []
|
|
||||||
system_users_ids = set()
|
|
||||||
for asset in queryset:
|
|
||||||
assets_ids.append(asset["id"])
|
|
||||||
system_users_ids.update(set(asset["system_users"]))
|
|
||||||
assets = Asset.objects.filter(id__in=assets_ids).only(
|
|
||||||
*self.serializer_class.Meta.only_fields
|
|
||||||
)
|
|
||||||
assets_map = {asset.id: asset for asset in assets}
|
|
||||||
system_users = SystemUser.objects.filter(id__in=system_users_ids).only(
|
|
||||||
*self.serializer_class.system_users_only_fields
|
|
||||||
)
|
|
||||||
system_users_map = {s.id: s for s in system_users}
|
|
||||||
data = []
|
|
||||||
for item in queryset:
|
|
||||||
i = item["id"]
|
|
||||||
asset = assets_map.get(i)
|
|
||||||
if not asset:
|
|
||||||
continue
|
|
||||||
|
|
||||||
_system_users = item["system_users"]
|
|
||||||
system_users_granted = []
|
|
||||||
for sid, action in _system_users.items():
|
|
||||||
system_user = system_users_map.get(sid)
|
|
||||||
if not system_user:
|
|
||||||
continue
|
|
||||||
system_user.actions = action
|
|
||||||
system_users_granted.append(system_user)
|
|
||||||
asset.system_users_granted = system_users_granted
|
|
||||||
data.append(asset)
|
|
||||||
return super().get_serializer(data, many=True)
|
|
||||||
|
|
||||||
def search_queryset(self, assets):
|
|
||||||
search = self.request.query_params.get("search")
|
|
||||||
if not search:
|
|
||||||
return assets
|
|
||||||
|
|
||||||
assets_map = {asset['id']: asset for asset in assets}
|
|
||||||
assets_ids = set(assets_map.keys())
|
|
||||||
assets_ids_search = Asset.objects.filter(id__in=assets_ids).filter(
|
|
||||||
Q(hostname__icontains=search) | Q(ip__icontains=search)
|
|
||||||
).values_list('id', flat=True)
|
|
||||||
assets_ids &= set(assets_ids_search)
|
|
||||||
return [assets_map.get(asset_id) for asset_id in assets_ids]
|
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedAssetsApi(UserPermissionCacheMixin, GrantAssetsMixin, ListAPIView):
|
class UserGrantedAssetsApi(UserPermissionCacheMixin, GrantAssetsMixin, ListAPIView):
|
||||||
"""
|
"""
|
||||||
用户授权的所有资产
|
用户授权的所有资产
|
||||||
|
@ -203,7 +54,6 @@ class UserGrantedAssetsApi(UserPermissionCacheMixin, GrantAssetsMixin, ListAPIVi
|
||||||
user = self.get_object()
|
user = self.get_object()
|
||||||
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
||||||
queryset = util.get_assets()
|
queryset = util.get_assets()
|
||||||
queryset = self.search_queryset(queryset)
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def get_permissions(self):
|
def get_permissions(self):
|
||||||
|
@ -212,29 +62,52 @@ class UserGrantedAssetsApi(UserPermissionCacheMixin, GrantAssetsMixin, ListAPIVi
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
class NodesWithUngroupMixin:
|
class UserGrantedNodeAssetsApi(UserPermissionCacheMixin, GrantAssetsMixin, ListAPIView):
|
||||||
util = None
|
"""
|
||||||
|
查询用户授权的节点下的资产的api, 与上面api不同的是,只返回某个节点下的资产
|
||||||
|
"""
|
||||||
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
@staticmethod
|
def get_object(self):
|
||||||
def get_ungrouped_node(ungroup_key):
|
user_id = self.kwargs.get('pk', '')
|
||||||
return Node(key=ungroup_key, id=const.UNGROUPED_NODE_ID,
|
|
||||||
value=_("ungrouped"))
|
|
||||||
|
|
||||||
@staticmethod
|
if user_id:
|
||||||
def get_empty_node():
|
user = get_object_or_404(User, id=user_id)
|
||||||
return Node(key=const.EMPTY_NODE_KEY, id=const.EMPTY_NODE_ID,
|
else:
|
||||||
value=_("empty"))
|
user = self.request.user
|
||||||
|
return user
|
||||||
|
|
||||||
def add_ungrouped_nodes(self, node_map, node_keys):
|
def get_node_key(self):
|
||||||
ungroup_key = '1:-1'
|
node_id = self.kwargs.get('node_id')
|
||||||
for key in node_keys:
|
if str(node_id) == const.UNGROUPED_NODE_ID:
|
||||||
if key.endswith('-1'):
|
key = self.util.tree.ungrouped_key
|
||||||
ungroup_key = key
|
elif str(node_id) == const.EMPTY_NODE_ID:
|
||||||
|
key = const.EMPTY_NODE_KEY
|
||||||
|
else:
|
||||||
|
node = get_object_or_404(Node, id=node_id)
|
||||||
|
key = node.key
|
||||||
|
return key
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
user = self.get_object()
|
||||||
|
self.util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
||||||
|
key = self.get_node_key()
|
||||||
|
nodes_items = self.util.get_nodes_with_assets()
|
||||||
|
assets_system_users = {}
|
||||||
|
for item in nodes_items:
|
||||||
|
if item["key"] == key:
|
||||||
|
assets_system_users = item["assets"]
|
||||||
break
|
break
|
||||||
ungroup_node = self.get_ungrouped_node(ungroup_key)
|
assets = []
|
||||||
empty_node = self.get_empty_node()
|
for asset_id, system_users in assets_system_users.items():
|
||||||
node_map[ungroup_key] = ungroup_node
|
assets.append({"id": asset_id, "system_users": system_users})
|
||||||
node_map[const.EMPTY_NODE_KEY] = empty_node
|
return assets
|
||||||
|
|
||||||
|
def get_permissions(self):
|
||||||
|
if self.kwargs.get('pk') is None:
|
||||||
|
self.permission_classes = (IsValidUser,)
|
||||||
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedNodesApi(UserPermissionCacheMixin, NodesWithUngroupMixin, ListAPIView):
|
class UserGrantedNodesApi(UserPermissionCacheMixin, NodesWithUngroupMixin, ListAPIView):
|
||||||
|
@ -435,55 +308,6 @@ class UserGrantedNodesWithAssetsAsTreeApi(UserGrantedNodesWithAssetsApi):
|
||||||
return self.serializer_class(queryset, many=True)
|
return self.serializer_class(queryset, many=True)
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedNodeAssetsApi(UserPermissionCacheMixin, GrantAssetsMixin, ListAPIView):
|
|
||||||
"""
|
|
||||||
查询用户授权的节点下的资产的api, 与上面api不同的是,只返回某个节点下的资产
|
|
||||||
"""
|
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
|
||||||
pagination_class = LimitOffsetPagination
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
user_id = self.kwargs.get('pk', '')
|
|
||||||
|
|
||||||
if user_id:
|
|
||||||
user = get_object_or_404(User, id=user_id)
|
|
||||||
else:
|
|
||||||
user = self.request.user
|
|
||||||
return user
|
|
||||||
|
|
||||||
def get_node_key(self):
|
|
||||||
node_id = self.kwargs.get('node_id')
|
|
||||||
if str(node_id) == const.UNGROUPED_NODE_ID:
|
|
||||||
key = self.util.tree.ungrouped_key
|
|
||||||
elif str(node_id) == const.EMPTY_NODE_ID:
|
|
||||||
key = const.EMPTY_NODE_KEY
|
|
||||||
else:
|
|
||||||
node = get_object_or_404(Node, id=node_id)
|
|
||||||
key = node.key
|
|
||||||
return key
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
user = self.get_object()
|
|
||||||
self.util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
|
||||||
key = self.get_node_key()
|
|
||||||
nodes_items = self.util.get_nodes_with_assets()
|
|
||||||
assets_system_users = {}
|
|
||||||
for item in nodes_items:
|
|
||||||
if item["key"] == key:
|
|
||||||
assets_system_users = item["assets"]
|
|
||||||
break
|
|
||||||
assets = []
|
|
||||||
for asset_id, system_users in assets_system_users.items():
|
|
||||||
assets.append({"id": asset_id, "system_users": system_users})
|
|
||||||
assets = self.search_queryset(assets)
|
|
||||||
return assets
|
|
||||||
|
|
||||||
def get_permissions(self):
|
|
||||||
if self.kwargs.get('pk') is None:
|
|
||||||
self.permission_classes = (IsValidUser,)
|
|
||||||
return super().get_permissions()
|
|
||||||
|
|
||||||
|
|
||||||
class ValidateUserAssetPermissionApi(UserPermissionCacheMixin, APIView):
|
class ValidateUserAssetPermissionApi(UserPermissionCacheMixin, APIView):
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
|
||||||
|
@ -522,16 +346,12 @@ class GetUserAssetPermissionActionsApi(UserPermissionCacheMixin, RetrieveAPIView
|
||||||
system_id = self.request.query_params.get('system_user_id', '')
|
system_id = self.request.query_params.get('system_user_id', '')
|
||||||
|
|
||||||
user = get_object_or_404(User, id=user_id)
|
user = get_object_or_404(User, id=user_id)
|
||||||
asset = get_object_or_404(Asset, id=asset_id)
|
|
||||||
su = get_object_or_404(SystemUser, id=system_id)
|
|
||||||
|
|
||||||
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
|
||||||
granted_assets = util.get_assets()
|
assets = util.get_assets()
|
||||||
granted_system_users = granted_assets.get(asset, {})
|
actions = 0
|
||||||
|
for asset in assets:
|
||||||
_object = {}
|
if asset_id == asset["id"]:
|
||||||
if su not in granted_system_users:
|
actions = asset["system_users"].get(system_id, 0)
|
||||||
_object['actions'] = 0
|
break
|
||||||
else:
|
return {"actions": actions}
|
||||||
_object['actions'] = granted_system_users[su]
|
|
||||||
return _object
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from users.models import User, UserGroup
|
from users.models import User, UserGroup
|
||||||
from assets.models import Asset, SystemUser, Node
|
from assets.models import Asset, SystemUser, Node, Label
|
||||||
from assets.serializers import NodeSerializer
|
from assets.serializers import NodeSerializer
|
||||||
from applications.serializers import RemoteAppSerializer
|
from applications.serializers import RemoteAppSerializer
|
||||||
from applications.models import RemoteApp
|
from applications.models import RemoteApp
|
||||||
|
|
|
@ -145,7 +145,7 @@ function addAssets(assets) {
|
||||||
var success = function(data) {
|
var success = function(data) {
|
||||||
location.reload();
|
location.reload();
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -160,7 +160,7 @@ function removeAssets(assets) {
|
||||||
var success = function(data) {
|
var success = function(data) {
|
||||||
location.reload();
|
location.reload();
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -172,7 +172,7 @@ function updateNodes(nodes, success) {
|
||||||
var body = {
|
var body = {
|
||||||
nodes: nodes
|
nodes: nodes
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
|
|
@ -187,7 +187,7 @@ function updateSystemUser(system_users) {
|
||||||
var body = {
|
var body = {
|
||||||
system_users: Object.assign([], system_users)
|
system_users: Object.assign([], system_users)
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body)
|
body: JSON.stringify(body)
|
||||||
});
|
});
|
||||||
|
@ -247,7 +247,7 @@ $(document).ready(function () {
|
||||||
var body = {
|
var body = {
|
||||||
'is_active': checked
|
'is_active': checked
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
});
|
});
|
||||||
|
|
|
@ -160,7 +160,7 @@ function addUsers(users) {
|
||||||
var success = function(data) {
|
var success = function(data) {
|
||||||
location.reload();
|
location.reload();
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -175,7 +175,7 @@ function removeUser(users) {
|
||||||
var success = function(data) {
|
var success = function(data) {
|
||||||
location.reload();
|
location.reload();
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -187,7 +187,7 @@ function updateGroup(groups) {
|
||||||
var body = {
|
var body = {
|
||||||
user_groups: groups
|
user_groups: groups
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body)
|
body: JSON.stringify(body)
|
||||||
});
|
});
|
||||||
|
|
|
@ -160,7 +160,7 @@ $(document).ready(function () {
|
||||||
var body = {
|
var body = {
|
||||||
'is_active': checked
|
'is_active': checked
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body)
|
body: JSON.stringify(body)
|
||||||
});
|
});
|
||||||
|
|
|
@ -120,7 +120,7 @@
|
||||||
var success = function(data) {
|
var success = function(data) {
|
||||||
location.reload();
|
location.reload();
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -134,7 +134,7 @@
|
||||||
var success = function(data) {
|
var success = function(data) {
|
||||||
location.reload();
|
location.reload();
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
|
|
@ -158,7 +158,7 @@
|
||||||
var success = function(data) {
|
var success = function(data) {
|
||||||
location.reload();
|
location.reload();
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -172,7 +172,7 @@
|
||||||
var success = function(data) {
|
var success = function(data) {
|
||||||
location.reload();
|
location.reload();
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -183,7 +183,7 @@
|
||||||
var body = {
|
var body = {
|
||||||
user_groups: groups
|
user_groups: groups
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body)
|
body: JSON.stringify(body)
|
||||||
});
|
});
|
||||||
|
|
|
@ -39,9 +39,10 @@ asset_permission_urlpatterns = [
|
||||||
|
|
||||||
# 查询某个用户组授权的资产和资产组
|
# 查询某个用户组授权的资产和资产组
|
||||||
path('user-groups/<uuid:pk>/assets/', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'),
|
path('user-groups/<uuid:pk>/assets/', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'),
|
||||||
path('user-groups/<uuid:pk>/nodes/', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'),
|
path('user-groups/<uuid:pk>/nodes/tree/', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'),
|
||||||
path('user-groups/<uuid:pk>/nodes-assets/', api.UserGroupGrantedNodesWithAssetsApi.as_view(), name='user-group-nodes-assets'),
|
path('user-groups/<uuid:pk>/nodes/', api.UserGroupGrantedNodesAsTreeApi.as_view(), name='user-group-nodes-as-tree'),
|
||||||
path('user-groups/<uuid:pk>/nodes-assets/tree/', api.UserGroupGrantedNodesWithAssetsAsTreeApi.as_view(), name='user-group-nodes-assets-as-tree'),
|
path('user-groups/<uuid:pk>/nodes-assets/tree/', api.UserGroupGrantedNodesWithAssetsAsTreeApi.as_view(), name='user-group-nodes-assets-as-tree'),
|
||||||
|
path('user-groups/<uuid:pk>/nodes-assets/', api.UserGroupGrantedNodesWithAssetsApi.as_view(), name='user-group-nodes-assets'),
|
||||||
path('user-groups/<uuid:pk>/nodes/<uuid:node_id>/assets/', api.UserGroupGrantedNodeAssetsApi.as_view(), name='user-group-node-assets'),
|
path('user-groups/<uuid:pk>/nodes/<uuid:node_id>/assets/', api.UserGroupGrantedNodeAssetsApi.as_view(), name='user-group-node-assets'),
|
||||||
|
|
||||||
# 用户和资产授权变更
|
# 用户和资产授权变更
|
||||||
|
|
|
@ -96,7 +96,7 @@ $(document).ready(function () {
|
||||||
function success(message) {
|
function success(message) {
|
||||||
toastr.success(message.msg)
|
toastr.success(message.msg)
|
||||||
}
|
}
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|
|
@ -100,7 +100,7 @@ $(document).ready(function () {
|
||||||
function success(message) {
|
function success(message) {
|
||||||
toastr.success(message.msg)
|
toastr.success(message.msg)
|
||||||
}
|
}
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -127,7 +127,7 @@ $(document).ready(function () {
|
||||||
function success(message) {
|
function success(message) {
|
||||||
toastr.success(message.msg)
|
toastr.success(message.msg)
|
||||||
}
|
}
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify({'username_list':username_list}),
|
body: JSON.stringify({'username_list':username_list}),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|
|
@ -256,7 +256,7 @@ function formSubmit(props) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function APIUpdateAttr(props) {
|
function requestApi(props) {
|
||||||
// props = {url: .., body: , success: , error: , method: ,}
|
// props = {url: .., body: , success: , error: , method: ,}
|
||||||
props = props || {};
|
props = props || {};
|
||||||
var user_success_message = props.success_message;
|
var user_success_message = props.success_message;
|
||||||
|
@ -328,7 +328,7 @@ function objectDelete(obj, name, url, redirectTo) {
|
||||||
// swal("错误", "删除"+"[ "+name+" ]"+"遇到错误", "error");
|
// swal("错误", "删除"+"[ "+name+" ]"+"遇到错误", "error");
|
||||||
swal(gettext('Error'), "[ "+name+" ]" + gettext("Being used by the asset, please unbind the asset first."), "error");
|
swal(gettext('Error'), "[ "+name+" ]" + gettext("Being used by the asset, please unbind the asset first."), "error");
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: url,
|
url: url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
|
@ -369,7 +369,7 @@ function orgDelete(obj, name, url, redirectTo){
|
||||||
swal(gettext("Error"), " [ "+ name + " ] " + gettext("Do not perform this operation under this organization. Try again after switching to another organization"), "error");
|
swal(gettext("Error"), " [ "+ name + " ] " + gettext("Do not perform this operation under this organization. Try again after switching to another organization"), "error");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: url,
|
url: url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
|
@ -1109,7 +1109,19 @@ function objectAttrsIsBool(obj, attrs) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cleanDate(d) {
|
||||||
|
for (var i=0; i<2; i++) {
|
||||||
|
if (isNaN(Date.parse(d))) {
|
||||||
|
d = d.split('+')[0].trimRight();
|
||||||
|
} else {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
function formatDateAsCN(d) {
|
function formatDateAsCN(d) {
|
||||||
|
d = cleanDate(d);
|
||||||
var date = new Date(d);
|
var date = new Date(d);
|
||||||
var date_s = date.toLocaleString(navigator.language, {hour12: false});
|
var date_s = date.toLocaleString(navigator.language, {hour12: false});
|
||||||
return date_s.split("/").join('-')
|
return date_s.split("/").join('-')
|
||||||
|
@ -1138,6 +1150,8 @@ function getTimeUnits(u) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function timeOffset(a, b) {
|
function timeOffset(a, b) {
|
||||||
|
a = cleanDate(a);
|
||||||
|
b = cleanDate(b);
|
||||||
var start = new Date(a);
|
var start = new Date(a);
|
||||||
var end = new Date(b);
|
var end = new Date(b);
|
||||||
var offset = (end - start)/1000;
|
var offset = (end - start)/1000;
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -132,7 +132,7 @@
|
||||||
}, 300)
|
}, 300)
|
||||||
}
|
}
|
||||||
var the_url = "{% url 'api-terminal:tasks-list' %}";
|
var the_url = "{% url 'api-terminal:tasks-list' %}";
|
||||||
APIUpdateAttr({url: the_url, method: 'POST', body: JSON.stringify(data), success: success, success_message: 'Terminate success'});
|
requestApi({url: the_url, method: 'POST', body: JSON.stringify(data), success: success, success_message: 'Terminate success'});
|
||||||
}
|
}
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('.footable').footable();
|
$('.footable').footable();
|
||||||
|
|
|
@ -90,7 +90,7 @@ function terminateSession(data) {
|
||||||
}
|
}
|
||||||
var success_message = '{% trans "Terminate task send, waiting ..." %}';
|
var success_message = '{% trans "Terminate task send, waiting ..." %}';
|
||||||
var the_url = "{% url 'api-terminal:kill-session' %}";
|
var the_url = "{% url 'api-terminal:kill-session' %}";
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
|
@ -174,7 +174,7 @@ function finishedSession(data) {
|
||||||
var success = function() {
|
var success = function() {
|
||||||
location.reload();
|
location.reload();
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
|
|
|
@ -14,7 +14,7 @@ from rest_framework.pagination import LimitOffsetPagination
|
||||||
|
|
||||||
from common.permissions import (
|
from common.permissions import (
|
||||||
IsOrgAdmin, IsCurrentUserOrReadOnly, IsOrgAdminOrAppUser,
|
IsOrgAdmin, IsCurrentUserOrReadOnly, IsOrgAdminOrAppUser,
|
||||||
CanUpdateSuperUser,
|
CanUpdateDeleteSuperUser,
|
||||||
)
|
)
|
||||||
from common.mixins import IDInCacheFilterMixin
|
from common.mixins import IDInCacheFilterMixin
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
@ -38,7 +38,7 @@ class UserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
queryset = User.objects.exclude(role=User.ROLE_APP)
|
queryset = User.objects.exclude(role=User.ROLE_APP)
|
||||||
serializer_class = UserSerializer
|
serializer_class = UserSerializer
|
||||||
permission_classes = (IsOrgAdmin, CanUpdateSuperUser)
|
permission_classes = (IsOrgAdmin, CanUpdateDeleteSuperUser)
|
||||||
pagination_class = LimitOffsetPagination
|
pagination_class = LimitOffsetPagination
|
||||||
|
|
||||||
def send_created_signal(self, users):
|
def send_created_signal(self, users):
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
{% load i18n %}
|
||||||
|
<div class="col-lg-3" style="padding-left: 0px">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-content mailbox-content" style="padding-top: 0">
|
||||||
|
<div class="file-manager ">
|
||||||
|
<div id="assetTree" class="ztree">
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-9 animated fadeInRight">
|
||||||
|
<div class="mail-box-header">
|
||||||
|
<div class="btn-group" style="float: right">
|
||||||
|
<button data-toggle="dropdown" class="btn btn-default btn-sm labels dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>
|
||||||
|
<ul class="dropdown-menu labels-menu">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-bordered table-hover" id="user_assets_table" >
|
||||||
|
<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 'System user' %}</th>
|
||||||
|
{% if show_actions %}
|
||||||
|
<th class="text-center">{% trans 'Action' %}</th>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
var zTree;
|
||||||
|
var inited = false;
|
||||||
|
var url;
|
||||||
|
var assetTable;
|
||||||
|
var treeUrl = "NeedInput";
|
||||||
|
var assetTableUrl = 'NeedInput';
|
||||||
|
var selectUrl = 'NeedInput';
|
||||||
|
var showAssetHref = true; // Need input default true
|
||||||
|
var actions = {};
|
||||||
|
var labels = '';
|
||||||
|
var requesting = false;
|
||||||
|
|
||||||
|
function initTable() {
|
||||||
|
if (inited){
|
||||||
|
return assetTable
|
||||||
|
} else {
|
||||||
|
inited = true;
|
||||||
|
}
|
||||||
|
var options = {
|
||||||
|
ele: $('#user_assets_table'),
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||||
|
cellData = htmlEscape(cellData);
|
||||||
|
var assetDetailUrl = '{% url 'assets:asset-detail' pk=DEFAULT_PK %}'
|
||||||
|
.replace("{{ DEFAULT_PK }}", rowData.id);
|
||||||
|
var detailBtn = '<a href="assetDetailUrl" class="asset-detail" data-asset="assetId">' + cellData + '</a>';
|
||||||
|
if (showAssetHref) {
|
||||||
|
cellData = detailBtn.replace("assetDetailUrl", assetDetailUrl);
|
||||||
|
} else {
|
||||||
|
detailBtn = detailBtn.replace("assetId", rowData.id);
|
||||||
|
cellData = detailBtn.replace("assetDetailUrl", "");
|
||||||
|
}
|
||||||
|
$(td).html(cellData);
|
||||||
|
}},
|
||||||
|
{targets: 3, createdCell: function (td, cellData) {
|
||||||
|
var users = [];
|
||||||
|
$.each(cellData, function (id, data) {
|
||||||
|
var name = htmlEscape(data.name);
|
||||||
|
users.push(name);
|
||||||
|
});
|
||||||
|
$(td).html(users.join(', '))
|
||||||
|
}},
|
||||||
|
],
|
||||||
|
ajax_url: assetTableUrl,
|
||||||
|
columns: [
|
||||||
|
{data: "id"}, {data: "hostname" }, {data: "ip" },
|
||||||
|
{data: "system_users_granted", orderable: false},
|
||||||
|
{% if show_actions %}
|
||||||
|
{data: "id", orderable: false}
|
||||||
|
{% endif %}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
{% if show_actions %}
|
||||||
|
options.columnDefs.push(actions);
|
||||||
|
{% endif %}
|
||||||
|
assetTable = jumpserver.initServerSideDataTable(options);
|
||||||
|
return assetTable
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSelected(event, treeNode) {
|
||||||
|
var node_id = treeNode.meta.node.id;
|
||||||
|
url = selectUrl.replace("{{ DEFAULT_PK }}", node_id);
|
||||||
|
assetTable.ajax.url(url);
|
||||||
|
assetTable.ajax.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function initTree() {
|
||||||
|
var setting = {
|
||||||
|
view: {
|
||||||
|
dblClickExpand: false,
|
||||||
|
showLine: true
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
simpleData: {
|
||||||
|
enable: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
callback: {
|
||||||
|
onSelected: onSelected
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$.get(treeUrl, function(data, status) {
|
||||||
|
$.fn.zTree.init($("#assetTree"), setting, data);
|
||||||
|
zTree = $.fn.zTree.getZTreeObj("assetTree");
|
||||||
|
rootNodeAddDom(zTree, function () {
|
||||||
|
treeUrl = treeUrl.replace('cache_policy=1', 'cache_policy=2');
|
||||||
|
initTree();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLabels() {
|
||||||
|
var labelListUrl = '{% url "api-assets:label-list" %}';
|
||||||
|
var label = '<li><a style="font-weight: bolder">labelName:labelValue</a></li>';
|
||||||
|
if (requesting) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!labels) {
|
||||||
|
var data = {
|
||||||
|
url: labelListUrl,
|
||||||
|
method: "GET",
|
||||||
|
success: function (data) {
|
||||||
|
data.forEach(function (value) {
|
||||||
|
labels += label.replace("labelName", value.name).replace("labelValue", value.value)
|
||||||
|
});
|
||||||
|
$(".labels-menu").append(labels);
|
||||||
|
requesting = false;
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
requesting = false;
|
||||||
|
},
|
||||||
|
flash_message: false
|
||||||
|
};
|
||||||
|
requesting = true;
|
||||||
|
requestApi(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
loadLabels()
|
||||||
|
}).on('click', '.labels-menu li', function () {
|
||||||
|
var val = $(this).text();
|
||||||
|
$("#user_assets_table_filter input").val(val);
|
||||||
|
assetTable.search(val).draw();
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -280,7 +280,7 @@ function updateUserGroups(groups) {
|
||||||
// clear jumpserver.groups_selected
|
// clear jumpserver.groups_selected
|
||||||
jumpserver.nodes_selected = {};
|
jumpserver.nodes_selected = {};
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -305,7 +305,7 @@ $(document).ready(function() {
|
||||||
'is_active': checked
|
'is_active': checked
|
||||||
};
|
};
|
||||||
var success = '{% trans "Update successfully!" %}';
|
var success = '{% trans "Update successfully!" %}';
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success_message: success
|
success_message: success
|
||||||
|
@ -332,7 +332,7 @@ $(document).ready(function() {
|
||||||
'otp_secret_key': otp_secret_key
|
'otp_secret_key': otp_secret_key
|
||||||
};
|
};
|
||||||
var success = '{% trans "Update successfully!" %}';
|
var success = '{% trans "Update successfully!" %}';
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success_message: success
|
success_message: success
|
||||||
|
@ -372,7 +372,7 @@ $(document).ready(function() {
|
||||||
var msg = "{% trans "An e-mail has been sent to the user`s mailbox." %}";
|
var msg = "{% trans "An e-mail has been sent to the user`s mailbox." %}";
|
||||||
swal("{% trans 'Reset password' %}", msg, "success");
|
swal("{% trans 'Reset password' %}", msg, "success");
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -398,7 +398,7 @@ $(document).ready(function() {
|
||||||
var msg = "{% trans 'The reset-ssh-public-key E-mail has been sent successfully. Please inform the user to update his new ssh public key.' %}";
|
var msg = "{% trans 'The reset-ssh-public-key E-mail has been sent successfully. Please inform the user to update his new ssh public key.' %}";
|
||||||
swal("{% trans 'Reset SSH public key' %}", msg, "success");
|
swal("{% trans 'Reset SSH public key' %}", msg, "success");
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: body,
|
body: body,
|
||||||
success: success
|
success: success
|
||||||
|
@ -441,7 +441,7 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, error: fail});
|
requestApi({ url: the_url, body: JSON.stringify(body), success: success, error: fail});
|
||||||
}).on('click', '.btn-delete-user', function () {
|
}).on('click', '.btn-delete-user', function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var name = "{{ user_object.name }}";
|
var name = "{{ user_object.name }}";
|
||||||
|
@ -466,7 +466,7 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
@ -485,7 +485,7 @@ $(document).ready(function() {
|
||||||
doReset();
|
doReset();
|
||||||
});
|
});
|
||||||
}).on('click', '#btn-reset-mfa', function () {
|
}).on('click', '#btn-reset-mfa', function () {
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: "{% url 'api-users:user-reset-otp' pk=user_object.id %}",
|
url: "{% url 'api-users:user-reset-otp' pk=user_object.id %}",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
success_message: "{% trans 'Reset user MFA success' %}"
|
success_message: "{% trans 'Reset user MFA success' %}"
|
||||||
|
|
|
@ -23,33 +23,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="col-lg-3" style="padding-left: 0px">
|
{% include 'users/_granted_assets.html' %}
|
||||||
<div class="ibox float-e-margins">
|
|
||||||
<div class="ibox-content mailbox-content" style="padding-top: 0">
|
|
||||||
<div class="file-manager ">
|
|
||||||
<div id="assetTree" class="ztree">
|
|
||||||
</div>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-9 animated fadeInRight">
|
|
||||||
<div class="mail-box-header">
|
|
||||||
<table class="table table-striped table-bordered table-hover" id="user_assets_table" >
|
|
||||||
<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 'System users' %}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,81 +32,10 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
var zTree;
|
var assetTableUrl = "{% url 'api-perms:user-assets' pk=object.id %}?cache_policy=1";
|
||||||
var inited = false;
|
var selectUrl = '{% url "api-perms:user-node-assets" pk=object.id node_id=DEFAULT_PK %}?cache_policy=1';
|
||||||
var url;
|
|
||||||
var asset_table;
|
|
||||||
var treeUrl = "{% url 'api-perms:user-nodes-as-tree' pk=object.id %}?&cache_policy=1";
|
var treeUrl = "{% url 'api-perms:user-nodes-as-tree' pk=object.id %}?&cache_policy=1";
|
||||||
|
|
||||||
function initTable() {
|
|
||||||
if (inited){
|
|
||||||
return asset_table
|
|
||||||
} else {
|
|
||||||
inited = true;
|
|
||||||
}
|
|
||||||
url = "{% url 'api-perms:user-assets' pk=object.id %}?cache_policy=1";
|
|
||||||
var options = {
|
|
||||||
ele: $('#user_assets_table'),
|
|
||||||
columnDefs: [
|
|
||||||
{targets: 1, createdCell: function (td, cellData, rowData) {
|
|
||||||
cellData = htmlEscape(cellData);
|
|
||||||
{% 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) {
|
|
||||||
var users = [];
|
|
||||||
$.each(cellData, function (id, data) {
|
|
||||||
var name = htmlEscape(data.name);
|
|
||||||
users.push(name);
|
|
||||||
});
|
|
||||||
$(td).html(users.join(', '))
|
|
||||||
}}
|
|
||||||
],
|
|
||||||
ajax_url: url,
|
|
||||||
columns: [
|
|
||||||
{data: "id"}, {data: "hostname" }, {data: "ip" },
|
|
||||||
{data: "system_users_granted", orderable: false}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
asset_table = jumpserver.initServerSideDataTable(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSelected(event, treeNode) {
|
|
||||||
url = '{% url "api-perms:user-node-assets" pk=object.id node_id=DEFAULT_PK %}?cache_policy=1';
|
|
||||||
var node_id = treeNode.meta.node.id;
|
|
||||||
url = url.replace("{{ DEFAULT_PK }}", node_id);
|
|
||||||
asset_table.ajax.url(url);
|
|
||||||
asset_table.ajax.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function initTree() {
|
|
||||||
var setting = {
|
|
||||||
view: {
|
|
||||||
dblClickExpand: false,
|
|
||||||
showLine: true
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
simpleData: {
|
|
||||||
enable: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
callback: {
|
|
||||||
onSelected: onSelected
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$.get(treeUrl, function(data, status) {
|
|
||||||
$.fn.zTree.init($("#assetTree"), setting, data);
|
|
||||||
zTree = $.fn.zTree.getZTreeObj("assetTree");
|
|
||||||
rootNodeAddDom(zTree, function () {
|
|
||||||
treeUrl = treeUrl.replace('cache_policy=1', 'cache_policy=2');
|
|
||||||
initTree();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
initTree();
|
initTree();
|
||||||
initTable();
|
initTable();
|
||||||
|
|
|
@ -142,7 +142,7 @@ function updateGroupMember(users) {
|
||||||
// clear jumpserver.selected_groups
|
// clear jumpserver.selected_groups
|
||||||
jumpserver.users_selected = {};
|
jumpserver.users_selected = {};
|
||||||
};
|
};
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
success: success
|
success: success
|
||||||
|
|
|
@ -23,142 +23,21 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="col-lg-3" style="padding-left: 0px">
|
{% include 'users/_granted_assets.html' %}
|
||||||
<div class="ibox float-e-margins">
|
|
||||||
<div class="ibox-content mailbox-content" style="padding-top: 0">
|
|
||||||
<div class="file-manager ">
|
|
||||||
<div id="assetTree" class="ztree">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-9 animated fadeInRight">
|
|
||||||
<div class="mail-box-header">
|
|
||||||
<table class="table table-striped table-bordered table-hover" id="user_assets_table" >
|
|
||||||
<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 'Active' %}</th>
|
|
||||||
<th class="text-center">{% trans 'Reachable' %}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
var zTree;
|
var treeUrl = "{% url 'api-perms:user-group-nodes-as-tree' pk=object.id %}?cache_policy=1";
|
||||||
var inited = false;
|
var assetTableUrl = "{% url 'api-perms:user-group-assets' pk=object.id %}?cache_policy=1";
|
||||||
var url;
|
var selectUrl = '{% url "api-perms:user-group-node-assets" pk=object.id node_id=DEFAULT_PK %}?cache_policy=1';
|
||||||
var asset_table;
|
var showAssetHref = true; // Need input default true
|
||||||
|
|
||||||
function initTable() {
|
|
||||||
if (inited){
|
|
||||||
return asset_table
|
|
||||||
} else {
|
|
||||||
inited = true;
|
|
||||||
}
|
|
||||||
url = "{% url 'api-perms:user-group-assets' pk=object.id %}";
|
|
||||||
var options = {
|
|
||||||
ele: $('#user_assets_table'),
|
|
||||||
columnDefs: [
|
|
||||||
{targets: 1, createdCell: function (td, cellData, rowData) {
|
|
||||||
cellData = htmlEscape(cellData);
|
|
||||||
{% 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) {
|
|
||||||
if (!cellData) {
|
|
||||||
$(td).html('<i class="fa fa-times text-danger"></i>')
|
|
||||||
} else {
|
|
||||||
$(td).html('<i class="fa fa-check text-navy"></i>')
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
{targets: 4, createdCell: function (td, cellData) {
|
|
||||||
var users = [];
|
|
||||||
$.each(cellData, function (id, data) {
|
|
||||||
var name = htmlEscape(data.name);
|
|
||||||
users.push(name);
|
|
||||||
});
|
|
||||||
$(td).html(users.join(', '))
|
|
||||||
}}
|
|
||||||
],
|
|
||||||
ajax_url: url,
|
|
||||||
columns: [
|
|
||||||
{data: "id"}, {data: "hostname" }, {data: "ip" },
|
|
||||||
{data: "is_active", orderable: false },
|
|
||||||
{data: "system_users_granted", orderable: false}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
asset_table = jumpserver.initDataTable(options);
|
|
||||||
return asset_table
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function onSelected(event, treeNode) {
|
|
||||||
url = '{% url "api-perms:user-group-node-assets" pk=object.id node_id=DEFAULT_PK %}';
|
|
||||||
var node_id = treeNode.meta.node.id;
|
|
||||||
url = url.replace("{{ DEFAULT_PK }}", node_id);
|
|
||||||
asset_table.ajax.url(url);
|
|
||||||
asset_table.ajax.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function selectQueryNode() {
|
|
||||||
var query_node_id = $.getUrlParam("node");
|
|
||||||
var cookie_node_id = getCookie('node_selected');
|
|
||||||
var node;
|
|
||||||
var node_id;
|
|
||||||
|
|
||||||
if (query_node_id !== null) {
|
|
||||||
node_id = query_node_id
|
|
||||||
} else if (cookie_node_id !== null) {
|
|
||||||
node_id = cookie_node_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
node = zTree.getNodesByParam("id", node_id, null);
|
|
||||||
if (node){
|
|
||||||
zTree.selectNode(node[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initTree() {
|
|
||||||
var setting = {
|
|
||||||
view: {
|
|
||||||
dblClickExpand: false,
|
|
||||||
showLine: true
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
simpleData: {
|
|
||||||
enable: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
callback: {
|
|
||||||
onSelected: onSelected
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$.get("{% url 'api-perms:user-group-nodes-assets-as-tree' pk=object.id %}?show_assets=0", function(data, status) {
|
|
||||||
$.fn.zTree.init($("#assetTree"), setting, data);
|
|
||||||
zTree = $.fn.zTree.getZTreeObj("assetTree");
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
initTree();
|
initTree();
|
||||||
|
|
|
@ -129,7 +129,7 @@ $(document).ready(function() {
|
||||||
swal("{% trans 'UserGroups Delete' %}", msg, "error");
|
swal("{% trans 'UserGroups Delete' %}", msg, "error");
|
||||||
};
|
};
|
||||||
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
|
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
|
||||||
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
|
requestApi({url: url_delete, method: 'DELETE', success: success, error: fail});
|
||||||
jumpserver.checked = false;
|
jumpserver.checked = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,7 +222,7 @@ $(document).ready(function(){
|
||||||
setTimeout( function () {
|
setTimeout( function () {
|
||||||
window.location.reload();}, 300);
|
window.location.reload();}, 300);
|
||||||
}
|
}
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
|
@ -239,7 +239,7 @@ $(document).ready(function(){
|
||||||
setTimeout( function () {
|
setTimeout( function () {
|
||||||
window.location.reload();}, 300);
|
window.location.reload();}, 300);
|
||||||
}
|
}
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
|
@ -259,7 +259,7 @@ $(document).ready(function(){
|
||||||
},function () {
|
},function () {
|
||||||
function success(data) {
|
function success(data) {
|
||||||
url = setUrlParam(the_url, 'spm', data.spm);
|
url = setUrlParam(the_url, 'spm', data.spm);
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url:url,
|
url:url,
|
||||||
method:'DELETE',
|
method:'DELETE',
|
||||||
success:refreshTag,
|
success:refreshTag,
|
||||||
|
@ -272,7 +272,7 @@ $(document).ready(function(){
|
||||||
var msg = "{% trans 'User Deleting failed.' %}";
|
var msg = "{% trans 'User Deleting failed.' %}";
|
||||||
swal("{% trans 'User Delete' %}", msg, "error");
|
swal("{% trans 'User Delete' %}", msg, "error");
|
||||||
}
|
}
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: "{% url 'api-common:resources-cache' %}",
|
url: "{% url 'api-common:resources-cache' %}",
|
||||||
method:'POST',
|
method:'POST',
|
||||||
body:JSON.stringify(data),
|
body:JSON.stringify(data),
|
||||||
|
@ -290,7 +290,7 @@ $(document).ready(function(){
|
||||||
var url = "{% url 'users:user-bulk-update' %}";
|
var url = "{% url 'users:user-bulk-update' %}";
|
||||||
location.href= setUrlParam(url, 'spm', data.spm);
|
location.href= setUrlParam(url, 'spm', data.spm);
|
||||||
}
|
}
|
||||||
APIUpdateAttr({
|
requestApi({
|
||||||
url: "{% url 'api-common:resources-cache' %}",
|
url: "{% url 'api-common:resources-cache' %}",
|
||||||
method:'POST',
|
method:'POST',
|
||||||
body:JSON.stringify(data),
|
body:JSON.stringify(data),
|
||||||
|
|
Loading…
Reference in New Issue