From a308000d2ec99ff8b9f7c76992d72e56ed661c00 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 18 Dec 2017 18:38:30 +0800 Subject: [PATCH] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E6=94=B9users=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E4=B8=80=E4=BA=9Bbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api.py | 67 +++++-- apps/assets/serializers.py | 58 +++++- apps/jumpserver/urls.py | 4 +- apps/perms/models.py | 15 +- apps/perms/utils.py | 22 +-- apps/static/js/jumpserver.js | 3 +- apps/terminal/api.py | 91 ++++----- apps/terminal/backends/__init__.py | 7 - apps/terminal/backends/replay/__init__.py | 2 - apps/terminal/backends/replay/base.py | 14 -- apps/terminal/backends/replay/db.py | 8 - apps/terminal/serializers.py | 5 + apps/terminal/urls/api_urls.py | 6 +- apps/users/api.py | 2 +- apps/users/forms.py | 104 +++++----- apps/users/serializers.py | 41 ---- apps/users/signals.py | 2 +- apps/users/templates/users/_user.html | 12 +- .../users/user_asset_permission.html | 182 ------------------ apps/users/templates/users/user_create.html | 2 +- apps/users/templates/users/user_detail.html | 81 ++++---- .../templates/users/user_granted_asset.html | 35 +--- .../users/user_group_asset_permission.html | 181 ----------------- .../users/user_group_create_update.html | 29 +-- .../templates/users/user_group_detail.html | 5 +- .../users/user_group_granted_asset.html | 7 +- .../templates/users/user_group_list.html | 18 +- apps/users/templates/users/user_list.html | 43 ++--- apps/users/templates/users/user_update.html | 10 +- apps/users/urls/views_urls.py | 4 - apps/users/views/group.py | 116 +++-------- apps/users/views/user.py | 146 +++++--------- 32 files changed, 400 insertions(+), 922 deletions(-) delete mode 100644 apps/terminal/backends/replay/__init__.py delete mode 100644 apps/terminal/backends/replay/base.py delete mode 100644 apps/terminal/backends/replay/db.py delete mode 100644 apps/users/templates/users/user_asset_permission.html delete mode 100644 apps/users/templates/users/user_group_asset_permission.html diff --git a/apps/assets/api.py b/apps/assets/api.py index 174ecb249..e1ef7032f 100644 --- a/apps/assets/api.py +++ b/apps/assets/api.py @@ -1,5 +1,4 @@ # ~*~ coding: utf-8 ~*~ - # Copyright (C) 2014-2017 Beijing DuiZhan Technology Co.,Ltd. All Rights Reserved. # # Licensed under the GNU General Public License v2.0 (the "License"); @@ -22,7 +21,7 @@ from django.shortcuts import get_object_or_404 from common.mixins import IDInFilterMixin from common.utils import get_object_or_none -from .hands import IsSuperUser, IsAppUser, IsValidUser, IsSuperUserOrAppUser, \ +from .hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser, \ get_user_granted_assets, push_users from .models import AssetGroup, Asset, Cluster, SystemUser, AdminUser from . import serializers @@ -58,63 +57,80 @@ class AssetViewSet(IDInFilterMixin, BulkModelViewSet): class AssetGroupViewSet(IDInFilterMixin, BulkModelViewSet): - """Asset group api set, for add,delete,update,list,retrieve resource""" + """ + Asset group api set, for add,delete,update,list,retrieve resource + """ queryset = AssetGroup.objects.all() serializer_class = serializers.AssetGroupSerializer permission_classes = (IsSuperUser,) class AssetUpdateGroupApi(generics.RetrieveUpdateAPIView): - """Asset update it's group api""" + """ + Asset update it's group api + """ queryset = Asset.objects.all() serializer_class = serializers.AssetUpdateGroupSerializer permission_classes = (IsSuperUser,) class AssetGroupUpdateApi(generics.RetrieveUpdateAPIView): - """Asset group, update it's asset member""" + """ + Asset group, update it's asset member + """ queryset = AssetGroup.objects.all() serializer_class = serializers.AssetGroupUpdateSerializer permission_classes = (IsSuperUser,) class AssetGroupUpdateSystemUserApi(generics.RetrieveUpdateAPIView): - """Asset group push system user""" + """ + Asset group push system user + """ queryset = AssetGroup.objects.all() serializer_class = serializers.AssetGroupUpdateSystemUserSerializer permission_classes = (IsSuperUser,) class ClusterUpdateAssetsApi(generics.RetrieveUpdateAPIView): - """Cluster update asset member""" + """ + Cluster update asset member + """ queryset = Cluster.objects.all() serializer_class = serializers.ClusterUpdateAssetsSerializer permission_classes = (IsSuperUser,) class ClusterViewSet(IDInFilterMixin, BulkModelViewSet): - """Cluster api set, for add,delete,update,list,retrieve resource""" + """ + Cluster api set, for add,delete,update,list,retrieve resource + """ queryset = Cluster.objects.all() serializer_class = serializers.ClusterSerializer permission_classes = (IsSuperUser,) class AdminUserViewSet(IDInFilterMixin, BulkModelViewSet): - """Admin user api set, for add,delete,update,list,retrieve resource""" + """ + Admin user api set, for add,delete,update,list,retrieve resource + """ queryset = AdminUser.objects.all() serializer_class = serializers.AdminUserSerializer permission_classes = (IsSuperUser,) class SystemUserViewSet(IDInFilterMixin, BulkModelViewSet): - """System user api set, for add,delete,update,list,retrieve resource""" + """ + System user api set, for add,delete,update,list,retrieve resource + """ queryset = SystemUser.objects.all() serializer_class = serializers.SystemUserSerializer permission_classes = (IsSuperUserOrAppUser,) class SystemUserUpdateApi(generics.RetrieveUpdateAPIView): - """Asset update it's system user + """ + Asset update it's system user when update then push system user to asset. """ @@ -134,28 +150,36 @@ class SystemUserUpdateApi(generics.RetrieveUpdateAPIView): class SystemUserUpdateAssetsApi(generics.RetrieveUpdateAPIView): - """System user update it's assets""" + """ + System user update it's assets + """ queryset = SystemUser.objects.all() serializer_class = serializers.SystemUserUpdateAssetsSerializer permission_classes = (IsSuperUser,) class SystemUserUpdateAssetGroupApi(generics.RetrieveUpdateAPIView): - """System user update asset group""" + """ + System user update asset group + """ queryset = SystemUser.objects.all() serializer_class = serializers.SystemUserUpdateAssetGroupSerializer permission_classes = (IsSuperUser,) class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView): - """Asset bulk update api""" + """ + Asset bulk update api + """ queryset = Asset.objects.all() serializer_class = serializers.AssetSerializer permission_classes = (IsSuperUser,) class SystemUserAuthInfoApi(generics.RetrieveAPIView): - """Get system user auth info""" + """ + Get system user auth info + """ queryset = SystemUser.objects.all() permission_classes = (IsSuperUserOrAppUser,) @@ -172,7 +196,9 @@ class SystemUserAuthInfoApi(generics.RetrieveAPIView): class AssetRefreshHardwareView(generics.RetrieveAPIView): - """Refresh asset hardware info""" + """ + Refresh asset hardware info + """ queryset = Asset.objects.all() serializer_class = serializers.AssetSerializer permission_classes = (IsSuperUser,) @@ -181,7 +207,6 @@ class AssetRefreshHardwareView(generics.RetrieveAPIView): asset_id = kwargs.get('pk') asset = get_object_or_404(Asset, pk=asset_id) summary = update_assets_hardware_info([asset]) - print(summary) if summary.get('dark'): return Response(summary['dark'].values(), status=501) else: @@ -189,7 +214,9 @@ class AssetRefreshHardwareView(generics.RetrieveAPIView): class AssetAdminUserTestView(AssetRefreshHardwareView): - """Test asset admin user connectivity""" + """ + Test asset admin user connectivity + """ queryset = Asset.objects.all() permission_classes = (IsSuperUser,) @@ -204,7 +231,9 @@ class AssetAdminUserTestView(AssetRefreshHardwareView): class AssetGroupPushSystemUserView(generics.UpdateAPIView): - """Asset group push system user api""" + """ + Asset group push system user api + """ queryset = AssetGroup.objects.all() permission_classes = (IsSuperUser,) serializer_class = serializers.AssetSerializer diff --git a/apps/assets/serializers.py b/apps/assets/serializers.py index c903b5891..41871759f 100644 --- a/apps/assets/serializers.py +++ b/apps/assets/serializers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from django.core.cache import cache -from rest_framework import viewsets, serializers, generics +from rest_framework import serializers from rest_framework_bulk.serializers import BulkListSerializer from common.mixins import BulkSerializerMixin @@ -9,6 +9,9 @@ from .const import ADMIN_USER_CONN_CACHE_KEY, SYSTEM_USER_CONN_CACHE_KEY class AssetGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer): + """ + 资产组序列化数据模型 + """ assets_amount = serializers.SerializerMethodField() assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all()) @@ -23,6 +26,9 @@ class AssetGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer): class AssetUpdateGroupSerializer(serializers.ModelSerializer): + """ + 资产更新自己所在资产组的请求数据结构定义 + """ groups = serializers.PrimaryKeyRelatedField(many=True, queryset=AssetGroup.objects.all()) class Meta: @@ -31,6 +37,9 @@ class AssetUpdateGroupSerializer(serializers.ModelSerializer): class AssetUpdateSystemUserSerializer(serializers.ModelSerializer): + """ + 资产更新其系统用户请求的数据结构定义 + """ system_users = serializers.PrimaryKeyRelatedField(many=True, queryset=SystemUser.objects.all()) class Meta: @@ -39,7 +48,9 @@ class AssetUpdateSystemUserSerializer(serializers.ModelSerializer): class AssetGroupUpdateSerializer(serializers.ModelSerializer): - """update the asset group, and add or delete the asset to the group""" + """ + 资产组更新需要的数据结构 + """ assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all()) class Meta: @@ -48,6 +59,9 @@ class AssetGroupUpdateSerializer(serializers.ModelSerializer): class AssetGroupUpdateSystemUserSerializer(serializers.ModelSerializer): + """ + 资产组更新系统用户定义的数据结构 + """ system_users = serializers.PrimaryKeyRelatedField(many=True, queryset=SystemUser.objects.all()) class Meta: @@ -56,6 +70,9 @@ class AssetGroupUpdateSystemUserSerializer(serializers.ModelSerializer): class ClusterUpdateAssetsSerializer(serializers.ModelSerializer): + """ + 集群更新资产数据结构 + """ assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all()) class Meta: @@ -64,6 +81,9 @@ class ClusterUpdateAssetsSerializer(serializers.ModelSerializer): class AdminUserSerializer(serializers.ModelSerializer): + """ + 管理用户 + """ assets_amount = serializers.SerializerMethodField() unreachable_amount = serializers.SerializerMethodField() @@ -89,6 +109,9 @@ class AdminUserSerializer(serializers.ModelSerializer): class SystemUserSerializer(serializers.ModelSerializer): + """ + 系统用户 + """ unreachable_amount = serializers.SerializerMethodField() assets_amount = serializers.SerializerMethodField() @@ -113,12 +136,18 @@ class SystemUserSerializer(serializers.ModelSerializer): class AssetSystemUserSerializer(serializers.ModelSerializer): + """ + 查看授权的资产系统用户的数据结构,这个和AssetSerializer不同,字段少 + """ class Meta: model = SystemUser fields = ('id', 'name', 'username', 'priority', 'protocol', 'comment',) class SystemUserUpdateAssetsSerializer(serializers.ModelSerializer): + """ + 系统用户更新关联资产的数据结构 + """ assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all()) class Meta: @@ -127,6 +156,9 @@ class SystemUserUpdateAssetsSerializer(serializers.ModelSerializer): class SystemUserUpdateAssetGroupSerializer(serializers.ModelSerializer): + """ + 系统用户更新资产组的api + """ asset_groups = serializers.PrimaryKeyRelatedField(many=True, queryset=AssetGroup.objects.all()) class Meta: @@ -135,12 +167,18 @@ class SystemUserUpdateAssetGroupSerializer(serializers.ModelSerializer): class SystemUserSimpleSerializer(serializers.ModelSerializer): + """ + 系统用户最基本信息的数据结构 + """ class Meta: model = SystemUser fields = ('id', 'name', 'username') class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer): + """ + 资产的数据结构 + """ class Meta(object): model = Asset list_serializer_class = BulkListSerializer @@ -157,6 +195,9 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer): class AssetGrantedSerializer(serializers.ModelSerializer): + """ + 被授权资产的数据结构 + """ system_users_granted = AssetSystemUserSerializer(many=True, read_only=True) is_inherited = serializers.SerializerMethodField() system_users_join = serializers.SerializerMethodField() @@ -180,7 +221,9 @@ class AssetGrantedSerializer(serializers.ModelSerializer): class MyAssetGrantedSerializer(AssetGrantedSerializer): - """Remove ip and port from asset for security""" + """ + 普通用户获取授权的资产定义的数据结构 + """ class Meta(object): model = Asset @@ -189,6 +232,9 @@ class MyAssetGrantedSerializer(AssetGrantedSerializer): class ClusterSerializer(BulkSerializerMixin, serializers.ModelSerializer): + """ + cluster + """ assets_amount = serializers.SerializerMethodField() admin_user_name = serializers.SerializerMethodField() assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all()) @@ -210,6 +256,9 @@ class ClusterSerializer(BulkSerializerMixin, serializers.ModelSerializer): class AssetGroupGrantedSerializer(BulkSerializerMixin, serializers.ModelSerializer): + """ + 授权资产组 + """ assets_granted = AssetGrantedSerializer(many=True, read_only=True) assets_amount = serializers.SerializerMethodField() @@ -224,6 +273,9 @@ class AssetGroupGrantedSerializer(BulkSerializerMixin, serializers.ModelSerializ class MyAssetGroupGrantedSerializer(serializers.ModelSerializer): + """ + 普通用户授权资产组结构 + """ assets_granted = MyAssetGrantedSerializer(many=True, read_only=True) assets_amount = serializers.SerializerMethodField() diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py index cf5270d29..fe25d82b5 100644 --- a/apps/jumpserver/urls.py +++ b/apps/jumpserver/urls.py @@ -16,7 +16,6 @@ urlpatterns = [ url(r'^users/', include('users.urls.views_urls', namespace='users')), url(r'^assets/', include('assets.urls.views_urls', namespace='assets')), url(r'^perms/', include('perms.urls.views_urls', namespace='perms')), - # url(r'^audits/', include('audits.urls.views_urls', namespace='audits')), url(r'^terminal/', include('terminal.urls.views_urls', namespace='terminal')), url(r'^ops/', include('ops.urls.view_urls', namespace='ops')), @@ -24,9 +23,10 @@ urlpatterns = [ url(r'^api/users/', include('users.urls.api_urls', namespace='api-users')), url(r'^api/assets/', include('assets.urls.api_urls', namespace='api-assets')), url(r'^api/perms/', include('perms.urls.api_urls', namespace='api-perms')), - # url(r'^api/audits/', include('audits.urls.api_urls', namespace='api-audits')), url(r'^api/terminal/', include('terminal.urls.api_urls', namespace='api-terminal')), url(r'^api/ops/', include('ops.urls.api_urls', namespace='api-ops')), + + # External apps url url(r'^captcha/', include('captcha.urls')), ] diff --git a/apps/perms/models.py b/apps/perms/models.py index f83993c9a..dfbd0a810 100644 --- a/apps/perms/models.py +++ b/apps/perms/models.py @@ -4,17 +4,12 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ from django.utils import timezone -from common.utils import date_expired_default, combine_seq +from common.utils import date_expired_default class AssetPermission(models.Model): from users.models import User, UserGroup from assets.models import Asset, AssetGroup, SystemUser - # PRIVATE_FOR_CHOICE = ( - # ('N', 'None'), - # ('U', 'user'), - # ('G', 'user group'), - # ) id = models.UUIDField(default=uuid.uuid4, primary_key=True) name = models.CharField( max_length=128, unique=True, verbose_name=_('Name')) @@ -38,14 +33,14 @@ class AssetPermission(models.Model): auto_now_add=True, verbose_name=_('Date created')) comment = models.TextField(verbose_name=_('Comment'), blank=True) - def __unicode__(self): + def __str__(self): return self.name @property def is_valid(self): if self.date_expired < timezone.now() and self.is_active: return True - return True + return False def get_granted_users(self): return list(set(self.users.all()) | self.get_granted_user_groups_member()) @@ -73,8 +68,8 @@ class AssetPermission(models.Model): assets.add(asset) return assets - class Meta: - db_table = 'asset_permission' + # class Meta: + # db_table = 'asset_permission' # def change_permission(sender, **kwargs): diff --git a/apps/perms/utils.py b/apps/perms/utils.py index b2c282f34..edbbe6dc9 100644 --- a/apps/perms/utils.py +++ b/apps/perms/utils.py @@ -13,9 +13,9 @@ logger = get_logger(__file__) def get_user_group_granted_asset_groups(user_group): """Return asset groups granted of the user group - :param user_group: Instance of :class: ``UserGroup`` - :return: {asset_group1: {system_user1, }, - asset_group2: {system_user1, system_user2}} + :param user_group: Instance of :class: ``UserGroup`` + :return: {asset_group1: {system_user1, }, + asset_group2: {system_user1, system_user2}} """ asset_groups = {} asset_permissions = user_group.asset_permissions.all() @@ -168,22 +168,6 @@ def get_user_granted_system_users(user): return system_users_dict -def get_user_groups_granted_in_asset(asset): - pass - - -def get_users_granted_in_asset(asset): - pass - - -def get_user_groups_granted_in_asset_group(asset): - pass - - -def get_users_granted_in_asset_group(asset): - pass - - def push_system_user(assets, system_user): logger.info('Push system user %s' % system_user.name) for asset in assets: diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index fcbbff4bd..cb7f1b098 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -245,7 +245,8 @@ jumpserver.initDataTable = function (options) { // buttons: ['excel', 'pdf', 'print'], // columnDefs: [{target: 0, createdCell: ()=>{}}, ...], // uc_html: 'header button', - // op_html: 'div.btn-group?' + // op_html: 'div.btn-group?', + // paging: true // } var ele = options.ele || $('.dataTable'); var columnDefs = [ diff --git a/apps/terminal/api.py b/apps/terminal/api.py index 6fcd2fc18..576f068f7 100644 --- a/apps/terminal/api.py +++ b/apps/terminal/api.py @@ -8,17 +8,18 @@ import os from rest_framework import viewsets, serializers from rest_framework.views import APIView, Response from rest_framework.permissions import AllowAny -from django.shortcuts import get_object_or_404 +from django.shortcuts import get_object_or_404, redirect from django.utils import timezone -from django.conf import settings +from django.core.files.storage import default_storage +from django.http import HttpResponseNotFound from common.utils import get_object_or_none from .models import Terminal, Status, Session, Task from .serializers import TerminalSerializer, StatusSerializer, \ - SessionSerializer, TaskSerializer + SessionSerializer, TaskSerializer, ReplaySerializer from .hands import IsSuperUserOrAppUser, IsAppUser, \ IsSuperUserOrAppUserOrUserReadonly -from .backends import get_command_store, get_replay_store, SessionCommandSerializer +from .backends import get_command_store, SessionCommandSerializer logger = logging.getLogger(__file__) @@ -149,46 +150,6 @@ class TaskViewSet(viewsets.ModelViewSet): serializer_class = TaskSerializer permission_classes = (IsSuperUserOrAppUser,) - # def get_queryset(self): - # terminal_id = self.kwargs.get("terminal", None) - # if terminal_id: - # terminal = get_object_or_404(Terminal, id=terminal_id) - # self.queryset = terminal.status_set.all() - # - # if hasattr(self.request.user, "terminal"): - # terminal = self.request.user.terminal - # self.queryset = terminal.status_set.all() - # return self.queryset - - -class SessionReplayAPI(APIView): - permission_classes = (IsSuperUserOrAppUser,) - - def post(self, request, **kwargs): - session_id = kwargs.get("pk", None) - session = get_object_or_404(Session, id=session_id) - record_dir = settings.CONFIG.SESSION_RECORDE_DIR - date = session.date_start.strftime("%Y-%m-%d") - record_dir = os.path.join(record_dir, date, str(session.id)) - record_filename = os.path.join(record_dir, "replay.tar.gz2") - - if not os.path.isdir(record_dir): - try: - os.makedirs(record_dir) - except FileExistsError: - pass - - archive_stream = request.data.get("archive") - if not archive_stream: - return Response("None file upload", status=400) - - with open(record_filename, 'wb') as f: - for chunk in archive_stream.chunks(): - f.write(chunk) - session.has_replay = True - session.save() - return Response({"session_id": session.id}, status=201) - class CommandViewSet(viewsets.ViewSet): """接受app发送来的command log, 格式如下 @@ -208,7 +169,7 @@ class CommandViewSet(viewsets.ViewSet): permission_classes = (IsSuperUserOrAppUser,) def get_queryset(self): - self.command_store.filter(**dict(self.request.data)) + self.command_store.filter(**dict(self.request.query_params)) def create(self, request, *args, **kwargs): serializer = self.serializer_class(data=request.data, many=True) @@ -227,3 +188,43 @@ class CommandViewSet(viewsets.ViewSet): queryset = list(self.command_store.all()) serializer = self.serializer_class(queryset, many=True) return Response(serializer.data) + + +class SessionReplayViewSet(viewsets.ViewSet): + serializer_class = ReplaySerializer + permission_classes = () + session = None + + def gen_session_path(self): + date = self.session.date_start.strftime('%Y-%m-%d') + return os.path.join(date, str(self.session.id)+'.gz') + + def create(self, request, *args, **kwargs): + session_id = kwargs.get('pk') + self.session = get_object_or_404(Session, id=session_id) + serializer = self.serializer_class(data=request.data) + + if serializer.is_valid(): + file = serializer.validated_data['file'] + file_path = self.gen_session_path() + try: + default_storage.save(file_path, file) + return Response({'url': default_storage.url(file_path)}, + status=201) + except IOError: + return Response("Save error", status=500) + else: + logger.error( + 'Update load data invalid: {}'.format(serializer.errors)) + return Response({'msg': serializer.errors}, status=401) + + def retrieve(self, request, *args, **kwargs): + session_id = kwargs.get('pk') + self.session = get_object_or_404(Session, id=session_id) + path = self.gen_session_path() + + if default_storage.exists(path): + url = default_storage.url(path) + return redirect(url) + else: + return HttpResponseNotFound() diff --git a/apps/terminal/backends/__init__.py b/apps/terminal/backends/__init__.py index 6a9d06079..3696db49b 100644 --- a/apps/terminal/backends/__init__.py +++ b/apps/terminal/backends/__init__.py @@ -1,7 +1,5 @@ from importlib import import_module - from django.conf import settings - from .command.serializers import SessionCommandSerializer @@ -10,8 +8,3 @@ def get_command_store(): command_store = command_engine.CommandStore() return command_store - -def get_replay_store(): - replay_engine = import_module(settings.REPLAY_STORE_BACKEND) - replay_store = replay_engine.ReplayStore() - return replay_store diff --git a/apps/terminal/backends/replay/__init__.py b/apps/terminal/backends/replay/__init__.py deleted file mode 100644 index 3e0d2fd0f..000000000 --- a/apps/terminal/backends/replay/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# ~*~ coding: utf-8 ~*~ - diff --git a/apps/terminal/backends/replay/base.py b/apps/terminal/backends/replay/base.py deleted file mode 100644 index 46a2df845..000000000 --- a/apps/terminal/backends/replay/base.py +++ /dev/null @@ -1,14 +0,0 @@ -# coding: utf-8 -import abc - - -class ReplayBase(object): - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def save(self, proxy_log_id, output, timestamp): - pass - - @abc.abstractmethod - def filter(self, date_from_ts=None, proxy_log_id=None): - pass diff --git a/apps/terminal/backends/replay/db.py b/apps/terminal/backends/replay/db.py deleted file mode 100644 index 06d2c5ab3..000000000 --- a/apps/terminal/backends/replay/db.py +++ /dev/null @@ -1,8 +0,0 @@ -# ~*~ coding: utf-8 ~*~ - -from .base import ReplayBase - - -class ReplayStore(ReplayBase): - pass - diff --git a/apps/terminal/serializers.py b/apps/terminal/serializers.py index 78991b9b9..b44d0b443 100644 --- a/apps/terminal/serializers.py +++ b/apps/terminal/serializers.py @@ -62,3 +62,8 @@ class TaskSerializer(serializers.ModelSerializer): class Meta: fields = '__all__' model = Task + + +class ReplaySerializer(serializers.Serializer): + file = serializers.FileField() + diff --git a/apps/terminal/urls/api_urls.py b/apps/terminal/urls/api_urls.py index 9858bf014..2ce0857c8 100644 --- a/apps/terminal/urls/api_urls.py +++ b/apps/terminal/urls/api_urls.py @@ -11,13 +11,15 @@ app_name = 'terminal' router = routers.DefaultRouter() router.register(r'v1/terminal/(?P[a-zA-Z0-9\-]{36})?/?status', api.StatusViewSet, 'terminal-status') -router.register(r'v1/terminal/(?Pa-zA-Z0-9\-]{36})?/?sessions', api.SessionViewSet, 'terminal-sessions') +router.register(r'v1/terminal/(?P[a-zA-Z0-9\-]{36})?/?sessions', api.SessionViewSet, 'terminal-sessions') router.register(r'v1/tasks', api.TaskViewSet, 'tasks') router.register(r'v1/terminal', api.TerminalViewSet, 'terminal') router.register(r'v1/command', api.CommandViewSet, 'command') urlpatterns = [ - url(r'^v1/sessions/(?P[0-9a-zA-Z\-_]+)/replay/$', api.SessionReplayAPI.as_view(), name='session-replay'), + url(r'^v1/sessions/(?P[0-9a-zA-Z\-]{36})/replay/$', + api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}), + name='session-replay'), ] urlpatterns += router.urls diff --git a/apps/users/api.py b/apps/users/api.py index 160c19028..556cec0bd 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -21,7 +21,7 @@ logger = get_logger(__name__) class UserViewSet(BulkModelViewSet): - queryset = User.objects.all() + queryset = User.objects.exclude(role="App") # queryset = User.objects.all().exclude(role="App").order_by("date_joined") serializer_class = UserSerializer permission_classes = (IsSuperUser,) diff --git a/apps/users/forms.py b/apps/users/forms.py index 95009e366..c2cc8c8df 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -13,17 +13,23 @@ from .models import User, UserGroup class UserLoginForm(AuthenticationForm): username = forms.CharField(label=_('Username'), max_length=100) password = forms.CharField( - label=_('Password'), widget=forms.PasswordInput, max_length=100, - strip=False) + label=_('Password'), widget=forms.PasswordInput, + max_length=128, strip=False + ) captcha = CaptchaField() class UserCreateUpdateForm(forms.ModelForm): + password = forms.CharField( + label=_('Password'), widget=forms.PasswordInput, + max_length=128, strip=False, required=False, + ) + class Meta: model = User fields = [ 'username', 'name', 'email', 'groups', 'wechat', - 'phone', 'enable_otp', 'role', 'date_expired', 'comment', + 'phone', 'role', 'date_expired', 'comment', 'password' ] help_texts = { 'username': '* required', @@ -36,6 +42,14 @@ class UserCreateUpdateForm(forms.ModelForm): 'data-placeholder': _('Join user groups')}), } + def save(self, commit=True): + user = super().save(commit=commit) + password = self.cleaned_data.get('password') + if password: + user.set_password(password) + user.save() + return user + class UserProfileForm(forms.ModelForm): class Meta: @@ -115,15 +129,11 @@ class UserPublicKeyForm(forms.Form): class UserBulkUpdateForm(forms.ModelForm): - role = forms.ChoiceField( - label=_('Role'), - choices=[('Admin', 'Administrator'), ('User', 'User')], - ) users = forms.MultipleChoiceField( required=True, help_text='* required', label=_('Select users'), - # choices=[(user.id, user.name) for user in User.objects.all()], + choices=[(user.id, user.name) for user in User.objects.all()], widget=forms.SelectMultiple( attrs={ 'class': 'select2', @@ -134,18 +144,26 @@ class UserBulkUpdateForm(forms.ModelForm): class Meta: model = User - fields = ['users', 'role', 'groups', 'date_expired', 'is_active', 'enable_otp'] + fields = ['users', 'role', 'groups', 'date_expired', 'is_active'] widgets = { - 'groups': forms.SelectMultiple( - attrs={'class': 'select2', - 'data-placeholder': _('Select user groups')}), + "groups": forms.SelectMultiple( + attrs={ + 'class': 'select2', + 'data-placeholder': _('Select users') + } + ) } def save(self, commit=True): - cleaned_data = {k: v for k, v in self.cleaned_data.items() if - v is not None} - users_id = cleaned_data.pop('users') - groups = cleaned_data.pop('groups') + changed_fields = [] + for field in self._meta.fields: + if self.data.get(field) is not None: + changed_fields.append(field) + + cleaned_data = {k: v for k, v in self.cleaned_data.items() + if k in changed_fields} + users_id = cleaned_data.pop('users', '') + groups = cleaned_data.pop('groups', []) users = User.objects.filter(id__in=users_id) users.update(**cleaned_data) if groups: @@ -155,42 +173,42 @@ class UserBulkUpdateForm(forms.ModelForm): class UserGroupForm(forms.ModelForm): + users = forms.ModelMultipleChoiceField( + queryset=User.objects.all(), + widget=forms.SelectMultiple( + attrs={ + 'class': 'select2', + 'data-placeholder': _('Select users') + } + ) + ) + + def __init__(self, **kwargs): + instance = kwargs.get('instance') + if instance: + initial = kwargs.get('initial', {}) + initial.update({ + 'users': instance.users.all(), + }) + kwargs['initial'] = initial + super().__init__(**kwargs) + + def save(self, commit=True): + group = super().save(commit=commit) + users = self.cleaned_data['users'] + group.users.set(users) + return group + class Meta: model = UserGroup fields = [ - 'name', 'comment' + 'name', 'users', 'comment' ] help_texts = { 'name': '* required' } -class UserPrivateAssetPermissionForm(forms.ModelForm): - def save(self, commit=True): - self.instance = super(UserPrivateAssetPermissionForm, self)\ - .save(commit=commit) - self.instance.users = [self.user] - self.instance.save() - return self.instance - - class Meta: - model = AssetPermission - fields = [ - 'assets', 'asset_groups', 'system_users', 'name', - ] - widgets = { - 'assets': forms.SelectMultiple( - attrs={'class': 'select2', - 'data-placeholder': _('Select assets')}), - 'asset_groups': forms.SelectMultiple( - attrs={'class': 'select2', - 'data-placeholder': _('Select asset groups')}), - 'system_users': forms.SelectMultiple( - attrs={'class': 'select2', - 'data-placeholder': _('Select system users')}), - } - - class UserGroupPrivateAssetPermissionForm(forms.ModelForm): def save(self, commit=True): self.instance = super(UserGroupPrivateAssetPermissionForm, self)\ diff --git a/apps/users/serializers.py b/apps/users/serializers.py index f8e1d6044..1da177f78 100644 --- a/apps/users/serializers.py +++ b/apps/users/serializers.py @@ -69,44 +69,3 @@ class UserGroupUpdateMemeberSerializer(serializers.ModelSerializer): model = UserGroup fields = ['id', 'users'] - -# class GroupDetailSerializer(serializers.ModelSerializer): -# class Meta: -# model = UserGroup -# fields = ['id', 'name', 'comment', 'date_created', 'created_by', 'users'] - - -# class UserBulkUpdateSerializer(BulkSerializerMixin, serializers.ModelSerializer): -# group_display = serializers.SerializerMethodField() -# active_display = serializers.SerializerMethodField() -# groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all()) -# -# class Meta(object): -# model = User -# list_serializer_class = BulkListSerializer -# fields = ['id', 'is_active', 'username', 'name', 'email', 'role', 'avatar', -# 'enable_otp', 'comment', 'groups', 'get_role_display', -# 'group_display', 'active_display'] -# -# @staticmethod -# def get_group_display(obj): -# return " ".join([group.name for group in obj.groups.all()]) -# -# @staticmethod -# def get_active_display(obj): -# TODO: user active state - # return not (obj.is_expired and obj.is_active) -# -# -# class GroupBulkUpdateSerializer(BulkSerializerMixin, serializers.ModelSerializer): -# user_amount = serializers.SerializerMethodField() -# -# class Meta: -# model = UserGroup -# list_serializer_class = BulkListSerializer -# fields = ['id', 'name', 'comment', 'user_amount'] -# -# @staticmethod -# def get_user_amount(obj): -# return obj.users.count() -# diff --git a/apps/users/signals.py b/apps/users/signals.py index 15bd56c55..537cfb329 100644 --- a/apps/users/signals.py +++ b/apps/users/signals.py @@ -6,7 +6,7 @@ from django.dispatch import Signal, receiver from common.utils import get_logger logger = get_logger(__file__) -on_user_created = Signal(providing_args=['user']) +on_user_created = Signal(providing_args=['user', 'request']) @receiver(on_user_created) diff --git a/apps/users/templates/users/_user.html b/apps/users/templates/users/_user.html index 313ed0644..b33abce51 100644 --- a/apps/users/templates/users/_user.html +++ b/apps/users/templates/users/_user.html @@ -32,12 +32,12 @@ {{ form.date_expired.errors }} -
- -
- {{ form.enable_otp }} -
-
+{#
#} +{# #} +{#
#} +{# {{ form.enable_otp }}#} +{#
#} +{#
#}

{% trans 'Profile' %}

{% bootstrap_field form.phone layout="horizontal" %} diff --git a/apps/users/templates/users/user_asset_permission.html b/apps/users/templates/users/user_asset_permission.html deleted file mode 100644 index 405a5f9c1..000000000 --- a/apps/users/templates/users/user_asset_permission.html +++ /dev/null @@ -1,182 +0,0 @@ -{% extends 'base.html' %} -{% load bootstrap3 %} -{% load static %} -{% load i18n %} - -{% block custom_head_css_js %} - - -{% endblock %} -{% block content %} -
-
-
-
- -
-
-
-
- {% trans 'Asset permission of ' %} {{ user.name }} -
- - - - - - - - - - -
-
-
- - - - - - - - - - - - - - -
- - {% trans 'Name' %}{% trans 'Asset' %}{% trans 'Asset group' %}{% trans 'System user' %}{% trans 'Valid' %}
-
-
-
-
-
-
- {% trans 'Quick create permission for user' %} -
-
-
- - - {% csrf_token %} - - - - - - - - - - - - - - - - -
-
- {% bootstrap_field form.assets %} -
- {% bootstrap_field form.asset_groups %} -
- {% bootstrap_field form.system_users %} -
- -
-
-
-
-
-
-
-
-
-
- -{% endblock %} -{% block custom_foot_js %} - -{% endblock %} diff --git a/apps/users/templates/users/user_create.html b/apps/users/templates/users/user_create.html index fdc41a597..052554903 100644 --- a/apps/users/templates/users/user_create.html +++ b/apps/users/templates/users/user_create.html @@ -6,7 +6,7 @@ {# {% bootstrap_field form.username layout="horizontal" %}#} {#{% endblock %}#} {% block password %} -

{% trans 'Password' %}

+

{% trans 'Auth' %}

diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index dea2a82ce..a879fe74a 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -18,9 +18,6 @@
  • {% trans 'User detail' %}
  • -
  • - {% trans 'Asset permission' %} -
  • {% trans 'Asset granted' %}
  • @@ -35,7 +32,7 @@
    -
    +
    {{ user_object.name }} @@ -118,7 +115,7 @@
    -
    +
    {% trans 'Quick modify' %} @@ -140,34 +137,34 @@
    +{# #} +{# {% trans 'Enable OTP' %}:#} +{# #} +{#
    #} +{#
    #} +{# #} +{# #} +{#
    #} +{#
    #} +{#
    #} +{# #} - {% trans 'Enable OTP' %}: - -
    -
    - - -
    -
    -
    - - - {% trans 'Reset password' %}: + {% trans 'Send reset password mail' %}: - + - {% trans 'Reset ssh key' %}: + {% trans 'Send reset ssh key mail' %}: - + @@ -188,7 +185,7 @@ @@ -263,7 +260,7 @@ $(document).ready(function() { }) .on('select2:unselect', function(evt) { var data = evt.params.data; - delete jumpserver.groups_selected[data.id] + delete jumpserver.groups_selected[data.id]; }) }) .on('click', '#is_active', function() { @@ -279,20 +276,20 @@ $(document).ready(function() { success_message: success }); }) - .on('click', '#enable_otp', function() { - var the_url = "{% url 'api-users:user-detail' pk=user_object.id %}"; - var checked = $(this).prop('checked'); - var body = { - 'enable_otp': checked - }; - var success = '{% trans "Update successfully!" %}'; - APIUpdateAttr({ - url: the_url, - body: JSON.stringify(body), - success_message: success - }); -}) - .on('click', '#btn_join_group', function() { +{#.on('click', '#enable_otp', function() {#} +{# var the_url = "{% url 'api-users:user-detail' pk=user_object.id %}";#} +{# var checked = $(this).prop('checked');#} +{# var body = {#} +{# 'enable_otp': checked#} +{# };#} +{# var success = '{% trans "Update successfully!" %}';#} +{# APIUpdateAttr({#} +{# url: the_url,#} +{# body: JSON.stringify(body),#} +{# success_message: success#} +{# });#} +{# });#} +.on('click', '#btn_join_group', function() { if (Object.keys(jumpserver.groups_selected).length === 0) { return false; } @@ -300,7 +297,7 @@ $(document).ready(function() { return $(this).data('gid'); }).get(); $.map(jumpserver.groups_selected, function(value, index) { - groups.push(parseInt(index)); + groups.push(index); $('#opt_' + index).remove(); }); updateUserGroups(groups) @@ -368,7 +365,7 @@ $(document).ready(function() { }, function() { doReset(); }); -}).on('click', '#btn_user_update_pk', function(){ +}).on('click', '#btn-user-update-pk', function(){ var $this = $(this); var pk = $('#txt_pk').val(); var the_url = '{% url "api-users:user-public-key-reset" pk=user.id %}'; diff --git a/apps/users/templates/users/user_granted_asset.html b/apps/users/templates/users/user_granted_asset.html index db8ca0800..753e1362e 100644 --- a/apps/users/templates/users/user_granted_asset.html +++ b/apps/users/templates/users/user_granted_asset.html @@ -17,9 +17,6 @@
  • {% trans 'User detail' %}
  • -
  • - {% trans 'Asset permission' %} -
  • {% trans 'Asset granted' %}
  • @@ -48,12 +45,9 @@ - - - @@ -84,7 +78,6 @@
    {% trans 'Hostname' %} {% trans 'IP' %}{% trans 'Port' %} {% trans 'System user' %}{% trans 'Valid' %}
    - @@ -112,28 +105,15 @@ order: [], select: [], columnDefs: [ - {targets: 1, createdCell: function (td, cellData, rowData) { + {targets: 0, createdCell: function (td, cellData, rowData) { var detail_btn = '' + cellData + ''; $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); - }}, - {targets: 4, createdCell: function (td, cellData, rowData) { - if (cellData.length > 10){ - $(td).html(cellData.substring(1, 10) + '..') - } else { - $(td).html(cellData) - } - }}, - {targets: 5, createdCell: function (td, cellData) { - if (!cellData) { - $(td).html('') - } else { - $(td).html('') - } - }} + }} ], ajax_url: '{% url "api-perms:user-assets" pk=user.id %}', - columns: [{data: function(){return ""}}, {data: "hostname" }, {data: "ip" }, {data: "port"}, - {data: "system_users_join"}, {data: "is_active"}] + columns: [{data: "hostname" }, {data: "ip" }, + {data: "system_users_join"} + ] }; var options2 = { ele: $('#user_asset_groups_table'), @@ -141,13 +121,14 @@ order: [], select: [], columnDefs: [ - {targets: 1, createdCell: function (td, cellData, rowData) { + {targets: 0, createdCell: function (td, cellData, rowData) { var detail_btn = '' + cellData + ''; $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); }} ], ajax_url: '{% url "api-perms:user-asset-groups" pk=user.id %}', - columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets_amount" }] + columns: [{data: "name" }, {data: "assets_amount" }], + paging: false }; jumpserver.initDataTable(options); jumpserver.initDataTable(options2); diff --git a/apps/users/templates/users/user_group_asset_permission.html b/apps/users/templates/users/user_group_asset_permission.html deleted file mode 100644 index a775b8518..000000000 --- a/apps/users/templates/users/user_group_asset_permission.html +++ /dev/null @@ -1,181 +0,0 @@ -{% extends 'base.html' %} -{% load bootstrap3 %} -{% load static %} -{% load i18n %} - -{% block custom_head_css_js %} - - -{% endblock %} -{% block content %} -
    -
    -
    -
    - -
    -
    -
    -
    - {% trans 'Asset permission of ' %} {{ user_group.name }} -
    - - - - - - - - - - -
    -
    -
    -
    {% trans 'Name' %} {% trans 'Asset' %}
    - - - - - - - - - - - - - -
    - - {% trans 'Name' %}{% trans 'Asset' %}{% trans 'Asset group' %}{% trans 'System user' %}{% trans 'Valid' %}
    -
    -
    -
    -
    -
    -
    - {% trans 'Quick create permission for user group' %} -
    -
    -
    - - - {% csrf_token %} - - - - - - - - - - - - - - - - -
    - {% bootstrap_field form.name %} -
    - {% bootstrap_field form.assets %} -
    - {% bootstrap_field form.asset_groups %} -
    - {% bootstrap_field form.system_users %} -
    - -
    -
    -
    -
    -
    -
    -
    -
    - - - -{% endblock %} -{% block custom_foot_js %} - -{% endblock %} diff --git a/apps/users/templates/users/user_group_create_update.html b/apps/users/templates/users/user_group_create_update.html index 4fc76389e..2a5727244 100644 --- a/apps/users/templates/users/user_group_create_update.html +++ b/apps/users/templates/users/user_group_create_update.html @@ -24,20 +24,21 @@
    {% csrf_token %} {% bootstrap_field form.name layout="horizontal" %} -
    - -
    - -
    -
    + {% bootstrap_field form.users layout="horizontal" %} +{#
    #} +{# #} +{#
    #} +{# #} +{#
    #} +{#
    #} {% bootstrap_field form.comment layout="horizontal" %}
    diff --git a/apps/users/templates/users/user_group_detail.html b/apps/users/templates/users/user_group_detail.html index 8bc04ca23..7f215231b 100644 --- a/apps/users/templates/users/user_group_detail.html +++ b/apps/users/templates/users/user_group_detail.html @@ -22,10 +22,7 @@ {% trans 'User group detail' %}
  • - {% trans 'Asset permission' %} -
  • -
  • - {% trans 'Asset granted' %} + {% trans 'Asset granted' %}
  • Update diff --git a/apps/users/templates/users/user_group_granted_asset.html b/apps/users/templates/users/user_group_granted_asset.html index 5749ac1f5..b0171e221 100644 --- a/apps/users/templates/users/user_group_granted_asset.html +++ b/apps/users/templates/users/user_group_granted_asset.html @@ -17,16 +17,13 @@
  • {% trans 'User detail' %}
  • -
  • - {% trans 'Asset permission' %} -
  • {% trans 'Asset granted' %}
  • -
    +
    {% trans 'Assets granted of ' %} {{ user_group.name }} @@ -62,7 +59,7 @@
    -
    +
    {% trans 'Asset groups granted of ' %} {{ user_group.name }} diff --git a/apps/users/templates/users/user_group_list.html b/apps/users/templates/users/user_group_list.html index 88a6d020a..bc909ceb4 100644 --- a/apps/users/templates/users/user_group_list.html +++ b/apps/users/templates/users/user_group_list.html @@ -16,18 +16,7 @@ -
    -
    - -
    - -
    -
    -
    + {% endblock %} {% block content_bottom_left %}{% endblock %} @@ -57,11 +46,12 @@ $(document).ready(function() { } else { $(td).html(update_btn + del_btn) } - }}], + }} + ], ajax_url: '{% url "api-users:user-group-list" %}', columns: [{data: function(){return ""}}, {data: "name" }, {data: "user_amount"}, {data: "comment"}, {data: "id" }], - order: [4, 'asc'], + order: [], op_html: $('#actions').html() }; jumpserver.initDataTable(options); diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index cd6e8e69e..177819e67 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -52,24 +52,8 @@ {% block custom_foot_js %}