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/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', [])) 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/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 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/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 = [ diff --git a/apps/perms/api/asset_permission.py b/apps/perms/api/asset_permission.py index d4f6e7c85..7f87ea768 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,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["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 +157,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) 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; diff --git a/apps/users/api/user.py b/apps/users/api/user.py index ba8e48b25..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): @@ -179,6 +175,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()