From 42ff5a382ab7530646806e3f54e8a6c5d54514d5 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 8 Aug 2019 15:14:08 +0800 Subject: [PATCH 1/8] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E8=AE=BE?= =?UTF-8?q?=E7=BD=AEsession=E7=9A=84=E6=97=B6=E9=97=B4=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8Dsdk=E7=AD=89=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/middleware.py | 3 ++- apps/users/api/user.py | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/jumpserver/middleware.py b/apps/jumpserver/middleware.py index c0472d64f..775041b9d 100644 --- a/apps/jumpserver/middleware.py +++ b/apps/jumpserver/middleware.py @@ -57,7 +57,8 @@ class RequestMiddleware: def __call__(self, request): set_current_request(request) response = self.get_response(request) - if not settings.SESSION_EXPIRE_AT_BROWSER_CLOSE: + is_request_api = request.path.startswith('/api') + if not settings.SESSION_EXPIRE_AT_BROWSER_CLOSE and not is_request_api: age = request.session.get_expiry_age() request.session.set_expiry(age) return response diff --git a/apps/users/api/user.py b/apps/users/api/user.py index ba8e48b25..8f3ad6150 100644 --- a/apps/users/api/user.py +++ b/apps/users/api/user.py @@ -179,6 +179,11 @@ class UserProfileApi(generics.RetrieveAPIView): def get_object(self): return self.request.user + def retrieve(self, request, *args, **kwargs): + age = request.session.get_expiry_age() + request.session.set_expiry(age) + return super().retrieve(request, *args, **kwargs) + class UserResetOTPApi(generics.RetrieveAPIView): queryset = User.objects.all() From fbfbfcc2743f2faa4c6a145ef4c4cbf536a1bae6 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 8 Aug 2019 17:59:51 +0800 Subject: [PATCH 2/8] =?UTF-8?q?[Update]=20=E4=BF=AE=E5=A4=8D=E7=A3=81?= =?UTF-8?q?=E7=9B=98=E6=98=BE=E7=A4=BA=E5=8D=95=E4=BD=8D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/utils/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/common/utils/common.py b/apps/common/utils/common.py index c0bd771e4..572820c49 100644 --- a/apps/common/utils/common.py +++ b/apps/common/utils/common.py @@ -106,7 +106,7 @@ def capacity_convert(size, expect='auto', rate=1000): if expect == 'auto': for unit, rate_ in rate_mapping.items(): - if rate > std_size/rate_ > 1: + if rate > std_size/rate_ >= 1 or unit == "T": expect = unit break From 785b375f4a3e4e425fed13cf41f02759b6dd7225 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 8 Aug 2019 18:32:44 +0800 Subject: [PATCH 3/8] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E5=A4=8D=E6=8E=88?= =?UTF-8?q?=E6=9D=83=E5=88=97=E8=A1=A8=E7=94=A8=E6=88=B7=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/perms/api/asset_permission.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/perms/api/asset_permission.py b/apps/perms/api/asset_permission.py index d4f6e7c85..0bbb81e4e 100644 --- a/apps/perms/api/asset_permission.py +++ b/apps/perms/api/asset_permission.py @@ -115,6 +115,7 @@ class AssetPermissionViewSet(viewsets.ModelViewSet): def filter_user(self, queryset): user_id = self.request.query_params.get('user_id') username = self.request.query_params.get('username') + query_group = self.request.query_params.get('all') if user_id: user = get_object_or_none(User, pk=user_id) elif username: @@ -123,6 +124,15 @@ class AssetPermissionViewSet(viewsets.ModelViewSet): return queryset if not user: return queryset.none() + else: + kwargs = {} + args = [] + if query_group: + groups = user.groups.all() + args.append(Q(users=user) | Q(user_groups__in=groups)) + else: + kwargs["users"] = user + return queryset.filter(*args, **kwargs).distinct() def filter_user_group(self, queryset): user_group_id = self.request.query_params.get('user_group_id') @@ -148,6 +158,7 @@ class AssetPermissionViewSet(viewsets.ModelViewSet): def filter_queryset(self, queryset): queryset = super().filter_queryset(queryset) queryset = self.filter_valid(queryset) + queryset = self.filter_user(queryset) queryset = self.filter_keyword(queryset) queryset = self.filter_asset(queryset) queryset = self.filter_node(queryset) From 3c9814191dc16059ac4adf8d01bec1de0a160359 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 8 Aug 2019 18:33:48 +0800 Subject: [PATCH 4/8] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E7=BC=A9?= =?UTF-8?q?=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/perms/api/asset_permission.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/perms/api/asset_permission.py b/apps/perms/api/asset_permission.py index 0bbb81e4e..7f87ea768 100644 --- a/apps/perms/api/asset_permission.py +++ b/apps/perms/api/asset_permission.py @@ -124,15 +124,14 @@ class AssetPermissionViewSet(viewsets.ModelViewSet): return queryset if not user: return queryset.none() + kwargs = {} + args = [] + if query_group: + groups = user.groups.all() + args.append(Q(users=user) | Q(user_groups__in=groups)) else: - kwargs = {} - args = [] - if query_group: - groups = user.groups.all() - args.append(Q(users=user) | Q(user_groups__in=groups)) - else: - kwargs["users"] = user - return queryset.filter(*args, **kwargs).distinct() + kwargs["users"] = user + return queryset.filter(*args, **kwargs).distinct() def filter_user_group(self, queryset): user_group_id = self.request.query_params.get('user_group_id') From a188968e5d68691e099bb987341baa42b08fe419 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 8 Aug 2019 19:04:50 +0800 Subject: [PATCH 5/8] =?UTF-8?q?[Update]=20=E4=BF=AE=E5=A4=8DModel=E8=BF=87?= =?UTF-8?q?=E9=95=BF=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/assets/tasks.py b/apps/assets/tasks.py index f96e1faa5..645b40c02 100644 --- a/apps/assets/tasks.py +++ b/apps/assets/tasks.py @@ -94,7 +94,7 @@ def set_assets_hardware_info(assets, result, **kwargs): break else: ___cpu_model = 'Unknown' - ___cpu_model = ___cpu_model[:64] + ___cpu_model = ___cpu_model[:48] ___cpu_count = info.get('ansible_processor_count', 0) ___cpu_cores = info.get('ansible_processor_cores', None) or \ len(info.get('ansible_processor', [])) From 22b0b77b83a9ce89aa483d2c02f607bcc9b1ac4b Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 9 Aug 2019 16:13:31 +0800 Subject: [PATCH 6/8] fix selected item display bug --- apps/static/plugins/elfinder/css/theme-gray.css | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/static/plugins/elfinder/css/theme-gray.css b/apps/static/plugins/elfinder/css/theme-gray.css index 5c2b35d9e..5876df536 100644 --- a/apps/static/plugins/elfinder/css/theme-gray.css +++ b/apps/static/plugins/elfinder/css/theme-gray.css @@ -1280,7 +1280,8 @@ div.elfinder-cwd-wrapper-list .ui-icon-grip-dotted-vertical { } .elfinder-cwd-file.ui-selected { background: #455158; - color: #555; + /*color: #555;*/ + color: #ddd; width: 120px !important; } .elfinder-cwd-filename input, @@ -1309,10 +1310,10 @@ div.elfinder-cwd-wrapper-list .ui-icon-grip-dotted-vertical { padding: 0; } .elfinder-cwd table tr:nth-child(odd) { - background-color: transparent; + /*background-color: transparent;*/ } .elfinder-cwd table tr:nth-child(odd).ui-state-hover { - background-color: #4c5961; + /*background-color: #4c5961;*/ } #elfinder-elfinder-cwd-thead td { background: #353b42; From fdef282c57f92f85be8a551616664eb81c83dd24 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 12 Aug 2019 17:05:01 +0800 Subject: [PATCH 7/8] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E5=A4=8D=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E6=9B=B4=E6=96=B0=E7=94=A8=E6=88=B7=E7=9A=84bug,=20?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E5=AD=97=E6=AE=B5=E6=B7=BB=E5=8A=A0=20*?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/mixins/serializers.py | 16 +++++++++++----- apps/common/parsers/csv.py | 4 ++-- apps/common/renders/csv.py | 29 ++++++++++------------------- apps/users/api/user.py | 24 ++++++++++-------------- 4 files changed, 33 insertions(+), 40 deletions(-) diff --git a/apps/common/mixins/serializers.py b/apps/common/mixins/serializers.py index 5f8668b93..2dc9483ad 100644 --- a/apps/common/mixins/serializers.py +++ b/apps/common/mixins/serializers.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # +from django.core.exceptions import ObjectDoesNotExist from rest_framework.utils import html from rest_framework.settings import api_settings from rest_framework.exceptions import ValidationError @@ -74,16 +75,21 @@ class BulkListSerializerMixin(object): for item in data: try: # prepare child serializer to only handle one instance - if 'id' in item.keys(): - self.child.instance = self.instance.get(id=item['id']) if self.instance else None - if 'pk' in item.keys(): - self.child.instance = self.instance.get(id=item['pk']) if self.instance else None - + if 'id' in item: + pk = item["id"] + elif 'pk' in item: + pk = item["pk"] + else: + raise ValidationError("id or pk not in data") + child = self.instance.get(id=pk) + self.child.instance = child self.child.initial_data = item # raw validated = self.child.run_validation(item) except ValidationError as exc: errors.append(exc.detail) + except ObjectDoesNotExist as e: + errors.append(e) else: ret.append(validated) errors.append({}) diff --git a/apps/common/parsers/csv.py b/apps/common/parsers/csv.py index d96e9d2ef..7cd7e1648 100644 --- a/apps/common/parsers/csv.py +++ b/apps/common/parsers/csv.py @@ -40,7 +40,7 @@ class JMSCSVParser(BaseParser): @staticmethod def _get_fields_map(serializer): fields_map = {} - fields = serializer.get_fields() + fields = serializer.fields fields_map.update({v.label: k for k, v in fields.items()}) fields_map.update({k: k for k, _ in fields.items()}) return fields_map @@ -91,7 +91,7 @@ class JMSCSVParser(BaseParser): header = next(rows) fields_map = self._get_fields_map(serializer) - header = [fields_map.get(name, '') for name in header] + header = [fields_map.get(name.strip('*'), '') for name in header] data = [] for row in rows: diff --git a/apps/common/renders/csv.py b/apps/common/renders/csv.py index f80498f55..7eaac2b47 100644 --- a/apps/common/renders/csv.py +++ b/apps/common/renders/csv.py @@ -20,26 +20,18 @@ class JMSCSVRender(BaseRenderer): format = 'csv' @staticmethod - def _get_header(fields, template): - if template == 'import': - header = [ - k for k, v in fields.items() - if not v.read_only and k != 'org_id' - ] - elif template == 'update': - header = [k for k, v in fields.items() if not v.read_only] + def _get_show_fields(fields, template): + if template in ('import', 'update'): + return [v for k, v in fields.items() if not v.read_only and k != "org_id"] else: - # template in ['export'] - header = [k for k, v in fields.items() if not v.write_only] - return header + return [v for k, v in fields.items() if not v.write_only and k != "org_id"] @staticmethod - def _gen_table(data, header, labels=None): - labels = labels or {} - yield [labels.get(k, k) for k in header] + def _gen_table(data, fields): + yield ['*{}'.format(f.label) if f.required else f.label for f in fields] for item in data: - row = [item.get(key) for key in header] + row = [item.get(f.field_name) for f in fields] yield row def set_response_disposition(self, serializer, context): @@ -73,10 +65,9 @@ class JMSCSVRender(BaseRenderer): logger.debug(e, exc_info=True) value = 'The resource not support export!'.encode('utf-8') else: - fields = serializer.get_fields() - header = self._get_header(fields, template) - labels = {k: v.label for k, v in fields.items() if v.label} - table = self._gen_table(data, header, labels) + fields = serializer.fields + show_fields = self._get_show_fields(fields, template) + table = self._gen_table(data, show_fields) csv_buffer = BytesIO() csv_buffer.write(codecs.BOM_UTF8) diff --git a/apps/users/api/user.py b/apps/users/api/user.py index 8f3ad6150..31fccf47b 100644 --- a/apps/users/api/user.py +++ b/apps/users/api/user.py @@ -5,10 +5,10 @@ from django.core.cache import cache from django.contrib.auth import logout from django.utils.translation import ugettext as _ -from rest_framework import status from rest_framework import generics from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated +from rest_framework.serializers import ValidationError from rest_framework_bulk import BulkModelViewSet from rest_framework.pagination import LimitOffsetPagination @@ -69,9 +69,7 @@ class UserViewSet(IDInCacheFilterMixin, BulkModelViewSet): check current user has permission to handle instance (update, destroy, bulk_update, bulk destroy) """ - if not self.request.user.is_superuser and instance.is_superuser: - return True - if self.request.user == instance: + if instance.is_superuser and not self.request.user.is_superuser: return True return False @@ -87,16 +85,14 @@ class UserViewSet(IDInCacheFilterMixin, BulkModelViewSet): return False return qs.count() != filtered.count() - def bulk_update(self, request, *args, **kwargs): - """ - rewrite because limit org_admin update superuser - """ - # restrict the update to the filtered queryset - queryset = self.filter_queryset(self.get_queryset()) - if self._bulk_deny_permission(queryset): - data = {'msg': _("You do not have permission.")} - return Response(data=data, status=status.HTTP_403_FORBIDDEN) - return super().bulk_update(request, *args, **kwargs) + def perform_bulk_update(self, serializer): + users_ids = [d.get("id") or d.get("pk") for d in serializer.validated_data] + users = User.objects.filter(id__in=users_ids) + deny_instances = [str(i.id) for i in users if self._deny_permission(i)] + if deny_instances: + msg = "{} can't be update".format(deny_instances) + raise ValidationError({"id": msg}) + return super().perform_bulk_update(serializer) class UserChangePasswordApi(generics.RetrieveUpdateAPIView): From 05e4e7301c921556e47f12fc833cbce734ccd35d Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 12 Aug 2019 17:41:18 +0800 Subject: [PATCH 8/8] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E6=AF=8F=E6=AC=A1=E5=88=B7=E6=96=B0asset=20user?= =?UTF-8?q?=E7=9A=84=E5=8F=AF=E8=BF=9E=E6=8E=A5=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/models/base.py | 3 --- apps/jumpserver/settings.py | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/assets/models/base.py b/apps/assets/models/base.py index 75f81917f..2c36b9d14 100644 --- a/apps/assets/models/base.py +++ b/apps/assets/models/base.py @@ -164,9 +164,6 @@ class AssetUser(OrgModelMixin): def set_asset_connectivity(self, asset, c): key = self.get_asset_connectivity_key(asset) Connectivity.set(key, c) - # 当为某个系统用户或管理用户设置的的时候,失效掉他们的连接数量 - amount_key = self.CONNECTIVITY_AMOUNT_CACHE_KEY.format(self.username, '*') - cache.delete_pattern(amount_key) def get_asset_user(self, asset): from ..backends import AssetUserManager diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index 6e726d938..a4d8e0796 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -54,6 +54,9 @@ LOG_LEVEL = CONFIG.LOG_LEVEL ALLOWED_HOSTS = ['*'] +# Max post update field num +DATA_UPLOAD_MAX_NUMBER_FIELDS = 10000 + # Application definition INSTALLED_APPS = [