From 10616b8d9edbf37e06d1b5c0a4e916162ba2b550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=81=E5=B9=BF?= Date: Wed, 19 Jun 2019 16:45:14 +0800 Subject: [PATCH] =?UTF-8?q?[Update]=20serializer=20mixin=E7=BB=A7=E6=89=BF?= =?UTF-8?q?=20(#2810)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Update] serializer mixin继承 * [Update] 修改system user更新serialzier * [Update] 修改success message --- apps/applications/serializers/remote_app.py | 4 ++-- apps/assets/serializers/admin_user.py | 5 ++-- apps/assets/serializers/asset.py | 24 +++++++++---------- apps/assets/serializers/asset_user.py | 5 ++-- apps/assets/serializers/cmd_filter.py | 5 ++-- apps/assets/serializers/domain.py | 7 +++--- apps/assets/serializers/label.py | 5 ++-- apps/assets/serializers/node.py | 3 ++- apps/assets/serializers/system_user.py | 18 ++++---------- apps/assets/signals_handler.py | 1 + .../templates/assets/_asset_list_modal.html | 1 + apps/assets/views/asset.py | 1 - apps/authentication/backends/api.py | 24 +++++++++++++++---- apps/common/mixins.py | 2 ++ apps/orgs/middleware.py | 15 ++++++++++++ apps/orgs/mixins.py | 17 ++++++++++++- apps/perms/serializers/asset_permission.py | 5 ++-- .../serializers/remote_app_permission.py | 7 ++++-- apps/terminal/views/terminal.py | 1 - apps/users/serializers/v1.py | 3 ++- 20 files changed, 102 insertions(+), 51 deletions(-) diff --git a/apps/applications/serializers/remote_app.py b/apps/applications/serializers/remote_app.py index 6957d11d5..9b5c56315 100644 --- a/apps/applications/serializers/remote_app.py +++ b/apps/applications/serializers/remote_app.py @@ -4,8 +4,8 @@ from rest_framework import serializers -from common.mixins import BulkSerializerMixin from common.serializers import AdaptedBulkListSerializer +from orgs.mixins import BulkOrgResourceModelSerializer from .. import const from ..models import RemoteApp @@ -66,7 +66,7 @@ class RemoteAppParamsDictField(serializers.DictField): return value -class RemoteAppSerializer(BulkSerializerMixin, serializers.ModelSerializer): +class RemoteAppSerializer(BulkOrgResourceModelSerializer): params = RemoteAppParamsDictField() class Meta: diff --git a/apps/assets/serializers/admin_user.py b/apps/assets/serializers/admin_user.py index 66f25db87..765559e70 100644 --- a/apps/assets/serializers/admin_user.py +++ b/apps/assets/serializers/admin_user.py @@ -8,11 +8,12 @@ from common.serializers import AdaptedBulkListSerializer from ..models import Node, AdminUser from ..const import ADMIN_USER_CONN_CACHE_KEY +from orgs.mixins import BulkOrgResourceModelSerializer from .base import AuthSerializer -class AdminUserSerializer(serializers.ModelSerializer): +class AdminUserSerializer(BulkOrgResourceModelSerializer): """ 管理用户 """ @@ -27,7 +28,7 @@ class AdminUserSerializer(serializers.ModelSerializer): list_serializer_class = AdaptedBulkListSerializer model = AdminUser fields = [ - 'id', 'org_id', 'name', 'username', 'assets_amount', + 'id', 'name', 'username', 'assets_amount', 'reachable_amount', 'unreachable_amount', 'password', 'comment', 'date_created', 'date_updated', 'become', 'become_method', 'become_user', 'created_by', diff --git a/apps/assets/serializers/asset.py b/apps/assets/serializers/asset.py index 1a493d456..0298f776c 100644 --- a/apps/assets/serializers/asset.py +++ b/apps/assets/serializers/asset.py @@ -5,8 +5,7 @@ from rest_framework.validators import ValidationError from django.utils.translation import ugettext_lazy as _ -from orgs.mixins import OrgResourceSerializerMixin -from common.mixins import BulkSerializerMixin +from orgs.mixins import BulkOrgResourceModelSerializer from common.serializers import AdaptedBulkListSerializer from ..models import Asset, Protocol from .system_user import AssetSystemUserSerializer @@ -23,8 +22,7 @@ class ProtocolSerializer(serializers.ModelSerializer): fields = ["name", "port"] -class AssetSerializer(BulkSerializerMixin, OrgResourceSerializerMixin, - serializers.ModelSerializer): +class AssetSerializer(BulkOrgResourceModelSerializer): protocols = ProtocolSerializer(many=True) """ @@ -34,7 +32,7 @@ class AssetSerializer(BulkSerializerMixin, OrgResourceSerializerMixin, model = Asset list_serializer_class = AdaptedBulkListSerializer fields = [ - 'id', 'org_id', 'org_name', 'ip', 'hostname', 'protocol', 'port', + 'id', 'ip', 'hostname', 'protocol', 'port', 'protocols', 'platform', 'is_active', 'public_ip', 'domain', 'admin_user', 'nodes', 'labels', 'number', 'vendor', 'model', 'sn', 'cpu_model', 'cpu_count', 'cpu_cores', 'cpu_vcpus', 'memory', @@ -93,7 +91,7 @@ class AssetSerializer(BulkSerializerMixin, OrgResourceSerializerMixin, return instance def update(self, instance, validated_data): - protocols_data = validated_data.pop("protocols") + protocols_data = validated_data.pop("protocols", None) # 兼容老的api protocol = validated_data.get("protocol") @@ -104,14 +102,16 @@ class AssetSerializer(BulkSerializerMixin, OrgResourceSerializerMixin, if not protocol and not port and protocols_data: validated_data["protocol"] = protocols_data[0]["name"] validated_data["port"] = protocols_data[0]["port"] - - protocols_serializer = ProtocolSerializer(data=protocols_data, many=True) - protocols_serializer.is_valid(raise_exception=True) - protocols = protocols_serializer.save() + protocols = None + if protocols_data: + protocols_serializer = ProtocolSerializer(data=protocols_data, many=True) + protocols_serializer.is_valid(raise_exception=True) + protocols = protocols_serializer.save() instance = super().update(instance, validated_data) - instance.protocols.all().delete() - instance.protocols.set(protocols) + if protocols: + instance.protocols.all().delete() + instance.protocols.set(protocols) return instance diff --git a/apps/assets/serializers/asset_user.py b/apps/assets/serializers/asset_user.py index 517a1e9c3..fe04b675a 100644 --- a/apps/assets/serializers/asset_user.py +++ b/apps/assets/serializers/asset_user.py @@ -9,6 +9,7 @@ from ..backends import AssetUserManager from common.utils import validate_ssh_private_key from common.mixins import BulkSerializerMixin from common.serializers import AdaptedBulkListSerializer +from orgs.mixins import BulkOrgResourceModelSerializer __all__ = [ @@ -23,7 +24,7 @@ class BasicAssetSerializer(serializers.ModelSerializer): fields = ['hostname', 'ip'] -class AssetUserSerializer(BulkSerializerMixin, serializers.ModelSerializer): +class AssetUserSerializer(BulkOrgResourceModelSerializer): hostname = serializers.CharField(read_only=True, label=_("Hostname")) ip = serializers.CharField(read_only=True, label=_("IP")) connectivity = serializers.CharField(read_only=True, label=_("Connectivity")) @@ -51,7 +52,7 @@ class AssetUserSerializer(BulkSerializerMixin, serializers.ModelSerializer): ) fields = [ "id", "hostname", "ip", "username", "password", "asset", "version", - "is_latest", "connectivity", "backend", "org_id", + "is_latest", "connectivity", "backend", "date_created", "date_updated", "private_key", "public_key", ] extra_kwargs = { diff --git a/apps/assets/serializers/cmd_filter.py b/apps/assets/serializers/cmd_filter.py index 26040f6aa..b523a1779 100644 --- a/apps/assets/serializers/cmd_filter.py +++ b/apps/assets/serializers/cmd_filter.py @@ -5,9 +5,10 @@ from rest_framework import serializers from common.fields import ChoiceDisplayField from common.serializers import AdaptedBulkListSerializer from ..models import CommandFilter, CommandFilterRule, SystemUser +from orgs.mixins import BulkOrgResourceModelSerializer -class CommandFilterSerializer(serializers.ModelSerializer): +class CommandFilterSerializer(BulkOrgResourceModelSerializer): rules = serializers.PrimaryKeyRelatedField(queryset=CommandFilterRule.objects.all(), many=True) system_users = serializers.PrimaryKeyRelatedField(queryset=SystemUser.objects.all(), many=True) @@ -17,7 +18,7 @@ class CommandFilterSerializer(serializers.ModelSerializer): fields = '__all__' -class CommandFilterRuleSerializer(serializers.ModelSerializer): +class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer): serializer_choice_field = ChoiceDisplayField class Meta: diff --git a/apps/assets/serializers/domain.py b/apps/assets/serializers/domain.py index 553911eb8..cda208b9f 100644 --- a/apps/assets/serializers/domain.py +++ b/apps/assets/serializers/domain.py @@ -3,11 +3,12 @@ from rest_framework import serializers from common.serializers import AdaptedBulkListSerializer +from orgs.mixins import BulkOrgResourceModelSerializer from ..models import Domain, Gateway -class DomainSerializer(serializers.ModelSerializer): +class DomainSerializer(BulkOrgResourceModelSerializer): asset_count = serializers.SerializerMethodField() gateway_count = serializers.SerializerMethodField() @@ -25,7 +26,7 @@ class DomainSerializer(serializers.ModelSerializer): return obj.gateway_set.all().count() -class GatewaySerializer(serializers.ModelSerializer): +class GatewaySerializer(BulkOrgResourceModelSerializer): class Meta: model = Gateway list_serializer_class = AdaptedBulkListSerializer @@ -45,7 +46,7 @@ class GatewayWithAuthSerializer(GatewaySerializer): return fields -class DomainWithGatewaySerializer(serializers.ModelSerializer): +class DomainWithGatewaySerializer(BulkOrgResourceModelSerializer): gateways = GatewayWithAuthSerializer(many=True, read_only=True) class Meta: diff --git a/apps/assets/serializers/label.py b/apps/assets/serializers/label.py index 9fbc9e804..526580216 100644 --- a/apps/assets/serializers/label.py +++ b/apps/assets/serializers/label.py @@ -3,11 +3,12 @@ from rest_framework import serializers from common.serializers import AdaptedBulkListSerializer +from orgs.mixins import BulkOrgResourceModelSerializer from ..models import Label -class LabelSerializer(serializers.ModelSerializer): +class LabelSerializer(BulkOrgResourceModelSerializer): asset_count = serializers.SerializerMethodField() class Meta: @@ -25,7 +26,7 @@ class LabelSerializer(serializers.ModelSerializer): return fields -class LabelDistinctSerializer(serializers.ModelSerializer): +class LabelDistinctSerializer(BulkOrgResourceModelSerializer): value = serializers.SerializerMethodField() class Meta: diff --git a/apps/assets/serializers/node.py b/apps/assets/serializers/node.py index f0ff3062b..61df12f64 100644 --- a/apps/assets/serializers/node.py +++ b/apps/assets/serializers/node.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from rest_framework import serializers +from orgs.mixins import BulkOrgResourceModelSerializer from ..models import Asset, Node @@ -10,7 +11,7 @@ __all__ = [ ] -class NodeSerializer(serializers.ModelSerializer): +class NodeSerializer(BulkOrgResourceModelSerializer): assets_amount = serializers.IntegerField(read_only=True) class Meta: diff --git a/apps/assets/serializers/system_user.py b/apps/assets/serializers/system_user.py index 88ae6eb38..c4cb5be05 100644 --- a/apps/assets/serializers/system_user.py +++ b/apps/assets/serializers/system_user.py @@ -3,12 +3,12 @@ from rest_framework import serializers from django.utils.translation import ugettext_lazy as _ from common.serializers import AdaptedBulkListSerializer - -from ..models import SystemUser, Asset +from orgs.mixins import BulkOrgResourceModelSerializer +from ..models import SystemUser from .base import AuthSerializer -class SystemUserSerializer(serializers.ModelSerializer): +class SystemUserSerializer(BulkOrgResourceModelSerializer): """ 系统用户 """ @@ -31,7 +31,7 @@ class SystemUserSerializer(serializers.ModelSerializer): model = SystemUser list_serializer_class = AdaptedBulkListSerializer fields = [ - 'id', 'org_id', 'name', 'username', 'login_mode', + 'id', 'name', 'username', 'login_mode', 'login_mode_display', 'login_mode_display', 'priority', 'protocol', 'auto_push', 'password', 'assets_amount', 'reachable_amount', 'reachable_assets', 'unreachable_amount', 'unreachable_assets', 'cmd_filters', 'sudo', @@ -39,17 +39,9 @@ class SystemUserSerializer(serializers.ModelSerializer): ] extra_kwargs = { 'login_mode_display': {'label': _('Login mode display')}, - 'created_by': {'read_only': True}, 'nodes': {'read_only': True}, - 'assets': {'read_only': True} + 'created_by': {'read_only': True}, } - def get_field_names(self, declared_fields, info): - fields = super(SystemUserSerializer, self).get_field_names(declared_fields, info) - fields.extend([ - 'login_mode_display', - ]) - return fields - @staticmethod def get_unreachable_assets(obj): return obj.assets_unreachable diff --git a/apps/assets/signals_handler.py b/apps/assets/signals_handler.py index 23e923a3e..a145437d0 100644 --- a/apps/assets/signals_handler.py +++ b/apps/assets/signals_handler.py @@ -65,6 +65,7 @@ def on_system_user_update(sender, instance=None, created=True, **kwargs): @receiver(m2m_changed, sender=SystemUser.nodes.through) def on_system_user_nodes_change(sender, instance=None, **kwargs): if instance and kwargs["action"] == "post_add": + logger.info("System user `{}` nodes update signal received".format(instance)) assets = set() nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set']) for node in nodes: diff --git a/apps/assets/templates/assets/_asset_list_modal.html b/apps/assets/templates/assets/_asset_list_modal.html index d62dc7a59..4abfa0587 100644 --- a/apps/assets/templates/assets/_asset_list_modal.html +++ b/apps/assets/templates/assets/_asset_list_modal.html @@ -76,6 +76,7 @@ function initTable2() { function onNodeSelected2(event, treeNode) { var url = asset_table2.ajax.url(); url = setUrlParam(url, "node_id", treeNode.meta.node.id); + url = setUrlParam(url, "show_current_asset", ""); asset_table2.ajax.url(url); asset_table2.ajax.reload(); } diff --git a/apps/assets/views/asset.py b/apps/assets/views/asset.py index 72ead3ca3..aef420171 100644 --- a/apps/assets/views/asset.py +++ b/apps/assets/views/asset.py @@ -20,7 +20,6 @@ from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator from django.core.cache import cache from django.utils import timezone -from django.contrib.auth.mixins import LoginRequiredMixin from django.shortcuts import redirect from django.contrib.messages.views import SuccessMessageMixin from django.forms.formsets import formset_factory diff --git a/apps/authentication/backends/api.py b/apps/authentication/backends/api.py index 75b9bdb3f..2b1cc0b5d 100644 --- a/apps/authentication/backends/api.py +++ b/apps/authentication/backends/api.py @@ -147,7 +147,23 @@ class PrivateTokenAuthentication(authentication.TokenAuthentication): class SessionAuthentication(authentication.SessionAuthentication): - def enforce_csrf(self, request): - reason = CSRFCheck().process_view(request, None, (), {}) - if reason: - raise exceptions.AuthenticationFailed(reason) + def authenticate(self, request): + """ + Returns a `User` if the request session currently has a logged in user. + Otherwise returns `None`. + """ + + # Get the session-based user from the underlying HttpRequest object + user = getattr(request._request, 'user', None) + + # Unauthenticated, CSRF validation not required + if not user or not user.is_active: + return None + + try: + self.enforce_csrf(request) + except exceptions.AuthenticationFailed: + return None + + # CSRF passed with authenticated user + return user, None diff --git a/apps/common/mixins.py b/apps/common/mixins.py index 12f147689..60d995d52 100644 --- a/apps/common/mixins.py +++ b/apps/common/mixins.py @@ -211,6 +211,8 @@ class ApiMessageMixin: _action_map = {"create": _("create"), "update": _("update")} def get_success_message(self, cleaned_data): + if not isinstance(cleaned_data, dict): + return '' data = {k: v for k, v in cleaned_data.items()} action = getattr(self, "action", "create") data["action"] = self._action_map.get(action) diff --git a/apps/orgs/middleware.py b/apps/orgs/middleware.py index 04a2d6f7a..5338735f3 100644 --- a/apps/orgs/middleware.py +++ b/apps/orgs/middleware.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # +from .models import Organization from .utils import get_org_from_request, set_current_org @@ -8,7 +9,21 @@ class OrgMiddleware: def __init__(self, get_response): self.get_response = get_response + @staticmethod + def set_permed_org_if_need(request): + if request.content_type != "text/plain": + return + if not (request.user.is_authenticated and request.user.is_org_admin): + return + org = get_org_from_request(request) + if org.can_admin_by(request.user): + return + admin_orgs = Organization.get_user_admin_orgs(request.user) + if admin_orgs: + request.session['oid'] = str(admin_orgs[0].id) + def __call__(self, request): + self.set_permed_org_if_need(request) org = get_org_from_request(request) request.current_org = org set_current_org(org) diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index f6edd545e..660d36beb 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -13,6 +13,7 @@ from rest_framework.validators import UniqueTogetherValidator from common.utils import get_logger from common.validators import ProjectUniqueValidator +from common.mixins import BulkSerializerMixin from .utils import ( current_org, set_current_org, set_to_root_org, get_current_org_id ) @@ -25,6 +26,7 @@ __all__ = [ 'OrgManager', 'OrgViewGenericMixin', 'OrgModelMixin', 'OrgModelForm', 'RootOrgViewMixin', 'OrgMembershipSerializerMixin', 'OrgMembershipModelViewSetMixin', 'OrgResourceSerializerMixin', + 'BulkOrgResourceSerializerMixin', 'BulkOrgResourceModelSerializer', ] @@ -217,7 +219,7 @@ class OrgResourceSerializerMixin(serializers.Serializer): 由于HiddenField字段不可读,API获取资产信息时获取不到org_id, 但是coco需要资产的org_id字段,所以修改为CharField类型 """ - org_id = serializers.CharField(default=get_current_org_id) + org_id = serializers.ReadOnlyField(default=get_current_org_id) def get_validators(self): _validators = super().get_validators() @@ -229,3 +231,16 @@ class OrgResourceSerializerMixin(serializers.Serializer): v = ProjectUniqueValidator(v.queryset, v.fields) validators.append(v) return validators + + def get_field_names(self, declared_fields, info): + fields = super().get_field_names(declared_fields, info) + fields.extend(["org_id", "org_name"]) + return fields + + +class BulkOrgResourceSerializerMixin(BulkSerializerMixin, OrgResourceSerializerMixin): + pass + + +class BulkOrgResourceModelSerializer(BulkOrgResourceSerializerMixin, serializers.ModelSerializer): + pass diff --git a/apps/perms/serializers/asset_permission.py b/apps/perms/serializers/asset_permission.py index f82322d78..842c719e8 100644 --- a/apps/perms/serializers/asset_permission.py +++ b/apps/perms/serializers/asset_permission.py @@ -4,6 +4,7 @@ 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 Node from assets.serializers import AssetGrantedSerializer @@ -22,13 +23,13 @@ class ActionSerializer(serializers.ModelSerializer): fields = '__all__' -class AssetPermissionCreateUpdateSerializer(serializers.ModelSerializer): +class AssetPermissionCreateUpdateSerializer(BulkOrgResourceModelSerializer): class Meta: model = AssetPermission exclude = ('created_by', 'date_created') -class AssetPermissionListSerializer(serializers.ModelSerializer): +class AssetPermissionListSerializer(BulkOrgResourceModelSerializer): users = StringManyToManyField(many=True, read_only=True) user_groups = StringManyToManyField(many=True, read_only=True) assets = StringManyToManyField(many=True, read_only=True) diff --git a/apps/perms/serializers/remote_app_permission.py b/apps/perms/serializers/remote_app_permission.py index bb95d910c..727076e77 100644 --- a/apps/perms/serializers/remote_app_permission.py +++ b/apps/perms/serializers/remote_app_permission.py @@ -3,6 +3,8 @@ from rest_framework import serializers +from common.serializers import AdaptedBulkListSerializer +from orgs.mixins import BulkOrgResourceModelSerializer from ..models import RemoteAppPermission @@ -13,13 +15,14 @@ __all__ = [ ] -class RemoteAppPermissionSerializer(serializers.ModelSerializer): +class RemoteAppPermissionSerializer(BulkOrgResourceModelSerializer): class Meta: model = RemoteAppPermission + list_serializer_class = AdaptedBulkListSerializer fields = [ 'id', 'name', 'users', 'user_groups', 'remote_apps', 'comment', 'is_active', 'date_start', 'date_expired', 'is_valid', - 'created_by', 'date_created', 'org_id' + 'created_by', 'date_created', ] read_only_fields = ['created_by', 'date_created'] diff --git a/apps/terminal/views/terminal.py b/apps/terminal/views/terminal.py index a59b06a0d..e5d4658ec 100644 --- a/apps/terminal/views/terminal.py +++ b/apps/terminal/views/terminal.py @@ -2,7 +2,6 @@ # from django.views.generic import ListView, UpdateView, DeleteView, \ DetailView, View -from django.contrib.auth.mixins import LoginRequiredMixin from django.utils.translation import ugettext as _ from django.shortcuts import redirect from django.urls import reverse_lazy, reverse diff --git a/apps/users/serializers/v1.py b/apps/users/serializers/v1.py index a0a13d4fa..672d7b62a 100644 --- a/apps/users/serializers/v1.py +++ b/apps/users/serializers/v1.py @@ -7,6 +7,7 @@ from rest_framework import serializers from common.utils import get_signer, validate_ssh_public_key from common.mixins import BulkSerializerMixin from common.serializers import AdaptedBulkListSerializer +from orgs.mixins import BulkOrgResourceModelSerializer from ..models import User, UserGroup signer = get_signer() @@ -56,7 +57,7 @@ class UserUpdateGroupSerializer(serializers.ModelSerializer): fields = ['id', 'groups'] -class UserGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer): +class UserGroupSerializer(BulkOrgResourceModelSerializer): users = serializers.PrimaryKeyRelatedField( required=False, many=True, queryset=User.objects.all(), label=_('User') )