diff --git a/apps/assets/api/label.py b/apps/assets/api/label.py index eb8594e4a..d3537b20c 100644 --- a/apps/assets/api/label.py +++ b/apps/assets/api/label.py @@ -13,11 +13,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from rest_framework_bulk import BulkModelViewSet from rest_framework.pagination import LimitOffsetPagination from django.db.models import Count from common.utils import get_logger +from orgs.mixins import OrgBulkModelViewSet from ..hands import IsOrgAdmin from ..models import Label from .. import serializers @@ -27,7 +27,7 @@ logger = get_logger(__file__) __all__ = ['LabelViewSet'] -class LabelViewSet(BulkModelViewSet): +class LabelViewSet(OrgBulkModelViewSet): filter_fields = ("name", "value") search_fields = filter_fields permission_classes = (IsOrgAdmin,) diff --git a/apps/assets/migrations/0035_auto_20190711_2018.py b/apps/assets/migrations/0035_auto_20190711_2018.py new file mode 100644 index 000000000..9dcbad1db --- /dev/null +++ b/apps/assets/migrations/0035_auto_20190711_2018.py @@ -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'), + ), + ] diff --git a/apps/assets/models/base.py b/apps/assets/models/base.py index b372bc9fd..75f81917f 100644 --- a/apps/assets/models/base.py +++ b/apps/assets/models/base.py @@ -28,7 +28,7 @@ class AssetUser(OrgModelMixin): name = models.CharField(max_length=128, verbose_name=_('Name')) 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')) - 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')) comment = models.TextField(blank=True, verbose_name=_('Comment')) date_created = models.DateTimeField(auto_now_add=True, verbose_name=_("Date created")) diff --git a/apps/assets/serializers/admin_user.py b/apps/assets/serializers/admin_user.py index b8295457f..0efc09801 100644 --- a/apps/assets/serializers/admin_user.py +++ b/apps/assets/serializers/admin_user.py @@ -21,17 +21,14 @@ class AdminUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): model = AdminUser fields = [ 'id', 'name', 'username', 'password', 'private_key', 'public_key', - 'comment', 'assets_amount', - 'date_created', 'date_updated', 'created_by', + 'comment', 'assets_amount', 'date_created', 'date_updated', 'created_by', ] + read_only_fields = ['date_created', 'date_updated', 'created_by', 'assets_amount'] extra_kwargs = { 'password': {"write_only": True}, 'private_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')}, } diff --git a/apps/assets/templates/assets/_asset_user_auth_view_modal.html b/apps/assets/templates/assets/_asset_user_auth_view_modal.html index 6fbd48fcd..417e1021d 100644 --- a/apps/assets/templates/assets/_asset_user_auth_view_modal.html +++ b/apps/assets/templates/assets/_asset_user_auth_view_modal.html @@ -70,7 +70,7 @@ function showAuth() { var msg = "{% trans 'Get auth info error' %}"; toastr.error(msg) }; - APIUpdateAttr({ + requestApi({ url: url, method: "GET", success: success, diff --git a/apps/assets/templates/assets/_asset_user_list.html b/apps/assets/templates/assets/_asset_user_list.html index 381aec13d..f76754391 100644 --- a/apps/assets/templates/assets/_asset_user_list.html +++ b/apps/assets/templates/assets/_asset_user_list.html @@ -141,7 +141,7 @@ $(document).ready(function(){ 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') }; - APIUpdateAttr({ + requestApi({ url: the_url, method: 'GET', success: success, diff --git a/apps/assets/templates/assets/_node_tree.html b/apps/assets/templates/assets/_node_tree.html index 61737184c..9a4004060 100644 --- a/apps/assets/templates/assets/_node_tree.html +++ b/apps/assets/templates/assets/_node_tree.html @@ -235,7 +235,7 @@ function onRename(event, treeId, treeNode, isCancel){ if (isCancel){ return } - APIUpdateAttr({ + requestApi({ url: url, body: JSON.stringify(data), 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 body = {nodes: treeNodesIds}; - APIUpdateAttr({ + requestApi({ url: the_url, method: "PUT", body: JSON.stringify(body) diff --git a/apps/assets/templates/assets/admin_user_assets.html b/apps/assets/templates/assets/admin_user_assets.html index 7c97259ab..9ca433930 100644 --- a/apps/assets/templates/assets/admin_user_assets.html +++ b/apps/assets/templates/assets/admin_user_assets.html @@ -88,7 +88,7 @@ $(document).ready(function () { 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') }; - APIUpdateAttr({ + requestApi({ url: the_url, method: 'GET', success: success, diff --git a/apps/assets/templates/assets/admin_user_detail.html b/apps/assets/templates/assets/admin_user_detail.html index f00e2352a..9e3365509 100644 --- a/apps/assets/templates/assets/admin_user_detail.html +++ b/apps/assets/templates/assets/admin_user_detail.html @@ -131,7 +131,7 @@ function replaceNodeAssetsAdminUser(nodes) { // clear jumpserver.groups_selected jumpserver.nodes_selected = {}; }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success diff --git a/apps/assets/templates/assets/asset_asset_user_list.html b/apps/assets/templates/assets/asset_asset_user_list.html index 39e4816b9..bf3cba583 100644 --- a/apps/assets/templates/assets/asset_asset_user_list.html +++ b/apps/assets/templates/assets/asset_asset_user_list.html @@ -84,7 +84,7 @@ $(document).ready(function () { 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') }; - APIUpdateAttr({ + requestApi({ url: the_url, method: 'GET', success: success, diff --git a/apps/assets/templates/assets/asset_detail.html b/apps/assets/templates/assets/asset_detail.html index ad635ca5b..760cc7e6d 100644 --- a/apps/assets/templates/assets/asset_detail.html +++ b/apps/assets/templates/assets/asset_detail.html @@ -70,7 +70,7 @@ {% trans 'Protocol' %} - {{ asset.protocols }} + {{ asset.protocols }} {% trans 'Admin user' %}: @@ -267,7 +267,7 @@ function updateAssetNodes(nodes) { // clear jumpserver.groups_selected jumpserver.nodes_selected = {}; }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success @@ -282,7 +282,7 @@ function refreshAssetHardware() { var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); window.open(url, '', 'width=800,height=600') }; - APIUpdateAttr({ + requestApi({ url: the_url, success: success, method: 'GET' @@ -306,7 +306,7 @@ $(document).ready(function () { }; var success = '{% trans "Update successfully!" %}'; var status = $(".ibox-content > table > tbody > tr:nth-child(13) > td:last >b").text(); - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success_message: success @@ -360,7 +360,7 @@ $(document).ready(function () { window.open(url, '', 'width=800,height=600') }; - APIUpdateAttr({ + requestApi({ url: the_url, method: 'GET', success: success diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index 8f889feb7..0cdcb176a 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -360,7 +360,7 @@ $(document).ready(function(){ setTimeout( function () { window.location.reload();}, 500); } - APIUpdateAttr({ + requestApi({ url: the_url, method: 'PATCH', body: JSON.stringify(data), @@ -377,7 +377,7 @@ $(document).ready(function(){ setTimeout( function () { window.location.reload();}, 300); } - APIUpdateAttr({ + requestApi({ url: the_url, method: 'PATCH', body: JSON.stringify(data), @@ -397,7 +397,7 @@ $(document).ready(function(){ },function () { function success(data) { url = setUrlParam(the_url, 'spm', data.spm); - APIUpdateAttr({ + requestApi({ url:url, method:'DELETE', success:refreshTag, @@ -410,7 +410,7 @@ $(document).ready(function(){ var msg = "{% trans 'Asset Deleting failed.' %}"; swal("{% trans 'Asset Delete' %}", msg, "error"); } - APIUpdateAttr({ + requestApi({ url: "{% url 'api-common:resources-cache' %}", method:'POST', body:JSON.stringify(data), @@ -428,7 +428,7 @@ $(document).ready(function(){ var url = "{% url 'assets:asset-bulk-update' %}"; location.href= setUrlParam(url, 'spm', data.spm); } - APIUpdateAttr({ + requestApi({ url: "{% url 'api-common:resources-cache' %}", method:'POST', body:JSON.stringify(data), @@ -452,7 +452,7 @@ $(document).ready(function(){ asset_table.ajax.reload() }; - APIUpdateAttr({ + requestApi({ 'url': '/api/assets/v1/nodes/' + current_node_id + '/assets/remove/', 'method': 'PUT', '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); } - APIUpdateAttr({ + requestApi({ 'url': url, 'method': 'PUT', '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); window.open(url, '', 'width=800,height=600') } - APIUpdateAttr({ + requestApi({ url: the_url, method: "GET", success: success, @@ -539,7 +539,7 @@ $(document).ready(function(){ var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); window.open(url, '', 'width=800,height=600') } - APIUpdateAttr({ + requestApi({ url: the_url, method: "GET", success: success, diff --git a/apps/assets/templates/assets/cmd_filter_detail.html b/apps/assets/templates/assets/cmd_filter_detail.html index ee68ff2f6..b98828f4e 100644 --- a/apps/assets/templates/assets/cmd_filter_detail.html +++ b/apps/assets/templates/assets/cmd_filter_detail.html @@ -136,7 +136,7 @@ function updateCMDFilterSystemUsers(system_users) { var success = function(data) { location.reload(); }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), method: 'PATCH', diff --git a/apps/assets/templates/assets/domain_gateway_list.html b/apps/assets/templates/assets/domain_gateway_list.html index d621fb0ec..eb348141e 100644 --- a/apps/assets/templates/assets/domain_gateway_list.html +++ b/apps/assets/templates/assets/domain_gateway_list.html @@ -134,7 +134,7 @@ $(document).ready(function(){ var data = $("#test_gateway_form").serializeObject(); var uid = data.gateway_id; var the_url = '{% url "api-assets:test-gateway-connective" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid); - APIUpdateAttr({ + requestApi({ url: the_url, method: "POST", body: JSON.stringify({'port': parseInt(data.port)}), diff --git a/apps/assets/templates/assets/system_user_assets.html b/apps/assets/templates/assets/system_user_assets.html index 546111130..5818e4ce6 100644 --- a/apps/assets/templates/assets/system_user_assets.html +++ b/apps/assets/templates/assets/system_user_assets.html @@ -146,7 +146,7 @@ function updateSystemUserNode(nodes) { // clear jumpserver.nodes_selected jumpserver.nodes_selected = {}; }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success @@ -206,7 +206,7 @@ $(document).ready(function () { 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') }; - APIUpdateAttr({ + requestApi({ url: the_url, error: error, method: 'GET', @@ -226,7 +226,7 @@ $(document).ready(function () { var error = function (data) { alert(data) }; - APIUpdateAttr({ + requestApi({ url: the_url, method: 'GET', success: success, @@ -243,7 +243,7 @@ $(document).ready(function () { 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') }; - APIUpdateAttr({ + requestApi({ url: the_url, error: error, method: 'GET', diff --git a/apps/assets/templates/assets/system_user_detail.html b/apps/assets/templates/assets/system_user_detail.html index 55f625d81..d9ff1a641 100644 --- a/apps/assets/templates/assets/system_user_detail.html +++ b/apps/assets/templates/assets/system_user_detail.html @@ -212,7 +212,7 @@ function updateCommandFilters(command_filters) { var success = function(data) { location.reload(); }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success @@ -235,7 +235,7 @@ $(document).ready(function () { var body = { 'auto_push': checked }; - APIUpdateAttr({ + requestApi({ url: the_url, 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); window.open(url, '', 'width=800,height=600,left=400,top=400') }; - APIUpdateAttr({ + requestApi({ url: the_url, method: 'GET', success: success, @@ -268,7 +268,7 @@ $(document).ready(function () { var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); window.open(url, '', 'width=800,height=600') }; - APIUpdateAttr({ + requestApi({ url: the_url, method: 'GET', success: success, diff --git a/apps/assets/templates/assets/system_user_list.html b/apps/assets/templates/assets/system_user_list.html index 6c5ff3339..621e18201 100644 --- a/apps/assets/templates/assets/system_user_list.html +++ b/apps/assets/templates/assets/system_user_list.html @@ -182,7 +182,7 @@ $(document).ready(function(){ swal("{% trans 'System Users Delete' %}", msg, "error"); }; 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(); jumpserver.checked = false; }); diff --git a/apps/assets/templates/assets/user_asset_list.html b/apps/assets/templates/assets/user_asset_list.html index f441baa39..d021df0d7 100644 --- a/apps/assets/templates/assets/user_asset_list.html +++ b/apps/assets/templates/assets/user_asset_list.html @@ -11,47 +11,7 @@ {% block content %}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- - -
- - - - - - - - - - - - -
{% trans 'Hostname' %}{% trans 'IP' %}{% trans 'System users' %}{% trans 'Action' %}
-
-
+ {% include 'users/_granted_assets.html' %}
@@ -62,121 +22,51 @@ {% block custom_foot_js %} - {% endblock %} \ No newline at end of file diff --git a/apps/assets/utils.py b/apps/assets/utils.py index a0de3b481..be8a80351 100644 --- a/apps/assets/utils.py +++ b/apps/assets/utils.py @@ -1,7 +1,8 @@ # ~*~ coding: utf-8 ~*~ # 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.struct import Stack @@ -21,24 +22,34 @@ def get_system_user_by_id(id): return system_user -class LabelFilter: - def filter_queryset(self, queryset): - queryset = super().filter_queryset(queryset) - query_keys = self.request.query_params.keys() +class LabelFilterMixin: + def get_filter_labels_ids(self): + query_params = self.request.query_params + query_keys = query_params.keys() all_label_keys = Label.objects.values_list('name', flat=True) 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 = [] - for k, v in labels_query.items(): - query = {'labels__name': k, 'labels__value': v} - conditions.append(query) + if not valid_keys: + return [] - if conditions: - for kwargs in conditions: - queryset = queryset.filter(**kwargs) + labels_query = [ + {"name": key, "value": query_params[key]} + 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 diff --git a/apps/assets/views/asset.py b/apps/assets/views/asset.py index ce701b129..40601d27f 100644 --- a/apps/assets/views/asset.py +++ b/apps/assets/views/asset.py @@ -69,7 +69,7 @@ class UserAssetListView(PermissionsMixin, TemplateView): context = { 'action': _('My assets'), 'labels': Label.objects.all().order_by('name'), - 'system_users': SystemUser.objects.all(), + 'show_actions': True } kwargs.update(context) return super().get_context_data(**kwargs) diff --git a/apps/authentication/templates/authentication/_mfa_confirm_modal.html b/apps/authentication/templates/authentication/_mfa_confirm_modal.html index 0d7b794bb..60512d7de 100644 --- a/apps/authentication/templates/authentication/_mfa_confirm_modal.html +++ b/apps/authentication/templates/authentication/_mfa_confirm_modal.html @@ -38,7 +38,7 @@ $(document).ready(function () { var error = function () { $("#mfa_error").addClass("text-danger").html(codeError); }; - APIUpdateAttr({ + requestApi({ url: url, method: "POST", body: JSON.stringify(data), diff --git a/apps/common/permissions.py b/apps/common/permissions.py index edb5ee4d0..bdc25fe21 100644 --- a/apps/common/permissions.py +++ b/apps/common/permissions.py @@ -145,13 +145,13 @@ class NeedMFAVerify(permissions.BasePermission): return False -class CanUpdateSuperUser(permissions.BasePermission): +class CanUpdateDeleteSuperUser(permissions.BasePermission): def has_object_permission(self, request, view, obj): if request.method in ['GET', 'OPTIONS']: return True - if str(request.user.id) == str(obj.id): + elif request.method == 'DELETE' and str(request.user.id) == str(obj.id): return False - if request.user.is_superuser: + elif request.user.is_superuser: return True if hasattr(obj, 'is_superuser') and obj.is_superuser: return False diff --git a/apps/ops/templates/ops/command_execution_create.html b/apps/ops/templates/ops/command_execution_create.html index 3e4436e41..17d2c044a 100644 --- a/apps/ops/templates/ops/command_execution_create.html +++ b/apps/ops/templates/ops/command_execution_create.html @@ -255,7 +255,7 @@ function execute() { } } - APIUpdateAttr({ + requestApi({ url: url, body: JSON.stringify(data), method: 'POST', diff --git a/apps/ops/templates/ops/task_list.html b/apps/ops/templates/ops/task_list.html index 0426e059d..ae8c45043 100644 --- a/apps/ops/templates/ops/task_list.html +++ b/apps/ops/templates/ops/task_list.html @@ -109,7 +109,7 @@ $(document).ready(function() { 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') }; - APIUpdateAttr({ + requestApi({ url: the_url, error: error, method: 'GET', diff --git a/apps/perms/api/mixin.py b/apps/perms/api/mixin.py new file mode 100644 index 000000000..24bd9abd2 --- /dev/null +++ b/apps/perms/api/mixin.py @@ -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 \ No newline at end of file diff --git a/apps/perms/api/user_group_permission.py b/apps/perms/api/user_group_permission.py index 9e32055d4..f83330c58 100644 --- a/apps/perms/api/user_group_permission.py +++ b/apps/perms/api/user_group_permission.py @@ -2,23 +2,21 @@ # 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 ..hands import UserGroup -from .. import serializers, const +from .. import serializers from .user_permission import ( UserGrantedAssetsApi, UserGrantedNodesApi, UserGrantedNodesWithAssetsApi, UserGrantedNodesWithAssetsAsTreeApi, UserGrantedNodeAssetsApi, + UserGrantedNodesAsTreeApi, ) __all__ = [ 'UserGroupGrantedAssetsApi', 'UserGroupGrantedNodesApi', 'UserGroupGrantedNodesWithAssetsApi', 'UserGroupGrantedNodeAssetsApi', - 'UserGroupGrantedNodesWithAssetsAsTreeApi', + 'UserGroupGrantedNodesWithAssetsAsTreeApi', 'UserGroupGrantedNodesAsTreeApi', ] @@ -36,6 +34,13 @@ class UserGroupGrantedNodesApi(UserGrantedNodesApi): 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): permission_classes = (IsOrgAdmin,) serializer_class = serializers.NodeGrantedSerializer diff --git a/apps/perms/api/user_permission.py b/apps/perms/api/user_permission.py index 8d55ee49c..0e67e2747 100644 --- a/apps/perms/api/user_permission.py +++ b/apps/perms/api/user_permission.py @@ -2,25 +2,23 @@ # import time import traceback +from functools import reduce 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.shortcuts import get_object_or_404 from rest_framework.views import APIView, Response from rest_framework.generics import ( ListAPIView, get_object_or_404, RetrieveAPIView ) -from django.utils.translation import ugettext as _ from rest_framework.pagination import LimitOffsetPagination from common.permissions import IsValidUser, IsOrgAdminOrAppUser from common.tree import TreeNodeSerializer -from common.utils import get_logger, get_object_or_none +from common.utils import get_logger from ..utils import ( AssetPermissionUtil, ParserNode, ) +from .mixin import UserPermissionCacheMixin, GrantAssetsMixin, NodesWithUngroupMixin from .. import const from ..hands import User, Asset, Node, SystemUser, NodeSerializer 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): """ 用户授权的所有资产 @@ -203,7 +54,6 @@ class UserGrantedAssetsApi(UserPermissionCacheMixin, GrantAssetsMixin, ListAPIVi user = self.get_object() util = AssetPermissionUtil(user, cache_policy=self.cache_policy) queryset = util.get_assets() - queryset = self.search_queryset(queryset) return queryset def get_permissions(self): @@ -212,29 +62,52 @@ class UserGrantedAssetsApi(UserPermissionCacheMixin, GrantAssetsMixin, ListAPIVi return super().get_permissions() -class NodesWithUngroupMixin: - util = None +class UserGrantedNodeAssetsApi(UserPermissionCacheMixin, GrantAssetsMixin, ListAPIView): + """ + 查询用户授权的节点下的资产的api, 与上面api不同的是,只返回某个节点下的资产 + """ + permission_classes = (IsOrgAdminOrAppUser,) + pagination_class = LimitOffsetPagination - @staticmethod - def get_ungrouped_node(ungroup_key): - return Node(key=ungroup_key, id=const.UNGROUPED_NODE_ID, - value=_("ungrouped")) + def get_object(self): + user_id = self.kwargs.get('pk', '') - @staticmethod - def get_empty_node(): - return Node(key=const.EMPTY_NODE_KEY, id=const.EMPTY_NODE_ID, - value=_("empty")) + if user_id: + user = get_object_or_404(User, id=user_id) + else: + user = self.request.user + return user - 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 + 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 - 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 + assets = [] + for asset_id, system_users in assets_system_users.items(): + assets.append({"id": asset_id, "system_users": system_users}) + 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): @@ -435,55 +308,6 @@ class UserGrantedNodesWithAssetsAsTreeApi(UserGrantedNodesWithAssetsApi): 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): permission_classes = (IsOrgAdminOrAppUser,) @@ -522,16 +346,12 @@ class GetUserAssetPermissionActionsApi(UserPermissionCacheMixin, RetrieveAPIView system_id = self.request.query_params.get('system_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) - granted_assets = util.get_assets() - granted_system_users = granted_assets.get(asset, {}) - - _object = {} - if su not in granted_system_users: - _object['actions'] = 0 - else: - _object['actions'] = granted_system_users[su] - return _object + assets = util.get_assets() + actions = 0 + for asset in assets: + if asset_id == asset["id"]: + actions = asset["system_users"].get(system_id, 0) + break + return {"actions": actions} diff --git a/apps/perms/hands.py b/apps/perms/hands.py index bbdc01e1e..aef0f4875 100644 --- a/apps/perms/hands.py +++ b/apps/perms/hands.py @@ -2,7 +2,7 @@ # 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 applications.serializers import RemoteAppSerializer from applications.models import RemoteApp diff --git a/apps/perms/templates/perms/asset_permission_asset.html b/apps/perms/templates/perms/asset_permission_asset.html index ea5b20e63..d8cd8ee96 100644 --- a/apps/perms/templates/perms/asset_permission_asset.html +++ b/apps/perms/templates/perms/asset_permission_asset.html @@ -145,7 +145,7 @@ function addAssets(assets) { var success = function(data) { location.reload(); }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success @@ -160,7 +160,7 @@ function removeAssets(assets) { var success = function(data) { location.reload(); }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success @@ -172,7 +172,7 @@ function updateNodes(nodes, success) { var body = { nodes: nodes }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success diff --git a/apps/perms/templates/perms/asset_permission_detail.html b/apps/perms/templates/perms/asset_permission_detail.html index 9c38cfc29..f9c11cb67 100644 --- a/apps/perms/templates/perms/asset_permission_detail.html +++ b/apps/perms/templates/perms/asset_permission_detail.html @@ -187,7 +187,7 @@ function updateSystemUser(system_users) { var body = { system_users: Object.assign([], system_users) }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body) }); @@ -247,7 +247,7 @@ $(document).ready(function () { var body = { 'is_active': checked }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), }); diff --git a/apps/perms/templates/perms/asset_permission_user.html b/apps/perms/templates/perms/asset_permission_user.html index eef6bf601..d5e8292e1 100644 --- a/apps/perms/templates/perms/asset_permission_user.html +++ b/apps/perms/templates/perms/asset_permission_user.html @@ -160,7 +160,7 @@ function addUsers(users) { var success = function(data) { location.reload(); }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success @@ -175,7 +175,7 @@ function removeUser(users) { var success = function(data) { location.reload(); }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success @@ -187,7 +187,7 @@ function updateGroup(groups) { var body = { user_groups: groups }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body) }); diff --git a/apps/perms/templates/perms/remote_app_permission_detail.html b/apps/perms/templates/perms/remote_app_permission_detail.html index 98bc6633e..352e9f17a 100644 --- a/apps/perms/templates/perms/remote_app_permission_detail.html +++ b/apps/perms/templates/perms/remote_app_permission_detail.html @@ -160,7 +160,7 @@ $(document).ready(function () { var body = { 'is_active': checked }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body) }); diff --git a/apps/perms/templates/perms/remote_app_permission_remote_app.html b/apps/perms/templates/perms/remote_app_permission_remote_app.html index 63d395941..2e705945f 100644 --- a/apps/perms/templates/perms/remote_app_permission_remote_app.html +++ b/apps/perms/templates/perms/remote_app_permission_remote_app.html @@ -120,7 +120,7 @@ var success = function(data) { location.reload(); }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success @@ -134,7 +134,7 @@ var success = function(data) { location.reload(); }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success diff --git a/apps/perms/templates/perms/remote_app_permission_user.html b/apps/perms/templates/perms/remote_app_permission_user.html index 7433327c5..b1fc8e488 100644 --- a/apps/perms/templates/perms/remote_app_permission_user.html +++ b/apps/perms/templates/perms/remote_app_permission_user.html @@ -158,7 +158,7 @@ var success = function(data) { location.reload(); }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success @@ -172,7 +172,7 @@ var success = function(data) { location.reload(); }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success @@ -183,7 +183,7 @@ var body = { user_groups: groups }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body) }); diff --git a/apps/perms/urls/api_urls.py b/apps/perms/urls/api_urls.py index 7075bda8e..093692a15 100644 --- a/apps/perms/urls/api_urls.py +++ b/apps/perms/urls/api_urls.py @@ -39,9 +39,10 @@ asset_permission_urlpatterns = [ # 查询某个用户组授权的资产和资产组 path('user-groups//assets/', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'), - path('user-groups//nodes/', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'), - path('user-groups//nodes-assets/', api.UserGroupGrantedNodesWithAssetsApi.as_view(), name='user-group-nodes-assets'), + path('user-groups//nodes/tree/', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'), + path('user-groups//nodes/', api.UserGroupGrantedNodesAsTreeApi.as_view(), name='user-group-nodes-as-tree'), path('user-groups//nodes-assets/tree/', api.UserGroupGrantedNodesWithAssetsAsTreeApi.as_view(), name='user-group-nodes-assets-as-tree'), + path('user-groups//nodes-assets/', api.UserGroupGrantedNodesWithAssetsApi.as_view(), name='user-group-nodes-assets'), path('user-groups//nodes//assets/', api.UserGroupGrantedNodeAssetsApi.as_view(), name='user-group-node-assets'), # 用户和资产授权变更 diff --git a/apps/settings/templates/settings/email_setting.html b/apps/settings/templates/settings/email_setting.html index 46c4f5dac..40ce9f4cc 100644 --- a/apps/settings/templates/settings/email_setting.html +++ b/apps/settings/templates/settings/email_setting.html @@ -96,7 +96,7 @@ $(document).ready(function () { function success(message) { toastr.success(message.msg) } - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(data), method: "POST", diff --git a/apps/settings/templates/settings/ldap_setting.html b/apps/settings/templates/settings/ldap_setting.html index 58e4ae71b..694fb66f9 100644 --- a/apps/settings/templates/settings/ldap_setting.html +++ b/apps/settings/templates/settings/ldap_setting.html @@ -100,7 +100,7 @@ $(document).ready(function () { function success(message) { toastr.success(message.msg) } - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(data), method: "POST", @@ -127,7 +127,7 @@ $(document).ready(function () { function success(message) { toastr.success(message.msg) } - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify({'username_list':username_list}), method: "POST", diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index a216a1ce0..c880a5499 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -256,7 +256,7 @@ function formSubmit(props) { }) } -function APIUpdateAttr(props) { +function requestApi(props) { // props = {url: .., body: , success: , error: , method: ,} props = props || {}; var user_success_message = props.success_message; @@ -328,7 +328,7 @@ function objectDelete(obj, name, url, redirectTo) { // swal("错误", "删除"+"[ "+name+" ]"+"遇到错误", "error"); swal(gettext('Error'), "[ "+name+" ]" + gettext("Being used by the asset, please unbind the asset first."), "error"); }; - APIUpdateAttr({ + requestApi({ url: url, body: JSON.stringify(body), 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"); } }; - APIUpdateAttr({ + requestApi({ url: url, body: JSON.stringify(body), 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) { + d = cleanDate(d); var date = new Date(d); var date_s = date.toLocaleString(navigator.language, {hour12: false}); return date_s.split("/").join('-') @@ -1138,6 +1150,8 @@ function getTimeUnits(u) { } function timeOffset(a, b) { + a = cleanDate(a); + b = cleanDate(b); var start = new Date(a); var end = new Date(b); var offset = (end - start)/1000; diff --git a/apps/static/js/plugins/moment/moment.min.js b/apps/static/js/plugins/moment/moment.min.js new file mode 100644 index 000000000..8e6866af0 --- /dev/null +++ b/apps/static/js/plugins/moment/moment.min.js @@ -0,0 +1,7 @@ +//! moment.js +//! version : 2.10.6 +//! authors : Tim Wood, Iskren Chernev, Moment.js contributors +//! license : MIT +//! momentjs.com +!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.moment=b()}(this,function(){"use strict";function a(){return Hc.apply(null,arguments)}function b(a){Hc=a}function c(a){return"[object Array]"===Object.prototype.toString.call(a)}function d(a){return a instanceof Date||"[object Date]"===Object.prototype.toString.call(a)}function e(a,b){var c,d=[];for(c=0;c0)for(c in Jc)d=Jc[c],e=b[d],"undefined"!=typeof e&&(a[d]=e);return a}function n(b){m(this,b),this._d=new Date(null!=b._d?b._d.getTime():NaN),Kc===!1&&(Kc=!0,a.updateOffset(this),Kc=!1)}function o(a){return a instanceof n||null!=a&&null!=a._isAMomentObject}function p(a){return 0>a?Math.ceil(a):Math.floor(a)}function q(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=p(b)),c}function r(a,b,c){var d,e=Math.min(a.length,b.length),f=Math.abs(a.length-b.length),g=0;for(d=0;e>d;d++)(c&&a[d]!==b[d]||!c&&q(a[d])!==q(b[d]))&&g++;return g+f}function s(){}function t(a){return a?a.toLowerCase().replace("_","-"):a}function u(a){for(var b,c,d,e,f=0;f0;){if(d=v(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&r(e,c,!0)>=b-1)break;b--}f++}return null}function v(a){var b=null;if(!Lc[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=Ic._abbr,require("./locale/"+a),w(b)}catch(c){}return Lc[a]}function w(a,b){var c;return a&&(c="undefined"==typeof b?y(a):x(a,b),c&&(Ic=c)),Ic._abbr}function x(a,b){return null!==b?(b.abbr=a,Lc[a]=Lc[a]||new s,Lc[a].set(b),w(a),Lc[a]):(delete Lc[a],null)}function y(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return Ic;if(!c(a)){if(b=v(a))return b;a=[a]}return u(a)}function z(a,b){var c=a.toLowerCase();Mc[c]=Mc[c+"s"]=Mc[b]=a}function A(a){return"string"==typeof a?Mc[a]||Mc[a.toLowerCase()]:void 0}function B(a){var b,c,d={};for(c in a)f(a,c)&&(b=A(c),b&&(d[b]=a[c]));return d}function C(b,c){return function(d){return null!=d?(E(this,b,d),a.updateOffset(this,c),this):D(this,b)}}function D(a,b){return a._d["get"+(a._isUTC?"UTC":"")+b]()}function E(a,b,c){return a._d["set"+(a._isUTC?"UTC":"")+b](c)}function F(a,b){var c;if("object"==typeof a)for(c in a)this.set(c,a[c]);else if(a=A(a),"function"==typeof this[a])return this[a](b);return this}function G(a,b,c){var d=""+Math.abs(a),e=b-d.length,f=a>=0;return(f?c?"+":"":"-")+Math.pow(10,Math.max(0,e)).toString().substr(1)+d}function H(a,b,c,d){var e=d;"string"==typeof d&&(e=function(){return this[d]()}),a&&(Qc[a]=e),b&&(Qc[b[0]]=function(){return G(e.apply(this,arguments),b[1],b[2])}),c&&(Qc[c]=function(){return this.localeData().ordinal(e.apply(this,arguments),a)})}function I(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function J(a){var b,c,d=a.match(Nc);for(b=0,c=d.length;c>b;b++)Qc[d[b]]?d[b]=Qc[d[b]]:d[b]=I(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function K(a,b){return a.isValid()?(b=L(b,a.localeData()),Pc[b]=Pc[b]||J(b),Pc[b](a)):a.localeData().invalidDate()}function L(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Oc.lastIndex=0;d>=0&&Oc.test(a);)a=a.replace(Oc,c),Oc.lastIndex=0,d-=1;return a}function M(a){return"function"==typeof a&&"[object Function]"===Object.prototype.toString.call(a)}function N(a,b,c){dd[a]=M(b)?b:function(a){return a&&c?c:b}}function O(a,b){return f(dd,a)?dd[a](b._strict,b._locale):new RegExp(P(a))}function P(a){return a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}).replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function Q(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),"number"==typeof b&&(d=function(a,c){c[b]=q(a)}),c=0;cd;d++){if(e=h([2e3,d]),c&&!this._longMonthsParse[d]&&(this._longMonthsParse[d]=new RegExp("^"+this.months(e,"").replace(".","")+"$","i"),this._shortMonthsParse[d]=new RegExp("^"+this.monthsShort(e,"").replace(".","")+"$","i")),c||this._monthsParse[d]||(f="^"+this.months(e,"")+"|^"+this.monthsShort(e,""),this._monthsParse[d]=new RegExp(f.replace(".",""),"i")),c&&"MMMM"===b&&this._longMonthsParse[d].test(a))return d;if(c&&"MMM"===b&&this._shortMonthsParse[d].test(a))return d;if(!c&&this._monthsParse[d].test(a))return d}}function X(a,b){var c;return"string"==typeof b&&(b=a.localeData().monthsParse(b),"number"!=typeof b)?a:(c=Math.min(a.date(),T(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a)}function Y(b){return null!=b?(X(this,b),a.updateOffset(this,!0),this):D(this,"Month")}function Z(){return T(this.year(),this.month())}function $(a){var b,c=a._a;return c&&-2===j(a).overflow&&(b=c[gd]<0||c[gd]>11?gd:c[hd]<1||c[hd]>T(c[fd],c[gd])?hd:c[id]<0||c[id]>24||24===c[id]&&(0!==c[jd]||0!==c[kd]||0!==c[ld])?id:c[jd]<0||c[jd]>59?jd:c[kd]<0||c[kd]>59?kd:c[ld]<0||c[ld]>999?ld:-1,j(a)._overflowDayOfYear&&(fd>b||b>hd)&&(b=hd),j(a).overflow=b),a}function _(b){a.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+b)}function aa(a,b){var c=!0;return g(function(){return c&&(_(a+"\n"+(new Error).stack),c=!1),b.apply(this,arguments)},b)}function ba(a,b){od[a]||(_(b),od[a]=!0)}function ca(a){var b,c,d=a._i,e=pd.exec(d);if(e){for(j(a).iso=!0,b=0,c=qd.length;c>b;b++)if(qd[b][1].exec(d)){a._f=qd[b][0];break}for(b=0,c=rd.length;c>b;b++)if(rd[b][1].exec(d)){a._f+=(e[6]||" ")+rd[b][0];break}d.match(ad)&&(a._f+="Z"),va(a)}else a._isValid=!1}function da(b){var c=sd.exec(b._i);return null!==c?void(b._d=new Date(+c[1])):(ca(b),void(b._isValid===!1&&(delete b._isValid,a.createFromInputFallback(b))))}function ea(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return 1970>a&&h.setFullYear(a),h}function fa(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function ga(a){return ha(a)?366:365}function ha(a){return a%4===0&&a%100!==0||a%400===0}function ia(){return ha(this.year())}function ja(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=Da(a).add(f,"d"),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function ka(a){return ja(a,this._week.dow,this._week.doy).week}function la(){return this._week.dow}function ma(){return this._week.doy}function na(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function oa(a){var b=ja(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")}function pa(a,b,c,d,e){var f,g=6+e-d,h=fa(a,0,1+g),i=h.getUTCDay();return e>i&&(i+=7),c=null!=c?1*c:e,f=1+g+7*(b-1)-i+c,{year:f>0?a:a-1,dayOfYear:f>0?f:ga(a-1)+f}}function qa(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function ra(a,b,c){return null!=a?a:null!=b?b:c}function sa(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function ta(a){var b,c,d,e,f=[];if(!a._d){for(d=sa(a),a._w&&null==a._a[hd]&&null==a._a[gd]&&ua(a),a._dayOfYear&&(e=ra(a._a[fd],d[fd]),a._dayOfYear>ga(e)&&(j(a)._overflowDayOfYear=!0),c=fa(e,0,a._dayOfYear),a._a[gd]=c.getUTCMonth(),a._a[hd]=c.getUTCDate()),b=0;3>b&&null==a._a[b];++b)a._a[b]=f[b]=d[b];for(;7>b;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b];24===a._a[id]&&0===a._a[jd]&&0===a._a[kd]&&0===a._a[ld]&&(a._nextDay=!0,a._a[id]=0),a._d=(a._useUTC?fa:ea).apply(null,f),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[id]=24)}}function ua(a){var b,c,d,e,f,g,h;b=a._w,null!=b.GG||null!=b.W||null!=b.E?(f=1,g=4,c=ra(b.GG,a._a[fd],ja(Da(),1,4).year),d=ra(b.W,1),e=ra(b.E,1)):(f=a._locale._week.dow,g=a._locale._week.doy,c=ra(b.gg,a._a[fd],ja(Da(),f,g).year),d=ra(b.w,1),null!=b.d?(e=b.d,f>e&&++d):e=null!=b.e?b.e+f:f),h=pa(c,d,e,g,f),a._a[fd]=h.year,a._dayOfYear=h.dayOfYear}function va(b){if(b._f===a.ISO_8601)return void ca(b);b._a=[],j(b).empty=!0;var c,d,e,f,g,h=""+b._i,i=h.length,k=0;for(e=L(b._f,b._locale).match(Nc)||[],c=0;c0&&j(b).unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),k+=d.length),Qc[f]?(d?j(b).empty=!1:j(b).unusedTokens.push(f),S(f,d,b)):b._strict&&!d&&j(b).unusedTokens.push(f);j(b).charsLeftOver=i-k,h.length>0&&j(b).unusedInput.push(h),j(b).bigHour===!0&&b._a[id]<=12&&b._a[id]>0&&(j(b).bigHour=void 0),b._a[id]=wa(b._locale,b._a[id],b._meridiem),ta(b),$(b)}function wa(a,b,c){var d;return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&12>b&&(b+=12),d||12!==b||(b=0),b):b}function xa(a){var b,c,d,e,f;if(0===a._f.length)return j(a).invalidFormat=!0,void(a._d=new Date(NaN));for(e=0;ef)&&(d=f,c=b));g(a,c||b)}function ya(a){if(!a._d){var b=B(a._i);a._a=[b.year,b.month,b.day||b.date,b.hour,b.minute,b.second,b.millisecond],ta(a)}}function za(a){var b=new n($(Aa(a)));return b._nextDay&&(b.add(1,"d"),b._nextDay=void 0),b}function Aa(a){var b=a._i,e=a._f;return a._locale=a._locale||y(a._l),null===b||void 0===e&&""===b?l({nullInput:!0}):("string"==typeof b&&(a._i=b=a._locale.preparse(b)),o(b)?new n($(b)):(c(e)?xa(a):e?va(a):d(b)?a._d=b:Ba(a),a))}function Ba(b){var f=b._i;void 0===f?b._d=new Date:d(f)?b._d=new Date(+f):"string"==typeof f?da(b):c(f)?(b._a=e(f.slice(0),function(a){return parseInt(a,10)}),ta(b)):"object"==typeof f?ya(b):"number"==typeof f?b._d=new Date(f):a.createFromInputFallback(b)}function Ca(a,b,c,d,e){var f={};return"boolean"==typeof c&&(d=c,c=void 0),f._isAMomentObject=!0,f._useUTC=f._isUTC=e,f._l=c,f._i=a,f._f=b,f._strict=d,za(f)}function Da(a,b,c,d){return Ca(a,b,c,d,!1)}function Ea(a,b){var d,e;if(1===b.length&&c(b[0])&&(b=b[0]),!b.length)return Da();for(d=b[0],e=1;ea&&(a=-a,c="-"),c+G(~~(a/60),2)+b+G(~~a%60,2)})}function Ka(a){var b=(a||"").match(ad)||[],c=b[b.length-1]||[],d=(c+"").match(xd)||["-",0,0],e=+(60*d[1])+q(d[2]);return"+"===d[0]?e:-e}function La(b,c){var e,f;return c._isUTC?(e=c.clone(),f=(o(b)||d(b)?+b:+Da(b))-+e,e._d.setTime(+e._d+f),a.updateOffset(e,!1),e):Da(b).local()}function Ma(a){return 15*-Math.round(a._d.getTimezoneOffset()/15)}function Na(b,c){var d,e=this._offset||0;return null!=b?("string"==typeof b&&(b=Ka(b)),Math.abs(b)<16&&(b=60*b),!this._isUTC&&c&&(d=Ma(this)),this._offset=b,this._isUTC=!0,null!=d&&this.add(d,"m"),e!==b&&(!c||this._changeInProgress?bb(this,Ya(b-e,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?e:Ma(this)}function Oa(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}function Pa(a){return this.utcOffset(0,a)}function Qa(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(Ma(this),"m")),this}function Ra(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(Ka(this._i)),this}function Sa(a){return a=a?Da(a).utcOffset():0,(this.utcOffset()-a)%60===0}function Ta(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Ua(){if("undefined"!=typeof this._isDSTShifted)return this._isDSTShifted;var a={};if(m(a,this),a=Aa(a),a._a){var b=a._isUTC?h(a._a):Da(a._a);this._isDSTShifted=this.isValid()&&r(a._a,b.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Va(){return!this._isUTC}function Wa(){return this._isUTC}function Xa(){return this._isUTC&&0===this._offset}function Ya(a,b){var c,d,e,g=a,h=null;return Ia(a)?g={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(g={},b?g[b]=a:g.milliseconds=a):(h=yd.exec(a))?(c="-"===h[1]?-1:1,g={y:0,d:q(h[hd])*c,h:q(h[id])*c,m:q(h[jd])*c,s:q(h[kd])*c,ms:q(h[ld])*c}):(h=zd.exec(a))?(c="-"===h[1]?-1:1,g={y:Za(h[2],c),M:Za(h[3],c),d:Za(h[4],c),h:Za(h[5],c),m:Za(h[6],c),s:Za(h[7],c),w:Za(h[8],c)}):null==g?g={}:"object"==typeof g&&("from"in g||"to"in g)&&(e=_a(Da(g.from),Da(g.to)),g={},g.ms=e.milliseconds,g.M=e.months),d=new Ha(g),Ia(a)&&f(a,"_locale")&&(d._locale=a._locale),d}function Za(a,b){var c=a&&parseFloat(a.replace(",","."));return(isNaN(c)?0:c)*b}function $a(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function _a(a,b){var c;return b=La(b,a),a.isBefore(b)?c=$a(a,b):(c=$a(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c}function ab(a,b){return function(c,d){var e,f;return null===d||isNaN(+d)||(ba(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period)."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=Ya(c,d),bb(this,e,a),this}}function bb(b,c,d,e){var f=c._milliseconds,g=c._days,h=c._months;e=null==e?!0:e,f&&b._d.setTime(+b._d+f*d),g&&E(b,"Date",D(b,"Date")+g*d),h&&X(b,D(b,"Month")+h*d),e&&a.updateOffset(b,g||h)}function cb(a,b){var c=a||Da(),d=La(c,this).startOf("day"),e=this.diff(d,"days",!0),f=-6>e?"sameElse":-1>e?"lastWeek":0>e?"lastDay":1>e?"sameDay":2>e?"nextDay":7>e?"nextWeek":"sameElse";return this.format(b&&b[f]||this.localeData().calendar(f,this,Da(c)))}function db(){return new n(this)}function eb(a,b){var c;return b=A("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=o(a)?a:Da(a),+this>+a):(c=o(a)?+a:+Da(a),c<+this.clone().startOf(b))}function fb(a,b){var c;return b=A("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=o(a)?a:Da(a),+a>+this):(c=o(a)?+a:+Da(a),+this.clone().endOf(b)b-f?(c=a.clone().add(e-1,"months"),d=(b-f)/(f-c)):(c=a.clone().add(e+1,"months"),d=(b-f)/(c-f)),-(e+d)}function kb(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function lb(){var a=this.clone().utc();return 0b;b++)if(this._weekdaysParse[b]||(c=Da([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b}function Pb(a){var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=Kb(a,this.localeData()),this.add(a-b,"d")):b}function Qb(a){var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function Rb(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)}function Sb(a,b){H(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})}function Tb(a,b){return b._meridiemParse}function Ub(a){return"p"===(a+"").toLowerCase().charAt(0)}function Vb(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function Wb(a,b){b[ld]=q(1e3*("0."+a))}function Xb(){return this._isUTC?"UTC":""}function Yb(){return this._isUTC?"Coordinated Universal Time":""}function Zb(a){return Da(1e3*a)}function $b(){return Da.apply(null,arguments).parseZone()}function _b(a,b,c){var d=this._calendar[a];return"function"==typeof d?d.call(b,c):d}function ac(a){var b=this._longDateFormat[a],c=this._longDateFormat[a.toUpperCase()];return b||!c?b:(this._longDateFormat[a]=c.replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a])}function bc(){return this._invalidDate}function cc(a){return this._ordinal.replace("%d",a)}function dc(a){return a}function ec(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)}function fc(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)}function gc(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function hc(a,b,c,d){var e=y(),f=h().set(d,b);return e[c](f,a)}function ic(a,b,c,d,e){if("number"==typeof a&&(b=a,a=void 0),a=a||"",null!=b)return hc(a,b,c,e);var f,g=[];for(f=0;d>f;f++)g[f]=hc(a,f,c,e);return g}function jc(a,b){return ic(a,b,"months",12,"month")}function kc(a,b){return ic(a,b,"monthsShort",12,"month")}function lc(a,b){return ic(a,b,"weekdays",7,"day")}function mc(a,b){return ic(a,b,"weekdaysShort",7,"day")}function nc(a,b){return ic(a,b,"weekdaysMin",7,"day")}function oc(){var a=this._data;return this._milliseconds=Wd(this._milliseconds),this._days=Wd(this._days),this._months=Wd(this._months),a.milliseconds=Wd(a.milliseconds),a.seconds=Wd(a.seconds),a.minutes=Wd(a.minutes),a.hours=Wd(a.hours),a.months=Wd(a.months),a.years=Wd(a.years),this}function pc(a,b,c,d){var e=Ya(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()}function qc(a,b){return pc(this,a,b,1)}function rc(a,b){return pc(this,a,b,-1)}function sc(a){return 0>a?Math.floor(a):Math.ceil(a)}function tc(){var a,b,c,d,e,f=this._milliseconds,g=this._days,h=this._months,i=this._data;return f>=0&&g>=0&&h>=0||0>=f&&0>=g&&0>=h||(f+=864e5*sc(vc(h)+g),g=0,h=0),i.milliseconds=f%1e3,a=p(f/1e3),i.seconds=a%60,b=p(a/60),i.minutes=b%60,c=p(b/60),i.hours=c%24,g+=p(c/24),e=p(uc(g)),h+=e,g-=sc(vc(e)),d=p(h/12),h%=12,i.days=g,i.months=h,i.years=d,this}function uc(a){return 4800*a/146097}function vc(a){return 146097*a/4800}function wc(a){var b,c,d=this._milliseconds;if(a=A(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+uc(b),"month"===a?c:c/12;switch(b=this._days+Math.round(vc(this._months)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 1440*b+d/6e4;case"second":return 86400*b+d/1e3;case"millisecond":return Math.floor(864e5*b)+d;default:throw new Error("Unknown unit "+a)}}function xc(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*q(this._months/12)}function yc(a){return function(){return this.as(a)}}function zc(a){return a=A(a),this[a+"s"]()}function Ac(a){return function(){return this._data[a]}}function Bc(){return p(this.days()/7)}function Cc(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function Dc(a,b,c){var d=Ya(a).abs(),e=ke(d.as("s")),f=ke(d.as("m")),g=ke(d.as("h")),h=ke(d.as("d")),i=ke(d.as("M")),j=ke(d.as("y")),k=e0,k[4]=c,Cc.apply(null,k)}function Ec(a,b){return void 0===le[a]?!1:void 0===b?le[a]:(le[a]=b,!0)}function Fc(a){var b=this.localeData(),c=Dc(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function Gc(){var a,b,c,d=me(this._milliseconds)/1e3,e=me(this._days),f=me(this._months);a=p(d/60),b=p(a/60),d%=60,a%=60,c=p(f/12),f%=12;var g=c,h=f,i=e,j=b,k=a,l=d,m=this.asSeconds();return m?(0>m?"-":"")+"P"+(g?g+"Y":"")+(h?h+"M":"")+(i?i+"D":"")+(j||k||l?"T":"")+(j?j+"H":"")+(k?k+"M":"")+(l?l+"S":""):"P0D"}var Hc,Ic,Jc=a.momentProperties=[],Kc=!1,Lc={},Mc={},Nc=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,Oc=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,Pc={},Qc={},Rc=/\d/,Sc=/\d\d/,Tc=/\d{3}/,Uc=/\d{4}/,Vc=/[+-]?\d{6}/,Wc=/\d\d?/,Xc=/\d{1,3}/,Yc=/\d{1,4}/,Zc=/[+-]?\d{1,6}/,$c=/\d+/,_c=/[+-]?\d+/,ad=/Z|[+-]\d\d:?\d\d/gi,bd=/[+-]?\d+(\.\d{1,3})?/,cd=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,dd={},ed={},fd=0,gd=1,hd=2,id=3,jd=4,kd=5,ld=6;H("M",["MM",2],"Mo",function(){return this.month()+1}),H("MMM",0,0,function(a){return this.localeData().monthsShort(this,a)}),H("MMMM",0,0,function(a){return this.localeData().months(this,a)}),z("month","M"),N("M",Wc),N("MM",Wc,Sc),N("MMM",cd),N("MMMM",cd),Q(["M","MM"],function(a,b){b[gd]=q(a)-1}),Q(["MMM","MMMM"],function(a,b,c,d){var e=c._locale.monthsParse(a,d,c._strict);null!=e?b[gd]=e:j(c).invalidMonth=a});var md="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),nd="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),od={};a.suppressDeprecationWarnings=!1;var pd=/^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,qd=[["YYYYYY-MM-DD",/[+-]\d{6}-\d{2}-\d{2}/],["YYYY-MM-DD",/\d{4}-\d{2}-\d{2}/],["GGGG-[W]WW-E",/\d{4}-W\d{2}-\d/],["GGGG-[W]WW",/\d{4}-W\d{2}/],["YYYY-DDD",/\d{4}-\d{3}/]],rd=[["HH:mm:ss.SSSS",/(T| )\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],sd=/^\/?Date\((\-?\d+)/i;a.createFromInputFallback=aa("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(a){a._d=new Date(a._i+(a._useUTC?" UTC":""))}),H(0,["YY",2],0,function(){return this.year()%100}),H(0,["YYYY",4],0,"year"),H(0,["YYYYY",5],0,"year"),H(0,["YYYYYY",6,!0],0,"year"),z("year","y"),N("Y",_c),N("YY",Wc,Sc),N("YYYY",Yc,Uc),N("YYYYY",Zc,Vc),N("YYYYYY",Zc,Vc),Q(["YYYYY","YYYYYY"],fd),Q("YYYY",function(b,c){c[fd]=2===b.length?a.parseTwoDigitYear(b):q(b)}),Q("YY",function(b,c){c[fd]=a.parseTwoDigitYear(b)}),a.parseTwoDigitYear=function(a){return q(a)+(q(a)>68?1900:2e3)};var td=C("FullYear",!1);H("w",["ww",2],"wo","week"),H("W",["WW",2],"Wo","isoWeek"),z("week","w"),z("isoWeek","W"),N("w",Wc),N("ww",Wc,Sc),N("W",Wc),N("WW",Wc,Sc),R(["w","ww","W","WW"],function(a,b,c,d){b[d.substr(0,1)]=q(a)});var ud={dow:0,doy:6};H("DDD",["DDDD",3],"DDDo","dayOfYear"),z("dayOfYear","DDD"),N("DDD",Xc),N("DDDD",Tc),Q(["DDD","DDDD"],function(a,b,c){c._dayOfYear=q(a)}),a.ISO_8601=function(){};var vd=aa("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(){var a=Da.apply(null,arguments);return this>a?this:a}),wd=aa("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(){var a=Da.apply(null,arguments);return a>this?this:a});Ja("Z",":"),Ja("ZZ",""),N("Z",ad),N("ZZ",ad),Q(["Z","ZZ"],function(a,b,c){c._useUTC=!0,c._tzm=Ka(a)});var xd=/([\+\-]|\d\d)/gi;a.updateOffset=function(){};var yd=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,zd=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/;Ya.fn=Ha.prototype;var Ad=ab(1,"add"),Bd=ab(-1,"subtract");a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ";var Cd=aa("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(a){return void 0===a?this.localeData():this.locale(a)});H(0,["gg",2],0,function(){return this.weekYear()%100}),H(0,["GG",2],0,function(){return this.isoWeekYear()%100}),Db("gggg","weekYear"),Db("ggggg","weekYear"),Db("GGGG","isoWeekYear"),Db("GGGGG","isoWeekYear"),z("weekYear","gg"),z("isoWeekYear","GG"),N("G",_c),N("g",_c),N("GG",Wc,Sc),N("gg",Wc,Sc),N("GGGG",Yc,Uc),N("gggg",Yc,Uc),N("GGGGG",Zc,Vc),N("ggggg",Zc,Vc),R(["gggg","ggggg","GGGG","GGGGG"],function(a,b,c,d){b[d.substr(0,2)]=q(a)}),R(["gg","GG"],function(b,c,d,e){c[e]=a.parseTwoDigitYear(b)}),H("Q",0,0,"quarter"),z("quarter","Q"),N("Q",Rc),Q("Q",function(a,b){b[gd]=3*(q(a)-1)}),H("D",["DD",2],"Do","date"),z("date","D"),N("D",Wc),N("DD",Wc,Sc),N("Do",function(a,b){return a?b._ordinalParse:b._ordinalParseLenient}),Q(["D","DD"],hd),Q("Do",function(a,b){b[hd]=q(a.match(Wc)[0],10)});var Dd=C("Date",!0);H("d",0,"do","day"),H("dd",0,0,function(a){return this.localeData().weekdaysMin(this,a)}),H("ddd",0,0,function(a){return this.localeData().weekdaysShort(this,a)}),H("dddd",0,0,function(a){return this.localeData().weekdays(this,a)}),H("e",0,0,"weekday"),H("E",0,0,"isoWeekday"),z("day","d"),z("weekday","e"),z("isoWeekday","E"),N("d",Wc),N("e",Wc),N("E",Wc),N("dd",cd),N("ddd",cd),N("dddd",cd),R(["dd","ddd","dddd"],function(a,b,c){var d=c._locale.weekdaysParse(a);null!=d?b.d=d:j(c).invalidWeekday=a}),R(["d","e","E"],function(a,b,c,d){b[d]=q(a)});var Ed="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Fd="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Gd="Su_Mo_Tu_We_Th_Fr_Sa".split("_");H("H",["HH",2],0,"hour"),H("h",["hh",2],0,function(){return this.hours()%12||12}),Sb("a",!0),Sb("A",!1),z("hour","h"),N("a",Tb),N("A",Tb),N("H",Wc),N("h",Wc),N("HH",Wc,Sc),N("hh",Wc,Sc),Q(["H","HH"],id),Q(["a","A"],function(a,b,c){c._isPm=c._locale.isPM(a),c._meridiem=a}),Q(["h","hh"],function(a,b,c){b[id]=q(a),j(c).bigHour=!0});var Hd=/[ap]\.?m?\.?/i,Id=C("Hours",!0);H("m",["mm",2],0,"minute"),z("minute","m"),N("m",Wc),N("mm",Wc,Sc),Q(["m","mm"],jd);var Jd=C("Minutes",!1);H("s",["ss",2],0,"second"),z("second","s"),N("s",Wc),N("ss",Wc,Sc),Q(["s","ss"],kd);var Kd=C("Seconds",!1);H("S",0,0,function(){return~~(this.millisecond()/100)}),H(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),H(0,["SSS",3],0,"millisecond"),H(0,["SSSS",4],0,function(){return 10*this.millisecond()}),H(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),H(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),H(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),H(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),H(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),z("millisecond","ms"),N("S",Xc,Rc),N("SS",Xc,Sc),N("SSS",Xc,Tc);var Ld;for(Ld="SSSS";Ld.length<=9;Ld+="S")N(Ld,$c);for(Ld="S";Ld.length<=9;Ld+="S")Q(Ld,Wb);var Md=C("Milliseconds",!1);H("z",0,0,"zoneAbbr"),H("zz",0,0,"zoneName");var Nd=n.prototype;Nd.add=Ad,Nd.calendar=cb,Nd.clone=db,Nd.diff=ib,Nd.endOf=ub,Nd.format=mb,Nd.from=nb,Nd.fromNow=ob,Nd.to=pb,Nd.toNow=qb,Nd.get=F,Nd.invalidAt=Cb,Nd.isAfter=eb,Nd.isBefore=fb,Nd.isBetween=gb,Nd.isSame=hb,Nd.isValid=Ab,Nd.lang=Cd,Nd.locale=rb,Nd.localeData=sb,Nd.max=wd,Nd.min=vd,Nd.parsingFlags=Bb,Nd.set=F,Nd.startOf=tb,Nd.subtract=Bd,Nd.toArray=yb,Nd.toObject=zb,Nd.toDate=xb,Nd.toISOString=lb,Nd.toJSON=lb,Nd.toString=kb,Nd.unix=wb,Nd.valueOf=vb,Nd.year=td,Nd.isLeapYear=ia,Nd.weekYear=Fb,Nd.isoWeekYear=Gb,Nd.quarter=Nd.quarters=Jb,Nd.month=Y,Nd.daysInMonth=Z,Nd.week=Nd.weeks=na,Nd.isoWeek=Nd.isoWeeks=oa,Nd.weeksInYear=Ib,Nd.isoWeeksInYear=Hb,Nd.date=Dd,Nd.day=Nd.days=Pb,Nd.weekday=Qb,Nd.isoWeekday=Rb,Nd.dayOfYear=qa,Nd.hour=Nd.hours=Id,Nd.minute=Nd.minutes=Jd,Nd.second=Nd.seconds=Kd, +Nd.millisecond=Nd.milliseconds=Md,Nd.utcOffset=Na,Nd.utc=Pa,Nd.local=Qa,Nd.parseZone=Ra,Nd.hasAlignedHourOffset=Sa,Nd.isDST=Ta,Nd.isDSTShifted=Ua,Nd.isLocal=Va,Nd.isUtcOffset=Wa,Nd.isUtc=Xa,Nd.isUTC=Xa,Nd.zoneAbbr=Xb,Nd.zoneName=Yb,Nd.dates=aa("dates accessor is deprecated. Use date instead.",Dd),Nd.months=aa("months accessor is deprecated. Use month instead",Y),Nd.years=aa("years accessor is deprecated. Use year instead",td),Nd.zone=aa("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",Oa);var Od=Nd,Pd={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},Qd={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},Rd="Invalid date",Sd="%d",Td=/\d{1,2}/,Ud={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},Vd=s.prototype;Vd._calendar=Pd,Vd.calendar=_b,Vd._longDateFormat=Qd,Vd.longDateFormat=ac,Vd._invalidDate=Rd,Vd.invalidDate=bc,Vd._ordinal=Sd,Vd.ordinal=cc,Vd._ordinalParse=Td,Vd.preparse=dc,Vd.postformat=dc,Vd._relativeTime=Ud,Vd.relativeTime=ec,Vd.pastFuture=fc,Vd.set=gc,Vd.months=U,Vd._months=md,Vd.monthsShort=V,Vd._monthsShort=nd,Vd.monthsParse=W,Vd.week=ka,Vd._week=ud,Vd.firstDayOfYear=ma,Vd.firstDayOfWeek=la,Vd.weekdays=Lb,Vd._weekdays=Ed,Vd.weekdaysMin=Nb,Vd._weekdaysMin=Gd,Vd.weekdaysShort=Mb,Vd._weekdaysShort=Fd,Vd.weekdaysParse=Ob,Vd.isPM=Ub,Vd._meridiemParse=Hd,Vd.meridiem=Vb,w("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===q(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),a.lang=aa("moment.lang is deprecated. Use moment.locale instead.",w),a.langData=aa("moment.langData is deprecated. Use moment.localeData instead.",y);var Wd=Math.abs,Xd=yc("ms"),Yd=yc("s"),Zd=yc("m"),$d=yc("h"),_d=yc("d"),ae=yc("w"),be=yc("M"),ce=yc("y"),de=Ac("milliseconds"),ee=Ac("seconds"),fe=Ac("minutes"),ge=Ac("hours"),he=Ac("days"),ie=Ac("months"),je=Ac("years"),ke=Math.round,le={s:45,m:45,h:22,d:26,M:11},me=Math.abs,ne=Ha.prototype;ne.abs=oc,ne.add=qc,ne.subtract=rc,ne.as=wc,ne.asMilliseconds=Xd,ne.asSeconds=Yd,ne.asMinutes=Zd,ne.asHours=$d,ne.asDays=_d,ne.asWeeks=ae,ne.asMonths=be,ne.asYears=ce,ne.valueOf=xc,ne._bubble=tc,ne.get=zc,ne.milliseconds=de,ne.seconds=ee,ne.minutes=fe,ne.hours=ge,ne.days=he,ne.weeks=Bc,ne.months=ie,ne.years=je,ne.humanize=Fc,ne.toISOString=Gc,ne.toString=Gc,ne.toJSON=Gc,ne.locale=rb,ne.localeData=sb,ne.toIsoString=aa("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",Gc),ne.lang=Cd,H("X",0,0,"unix"),H("x",0,0,"valueOf"),N("x",_c),N("X",bd),Q("X",function(a,b,c){c._d=new Date(1e3*parseFloat(a,10))}),Q("x",function(a,b,c){c._d=new Date(q(a))}),a.version="2.10.6",b(Da),a.fn=Od,a.min=Fa,a.max=Ga,a.utc=h,a.unix=Zb,a.months=jc,a.isDate=d,a.locale=w,a.invalid=l,a.duration=Ya,a.isMoment=o,a.weekdays=lc,a.parseZone=$b,a.localeData=y,a.isDuration=Ia,a.monthsShort=kc,a.weekdaysMin=nc,a.defineLocale=x,a.weekdaysShort=mc,a.normalizeUnits=A,a.relativeTimeThreshold=Ec;var oe=a;return oe}); \ No newline at end of file diff --git a/apps/terminal/templates/terminal/session_detail.html b/apps/terminal/templates/terminal/session_detail.html index 8bec6c50c..b54eedec7 100644 --- a/apps/terminal/templates/terminal/session_detail.html +++ b/apps/terminal/templates/terminal/session_detail.html @@ -132,7 +132,7 @@ }, 300) } 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 () { $('.footable').footable(); diff --git a/apps/terminal/templates/terminal/session_list.html b/apps/terminal/templates/terminal/session_list.html index e9a992481..5dd2e24df 100644 --- a/apps/terminal/templates/terminal/session_list.html +++ b/apps/terminal/templates/terminal/session_list.html @@ -90,7 +90,7 @@ function terminateSession(data) { } var success_message = '{% trans "Terminate task send, waiting ..." %}'; var the_url = "{% url 'api-terminal:kill-session' %}"; - APIUpdateAttr({ + requestApi({ url: the_url, method: 'POST', body: JSON.stringify(data), @@ -174,7 +174,7 @@ function finishedSession(data) { var success = function() { location.reload(); }; - APIUpdateAttr({ + requestApi({ url: the_url, method: 'PATCH', body: JSON.stringify(data), diff --git a/apps/users/api/user.py b/apps/users/api/user.py index c79b9e865..ba8e48b25 100644 --- a/apps/users/api/user.py +++ b/apps/users/api/user.py @@ -14,7 +14,7 @@ from rest_framework.pagination import LimitOffsetPagination from common.permissions import ( IsOrgAdmin, IsCurrentUserOrReadOnly, IsOrgAdminOrAppUser, - CanUpdateSuperUser, + CanUpdateDeleteSuperUser, ) from common.mixins import IDInCacheFilterMixin from common.utils import get_logger @@ -38,7 +38,7 @@ class UserViewSet(IDInCacheFilterMixin, BulkModelViewSet): search_fields = filter_fields queryset = User.objects.exclude(role=User.ROLE_APP) serializer_class = UserSerializer - permission_classes = (IsOrgAdmin, CanUpdateSuperUser) + permission_classes = (IsOrgAdmin, CanUpdateDeleteSuperUser) pagination_class = LimitOffsetPagination def send_created_signal(self, users): diff --git a/apps/users/templates/users/_granted_assets.html b/apps/users/templates/users/_granted_assets.html new file mode 100644 index 000000000..36cc27a1c --- /dev/null +++ b/apps/users/templates/users/_granted_assets.html @@ -0,0 +1,165 @@ +{% load i18n %} +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+ + + + + + + + {% if show_actions %} + + {% endif %} + + + + +
{% trans 'Hostname' %}{% trans 'IP' %}{% trans 'System user' %}{% trans 'Action' %}
+
+
+ diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index 351823a9e..929d7def4 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -280,7 +280,7 @@ function updateUserGroups(groups) { // clear jumpserver.groups_selected jumpserver.nodes_selected = {}; }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success @@ -305,7 +305,7 @@ $(document).ready(function() { 'is_active': checked }; var success = '{% trans "Update successfully!" %}'; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success_message: success @@ -332,7 +332,7 @@ $(document).ready(function() { 'otp_secret_key': otp_secret_key }; var success = '{% trans "Update successfully!" %}'; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success_message: success @@ -372,7 +372,7 @@ $(document).ready(function() { var msg = "{% trans "An e-mail has been sent to the user`s mailbox." %}"; swal("{% trans 'Reset password' %}", msg, "success"); }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), 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.' %}"; swal("{% trans 'Reset SSH public key' %}", msg, "success"); }; - APIUpdateAttr({ + requestApi({ url: the_url, body: body, 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 () { var $this = $(this); var name = "{{ user_object.name }}"; @@ -466,7 +466,7 @@ $(document).ready(function() { } ); }; - APIUpdateAttr({ + requestApi({ url: the_url, body: JSON.stringify(body), success: success @@ -485,7 +485,7 @@ $(document).ready(function() { doReset(); }); }).on('click', '#btn-reset-mfa', function () { - APIUpdateAttr({ + requestApi({ url: "{% url 'api-users:user-reset-otp' pk=user_object.id %}", method: "GET", success_message: "{% trans 'Reset user MFA success' %}" diff --git a/apps/users/templates/users/user_granted_asset.html b/apps/users/templates/users/user_granted_asset.html index 225005ffd..fc5197a77 100644 --- a/apps/users/templates/users/user_granted_asset.html +++ b/apps/users/templates/users/user_granted_asset.html @@ -23,33 +23,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - - - - - - - -
{% trans 'Hostname' %}{% trans 'IP' %}{% trans 'System users' %}
-
-
+ {% include 'users/_granted_assets.html' %}
@@ -58,81 +32,10 @@ {% endblock %} {% block custom_foot_js %}