jumpserver/apps/terminal/api/session.py
八千流 a2376d3afd 超级管理员可创建超级审计员并可设置审计员为组织审计员 (#3141)
* [Update] 超级管理员可创建超级审计员并可设置审计员为组织审计员

* [Update] 修改小问题

* [Update] 修改普通用户角色可以是组织审计员

* [Update] 更改组织审计员切换组织问题

* [Update] 修改小问题

* [Update] 普通用户是组织审计员的页面左侧栏显示

* [Update] 修改删除权限问题和组织显示问题

* [Update] 优化逻辑

* [Update] 优化类名

* [Update] 修改小问题

* [Update] 优化逻辑

* [Update] 优化切换到某一个组织逻辑

* [Update] 修改用户详情页的 删除/更新 按钮是否可点击

* [Update] 优化代码

* [Update] 组织管理列表增加审计员显示

* [Update] 优化代码细节

* [Update] 优化权限类逻辑

* [Update] 优化导航菜单控制

* [Update] 优化页面控制逻辑

* [Update] 修改变量名错误问题

* [Update] 修改页面上的小问题

* [Update] 审计员或组织审计员能够更新个人部分信息

* [Update] 用户名为admin的用户不能被删除

* [Update] 不同用户在不同组织下扮演不同角色的权限不同,为了避免切换组织时出现403,重定向到index

* [Update] 一个用户在同一个组织既是管理员又是审计员,隐藏个人信息模块,仅当是审计员,在当前组织显示个人信息模块

* [Update] 修改方法命名

* [Update] 优化代码细节

* [Update] 修改命令执行列表方法

* [Update] 优化用户之间操作的权限逻辑;添加 UserModel 的 property 属性;修改 Organization 的 related name 名称;

* [Update] 修改OrgProcessor Anonymous问题

* [Update] 修改用户序列类校验组织和转换raw密码的逻辑
2019-09-12 18:56:26 +08:00

127 lines
4.7 KiB
Python

# -*- coding: utf-8 -*-
#
import os
from django.shortcuts import get_object_or_404
from django.core.files.storage import default_storage
from django.http import HttpResponseNotFound
from django.conf import settings
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
import jms_storage
from common.utils import is_uuid, get_logger
from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor
from common.filters import DatetimeRangeFilter
from orgs.mixins.api import OrgBulkModelViewSet
from ..hands import SystemUser
from ..models import Session
from .. import serializers
__all__ = ['SessionViewSet', 'SessionReplayViewSet',]
logger = get_logger(__name__)
class SessionViewSet(OrgBulkModelViewSet):
queryset = Session.objects.all()
serializer_class = serializers.SessionSerializer
permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor, )
filter_fields = [
"user", "asset", "system_user", "remote_addr",
"protocol", "terminal", "is_finished",
]
date_range_filter_fields = [
('date_start', ('date_from', 'date_to'))
]
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
# 解决guacamole更新session时并发导致幽灵会话的问题
if self.request.method in ('PATCH',):
queryset = queryset.select_for_update()
return queryset
@property
def filter_backends(self):
backends = list(GenericAPIView.filter_backends)
backends.append(DatetimeRangeFilter)
return backends
def perform_create(self, serializer):
if hasattr(self.request.user, 'terminal'):
serializer.validated_data["terminal"] = self.request.user.terminal
sid = serializer.validated_data["system_user"]
# guacamole提交的是id
if is_uuid(sid):
_system_user = get_object_or_404(SystemUser, id=sid)
serializer.validated_data["system_user"] = _system_user.name
return super().perform_create(serializer)
class SessionReplayViewSet(viewsets.ViewSet):
serializer_class = serializers.ReplaySerializer
permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor,)
session = None
def create(self, request, *args, **kwargs):
session_id = kwargs.get('pk')
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']
name, err = session.save_to_storage(file)
if not name:
msg = "Failed save replay `{}`: {}".format(session_id, err)
logger.error(msg)
return Response({'msg': str(err)}, status=400)
url = default_storage.url(name)
return Response({'url': url}, status=201)
else:
msg = 'Upload data invalid: {}'.format(serializer.errors)
logger.error(msg)
return Response({'msg': serializer.errors}, status=401)
def retrieve(self, request, *args, **kwargs):
session_id = kwargs.get('pk')
session = get_object_or_404(Session, id=session_id)
tp = 'json'
if session.protocol in ('rdp', 'vnc'):
tp = 'guacamole'
data = {'type': tp, 'src': ''}
# 新版本和老版本的文件后缀不同
session_path = session.get_rel_replay_path() # 存在外部存储上的路径
local_path = session.get_local_path()
local_path_v1 = session.get_local_path(version=1)
# 去default storage中查找
for _local_path in (local_path, local_path_v1, session_path):
if default_storage.exists(_local_path):
url = default_storage.url(_local_path)
data['src'] = url
return Response(data)
# 去定义的外部storage查找
configs = settings.TERMINAL_REPLAY_STORAGE
configs = {k: v for k, v in configs.items() if v['TYPE'] != 'server'}
if not configs:
return HttpResponseNotFound()
target_path = os.path.join(default_storage.base_location, local_path) # 保存到storage的路径
target_dir = os.path.dirname(target_path)
if not os.path.isdir(target_dir):
os.makedirs(target_dir, exist_ok=True)
storage = jms_storage.get_multi_object_storage(configs)
ok, err = storage.download(session_path, target_path)
if not ok:
logger.error("Failed download replay file: {}".format(err))
return HttpResponseNotFound()
data['src'] = default_storage.url(local_path)
return Response(data)