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/serializers/domain.py b/apps/assets/serializers/domain.py index 68145e7a9..68feccb41 100644 --- a/apps/assets/serializers/domain.py +++ b/apps/assets/serializers/domain.py @@ -15,7 +15,11 @@ class DomainSerializer(BulkOrgResourceModelSerializer): class Meta: model = Domain - fields = '__all__' + fields = [ + 'id', 'name', 'asset_count', 'gateway_count', 'comment', 'assets', + 'date_created' + ] + read_only_fields = ( 'asset_count', 'gateway_count', 'date_created') list_serializer_class = AdaptedBulkListSerializer @staticmethod diff --git a/apps/assets/serializers/label.py b/apps/assets/serializers/label.py index 526580216..a20c43a11 100644 --- a/apps/assets/serializers/label.py +++ b/apps/assets/serializers/label.py @@ -13,7 +13,13 @@ class LabelSerializer(BulkOrgResourceModelSerializer): class Meta: model = Label - fields = '__all__' + fields = [ + 'id', 'name', 'value', 'category', 'is_active', 'comment', + 'date_created', 'asset_count', 'assets', 'get_category_display' + ] + read_only_fields = ( + 'category', 'date_created', 'asset_count', 'get_category_display' + ) list_serializer_class = AdaptedBulkListSerializer @staticmethod 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/_system_user.html b/apps/assets/templates/assets/_system_user.html index ef78187b5..5cc3e04ba 100644 --- a/apps/assets/templates/assets/_system_user.html +++ b/apps/assets/templates/assets/_system_user.html @@ -228,6 +228,7 @@ $(document).ready(function () { var form = $("form"); var data = form.serializeObject(); + objectAttrsIsList(data, ['cmd_filters']); objectAttrsIsBool(data, ["auto_generate_key", "auto_push"]); data["private_key"] = $("#id_private_key_file").data('file'); 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/cmd_filter_rule_create_update.html b/apps/assets/templates/assets/cmd_filter_rule_create_update.html index 9b240bd74..2edaa97dc 100644 --- a/apps/assets/templates/assets/cmd_filter_rule_create_update.html +++ b/apps/assets/templates/assets/cmd_filter_rule_create_update.html @@ -70,5 +70,25 @@ $(document).ready(function(){ content_help_ref.html(content_origin_help_text); } }) +.on("submit", "form", function (evt) { + evt.preventDefault(); + var form = $("form"); + var data = form.serializeObject(); + var the_url = '{% url "api-assets:cmd-filter-rule-list" filter_pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", data.filter); + var redirect_to = '{% url "assets:cmd-filter-rule-list" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", data.filter); + var method = "POST"; + {% if request_type == "update" %} + the_url = '{% url "api-assets:cmd-filter-rule-detail" filter_pk=DEFAULT_PK pk=rule.id %}'.replace('{{ DEFAULT_PK }}', data.filter); + method = "PUT"; + {% endif %} + var props = { + url: the_url, + data: data, + method: method, + form: form, + redirect_to: redirect_to + }; + formSubmit(props); +}) {% endblock %} \ No newline at end of file diff --git a/apps/assets/templates/assets/domain_create_update.html b/apps/assets/templates/assets/domain_create_update.html index 7a31e3e88..399b38011 100644 --- a/apps/assets/templates/assets/domain_create_update.html +++ b/apps/assets/templates/assets/domain_create_update.html @@ -48,5 +48,26 @@ $(document).ready(function () { $("#asset_list_modal").modal('hide'); }) +.on("submit", "form", function (evt) { + evt.preventDefault(); + var form = $("form"); + var data = form.serializeObject(); + var method = "POST"; + var the_url = '{% url "api-assets:domain-list" %}'; + var redirect_to = '{% url "assets:domain-list" %}'; + {% if type == "update" %} + the_url = '{% url 'api-assets:domain-detail' pk=object.id %}'; + method = "PUT"; + {% endif %} + objectAttrsIsList(data, ['assets']); + var props = { + url:the_url, + data:data, + method:method, + form:form, + redirect_to:redirect_to + }; + formSubmit(props); + }) {% endblock %} \ No newline at end of file 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/label_create_update.html b/apps/assets/templates/assets/label_create_update.html index d55bb8827..a6b9582a5 100644 --- a/apps/assets/templates/assets/label_create_update.html +++ b/apps/assets/templates/assets/label_create_update.html @@ -51,5 +51,26 @@ $(document).ready(function () { $('#id_assets').val(assets).trigger('change'); $("#asset_list_modal").modal('hide'); }) +.on("submit", "form", function (evt) { + evt.preventDefault(); + var the_url = '{% url 'api-assets:label-list' %}'; + var redirect_to = '{% url "assets:label-list" %}'; + var method = "POST"; + {% if type == "update" %} + the_url = '{% url 'api-assets:label-detail' pk=object.id %}'; + method = "PUT"; + {% endif %} + var form = $("form"); + var data = form.serializeObject(); + objectAttrsIsList(data, ['assets']); + var props = { + url: the_url, + data: data, + method: method, + form: form, + redirect_to: redirect_to + }; + formSubmit(props); +}) {% endblock %} \ No newline at end of file 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/assets/views/cmd_filter.py b/apps/assets/views/cmd_filter.py index 7eef5980a..530f4193b 100644 --- a/apps/assets/views/cmd_filter.py +++ b/apps/assets/views/cmd_filter.py @@ -138,6 +138,7 @@ class CommandFilterRuleCreateView(PermissionsMixin, CreateView): 'app': _('Assets'), 'action': _('Create command filter rule'), 'object': self.cmd_filter, + 'request_type': 'create' } kwargs.update(context) return super().get_context_data(**kwargs) @@ -172,6 +173,8 @@ class CommandFilterRuleUpdateView(PermissionsMixin, UpdateView): 'app': _('Assets'), 'action': _('Update command filter rule'), 'object': self.cmd_filter, + 'rule': self.get_object(), + 'request_type': 'update' } kwargs.update(context) return super().get_context_data(**kwargs) \ No newline at end of file diff --git a/apps/assets/views/domain.py b/apps/assets/views/domain.py index 6bbf09e25..7b4dcfcce 100644 --- a/apps/assets/views/domain.py +++ b/apps/assets/views/domain.py @@ -46,6 +46,7 @@ class DomainCreateView(PermissionsMixin, CreateView): context = { 'app': _('Assets'), 'action': _('Create domain'), + 'type': 'create' } kwargs.update(context) return super().get_context_data(**kwargs) @@ -63,6 +64,7 @@ class DomainUpdateView(PermissionsMixin, UpdateView): context = { 'app': _('Assets'), 'action': _('Update domain'), + 'type': 'update' } kwargs.update(context) return super().get_context_data(**kwargs) diff --git a/apps/assets/views/label.py b/apps/assets/views/label.py index b53a5d040..522962ce3 100644 --- a/apps/assets/views/label.py +++ b/apps/assets/views/label.py @@ -44,6 +44,7 @@ class LabelCreateView(PermissionsMixin, CreateView): context = { 'app': _('Assets'), 'action': _('Create label'), + 'type': 'create' } kwargs.update(context) return super().get_context_data(**kwargs) @@ -71,6 +72,7 @@ class LabelUpdateView(PermissionsMixin, UpdateView): context = { 'app': _('Assets'), 'action': _('Update label'), + 'type': 'update' } 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/asset_permission.py b/apps/perms/api/asset_permission.py index 38e44abd0..d4f6e7c85 100644 --- a/apps/perms/api/asset_permission.py +++ b/apps/perms/api/asset_permission.py @@ -4,7 +4,8 @@ from django.utils import timezone from django.db.models import Q from rest_framework.views import Response -from rest_framework.generics import RetrieveUpdateAPIView +from django.shortcuts import get_object_or_404 +from rest_framework.generics import RetrieveUpdateAPIView, ListAPIView from rest_framework import viewsets from rest_framework.pagination import LimitOffsetPagination @@ -20,7 +21,7 @@ from .. import serializers __all__ = [ 'AssetPermissionViewSet', 'AssetPermissionRemoveUserApi', 'AssetPermissionAddUserApi', 'AssetPermissionRemoveAssetApi', - 'AssetPermissionAddAssetApi', + 'AssetPermissionAddAssetApi', 'AssetPermissionAssetsApi', ] @@ -232,3 +233,22 @@ class AssetPermissionAddAssetApi(RetrieveUpdateAPIView): return Response({"msg": "ok"}) else: return Response({"error": serializer.errors}) + + +class AssetPermissionAssetsApi(ListAPIView): + permission_classes = (IsOrgAdmin,) + pagination_class = LimitOffsetPagination + serializer_class = serializers.AssetPermissionAssetsSerializer + filter_fields = ("hostname", "ip") + search_fields = filter_fields + + def get_object(self): + pk = self.kwargs.get('pk') + return get_object_or_404(AssetPermission, pk=pk) + + def get_queryset(self): + perm = self.get_object() + assets = perm.get_all_assets().only( + *self.serializer_class.Meta.only_fields + ) + return assets 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/models/asset_permission.py b/apps/perms/models/asset_permission.py index cb3e37768..b10d33d6b 100644 --- a/apps/perms/models/asset_permission.py +++ b/apps/perms/models/asset_permission.py @@ -2,6 +2,7 @@ import uuid from functools import reduce from django.db import models +from django.db.models import Q from django.utils.translation import ugettext_lazy as _ from common.utils import date_expired_default, set_or_append_attr_bulk @@ -93,11 +94,16 @@ class AssetPermission(BasePermission): ) def get_all_assets(self): - assets = set(self.assets.all()) - for node in self.nodes.all(): - _assets = node.get_all_assets() - set_or_append_attr_bulk(_assets, 'inherit', node.value) - assets.update(set(_assets)) + args = [Q(granted_by_permissions=self)] + pattern = set() + nodes_keys = self.nodes.all().values_list('key', flat=True) + for key in nodes_keys: + pattern.add(r'^{0}$|^{0}:'.format(key)) + pattern = '|'.join(list(pattern)) + if pattern: + args.append(Q(nodes__key__regex=pattern)) + args = reduce(lambda x, y: x | y, args) + assets = Asset.objects.filter(args) return assets diff --git a/apps/perms/serializers/asset_permission.py b/apps/perms/serializers/asset_permission.py index ecbed669b..7bc145d9f 100644 --- a/apps/perms/serializers/asset_permission.py +++ b/apps/perms/serializers/asset_permission.py @@ -6,11 +6,12 @@ from rest_framework import serializers from common.fields import StringManyToManyField from orgs.mixins import BulkOrgResourceModelSerializer from perms.models import AssetPermission, Action +from assets.models import Asset __all__ = [ 'AssetPermissionCreateUpdateSerializer', 'AssetPermissionListSerializer', 'AssetPermissionUpdateUserSerializer', 'AssetPermissionUpdateAssetSerializer', - 'ActionsField', + 'ActionsField', 'AssetPermissionAssetsSerializer', ] @@ -70,3 +71,11 @@ class AssetPermissionUpdateAssetSerializer(serializers.ModelSerializer): class Meta: model = AssetPermission fields = ['id', 'assets'] + + +class AssetPermissionAssetsSerializer(serializers.ModelSerializer): + + class Meta: + model = Asset + only_fields = ['id', 'hostname', 'ip'] + fields = tuple(only_fields) diff --git a/apps/perms/templates/perms/asset_permission_asset.html b/apps/perms/templates/perms/asset_permission_asset.html index e774692f3..d8cd8ee96 100644 --- a/apps/perms/templates/perms/asset_permission_asset.html +++ b/apps/perms/templates/perms/asset_permission_asset.html @@ -48,29 +48,19 @@
- +
- - - - - + + + + + - {% for asset in object_list %} - - - - - - {% endfor %}
{% trans 'Hostname' %}{% trans 'IP' %}
+ + {% trans 'Hostname' %}{% trans 'IP' %}
{{ asset.hostname }}{{ asset.ip }} - -
-
- {% include '_pagination.html' %} -
@@ -86,9 +76,6 @@ @@ -146,6 +133,7 @@ +{% include 'assets/_asset_list_modal.html' %} {% endblock %} {% block custom_foot_js %} diff --git a/apps/users/templates/users/user_create.html b/apps/users/templates/users/user_create.html index 13bb26cbc..5e15b5469 100644 --- a/apps/users/templates/users/user_create.html +++ b/apps/users/templates/users/user_create.html @@ -4,9 +4,7 @@ {% block user_template_title %}{% trans "Create user" %}{% endblock %} {% block password %} {% bootstrap_field form.password_strategy layout="horizontal" %} -
- {% bootstrap_field form.password layout="horizontal" %} -
+ {% bootstrap_field form.password layout="horizontal" %} {# 密码popover #}
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - - - - - - - -
{% trans 'Hostname' %}{% trans 'IP' %}{% trans 'System users' %}
-
-
+ {% include 'users/_granted_assets.html' %}
@@ -58,81 +32,10 @@ {% endblock %} {% block custom_foot_js %} {% endblock %}