mirror of
				https://github.com/jumpserver/jumpserver.git
				synced 2025-11-03 23:47:27 +00:00 
			
		
		
		
	* [Update] 初始化操作日志 * [Feature] 完成操作日志记录 * [Update] 修改mfa失败提示 * [Update] 修改增加created by内容 * [Update] 增加改密日志 * [Update] 登录日志迁移到日志审计中 * [Update] change block user logic, if login success, clean block limit * [Update] 更新中/英文翻译(ALL) (#1662) * Revert "授权页面分页问题" * 增加命令导出 (#1566) * [Update] gunicorn不使用eventlet * [Update] 添加eventlet * 替换淘宝IP查询接口 * [Feature] 添加命令记录下载功能 (#1559) * [Feature] 添加命令记录下载功能 * [Update] 文案修改,导出记录、提交,取消全部命令导出 * [Update] 命令导出,修复时间问题 * [Update] paramiko => 2.4.1 * [Update] 修改settings * [Update] 修改权限判断 * Dev (#1646) * [Update] 添加org * [Update] 修改url * [Update] 完成基本框架 * [Update] 修改一些逻辑 * [Update] 修改用户view * [Update] 修改资产 * [Update] 修改asset api * [Update] 修改协议小问题 * [Update] stash it * [Update] 修改约束 * [Update] 修改外键为org_id * [Update] 删掉Premiddleware * [Update] 修改Node * [Update] 修改get_current_org 为 proxy对象 current_org * [Bugfix] 解决Node.root() 死循环,移动AdminRequired到permission中 (#1571) * [Update] 修改permission (#1574) * Tmp org (#1579) * [Update] 添加org api, 升级到django 2.0 * [Update] fix some bug * [Update] 修改一些bug * [Update] 添加授权规则org (#1580) * [Update] 修复创建授权规则,显示org_name不是有效UUID的bug * [Update] 更新org之间隔离授权规则,解决QuerySet与Manager问题;修复创建用户,显示org_name不是有效UUID之bug; * Tmp org (#1583) * [Update] 修改一些内容 * [Update] 修改datatable 支持process * [Bugfix] 修复asset queryset 没有valid方法的bug * [Update] 在线/历史/命令model添加org;修复命令记录保存org失败bug (#1584) * [Update] 修复创建授权规则,显示org_name不是有效UUID的bug * [Update] 更新org之间隔离授权规则,解决QuerySet与Manager问题;修复创建用户,显示org_name不是有效UUID之bug; * [Update] 在线/历史/命令model添加org * [Bugfix] 修复命令记录,保存org不成功bug * [Update] Org功能修改 * [Bugfix] 修复merge带来的问题 * [Update] org admin显示资产详情右侧选项卡;修复资产授权添加用户,会显示其他org用户的bug (#1594) * [Bugfix] 修复资产授权添加用户,显示其他org的用户bug * [Update] org admin 显示资产详情右侧选项卡 * Tmp org (#1596) * [Update] 修改index view * [Update] 修改nav * [Update] 修改profile * [Bugfix] 修复org下普通用户打开web终端看不到已被授权的资产和节点bug * [Update] 修改get_all_assets * [Bugfix] 修复节点前面有个空目录 * [Bugfix] 修复merge引起的bug * [Update] Add init * [Update] Node get_all_assets 过滤游离资产,条件nodes_key=None -> nodes=None * [Update] 恢复原来的api地址 * [Update] 修改api * [Bugfix] 修复org下用户查看我的资产不显示已授权节点/资产的bug * [Bugfix] Fix perm name unique * [Bugfix] 修复校验失败api * [Update] Merge with org * [Merge] 修改一下bug * [Update] 暂时修改一些url * [Update] 修改url 为django 2.0 path * [Update] 优化datatable 和显示组织优化 * [Update] 升级url * [Bugfix] 修复coco启动失败(load_config_from_server)、硬件刷新,测试连接,str 没有 decode(… (#1613) * [Bugfix] 修复coco启动失败(load_config_from_server)、硬件刷新,测试连接,str 没有 decode() method的bug * [Bugfix] (task任务系统)修复资产连接性测试、硬件刷新和系统用户连接性测试失败等bug * [Bugfix] 修复一些bug * [Bugfix] 修复一些bug * [Update] 更新org下普通用户的资产详情 (#1619) * [Update] 更新org下普通用户查看资产详情,只显示数据 * [Update] 优化org下普通用户查看资产详情前端代码 * [Update] 创建/更新用户的role选项;密码强度提示信息中英文; (#1623) * [Update] 修改 超级管理员/组织管理员 在 创建/更新 用户时role的选项 问题 * [Update] 用户密码强度提示信息支持中英文 * [Update] 修改token返回 * [Update] Asset返回org name * [Update] 修改支持xpack * [Update] 修改url * [Bugfix] 修复不登录就能查看资产的bug * [Update] 用户修改 * [Bugfix] ... * [Bugfix] 修复跳转错误的问题 * [Update] xpack/orgs组织添加删除功能-js; 修复Label继承Org后bug; (#1644) * [Update] 更新xpack下orgs的翻译信息 * [Update] 更新model Label,继承OrgModelMixin; * [Update] xpack/orgs组织添加删除功能-js; 修复Label继承Org后bug; * [Bugfix] 修复小bug * [Update] 优化一些api * [Update] 优化用户资产页面 * [Update] 更新 xpack/orgs 删除功能:限制在当前org下删除当前org (#1645) * [Update] 修改版本号 * [Update] 添加功能: 语言切换(中/英);修改 header_bar <商业支持、文档>显示方式 * [Update] 中/英切换文案修改;修改django_language key 从 settings 中获取 * [Update] 修改Dashboard页面文案,支持英文 * [Update] 更新中/英文翻译(ALL) * [Update] 解决翻译文件冲突 * [Update] 系统用户支持单独隋松 * [Update] 重置用户MFA * [Update] 设置session空闲时间 * [Update] 加密setting配置 * [Update] 修改单独推送和测试资产可连接性 * [Update] 添加功能:用户个人详情页添加 更改MFA操作 (#1748) * [Update] 添加功能:用户个人详情页添加 更改MFA操作 * [Update] 删除print * [Bugfix] 添加部分views的权限控制;从组织移除用户,同时从授权规则和用户组中移除此用户。 (#1746) * [Bugfix] 修复上传command log 为空 * [Update] 修复执行任务的bug * [Bugfix] 修复将用户从组内移除,其依然具有之前的组权限的bug, perms and user_groups * [Bugfix] 修复组管理员可以访问部分url-views的bug(如: /settings/)添加views权限控制 * [Update] 修改日志滚动 * [Bugfix] 修复组织权限控制的bug (#1763) * [Bugfix] 修复将用户从组内移除,其依然具有之前的组权限的bug, perms and user_groups * [Bugfix] 修复组管理员可以访问部分url-views的bug(如: /settings/)添加views权限控制
		
			
				
	
	
		
			235 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			235 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# -*- coding: utf-8 -*-
 | 
						|
#
 | 
						|
import uuid
 | 
						|
 | 
						|
from django.core.cache import cache
 | 
						|
from django.urls import reverse
 | 
						|
from django.shortcuts import get_object_or_404
 | 
						|
from django.utils.translation import ugettext as _
 | 
						|
 | 
						|
from rest_framework.permissions import AllowAny
 | 
						|
from rest_framework.response import Response
 | 
						|
from rest_framework.views import APIView
 | 
						|
 | 
						|
from common.utils import get_logger, get_request_ip
 | 
						|
from ..serializers import UserSerializer
 | 
						|
from ..tasks import write_login_log_async
 | 
						|
from ..models import User, LoginLog
 | 
						|
from ..utils import check_user_valid, generate_token, \
 | 
						|
    check_otp_code, increase_login_failed_count, is_block_login, clean_failed_count
 | 
						|
from common.permissions import IsOrgAdminOrAppUser
 | 
						|
from ..hands import Asset, SystemUser
 | 
						|
 | 
						|
 | 
						|
logger = get_logger(__name__)
 | 
						|
 | 
						|
 | 
						|
class UserAuthApi(APIView):
 | 
						|
    permission_classes = (AllowAny,)
 | 
						|
    serializer_class = UserSerializer
 | 
						|
 | 
						|
    def post(self, request):
 | 
						|
        # limit login
 | 
						|
        username = request.data.get('username')
 | 
						|
        ip = request.data.get('remote_addr', None)
 | 
						|
        ip = ip or get_request_ip(request)
 | 
						|
 | 
						|
        if is_block_login(username, ip):
 | 
						|
            msg = _("Log in frequently and try again later")
 | 
						|
            logger.warn(msg + ': ' + username + ':' + ip)
 | 
						|
            return Response({'msg': msg}, status=401)
 | 
						|
 | 
						|
        user, msg = self.check_user_valid(request)
 | 
						|
        if not user:
 | 
						|
            data = {
 | 
						|
                'username': request.data.get('username', ''),
 | 
						|
                'mfa': LoginLog.MFA_UNKNOWN,
 | 
						|
                'reason': LoginLog.REASON_PASSWORD,
 | 
						|
                'status': False
 | 
						|
            }
 | 
						|
            self.write_login_log(request, data)
 | 
						|
            increase_login_failed_count(username, ip)
 | 
						|
            return Response({'msg': msg}, status=401)
 | 
						|
 | 
						|
        if not user.otp_enabled:
 | 
						|
            data = {
 | 
						|
                'username': user.username,
 | 
						|
                'mfa': int(user.otp_enabled),
 | 
						|
                'reason': LoginLog.REASON_NOTHING,
 | 
						|
                'status': True
 | 
						|
            }
 | 
						|
            self.write_login_log(request, data)
 | 
						|
            # 登陆成功,清除原来的缓存计数
 | 
						|
            clean_failed_count(username, ip)
 | 
						|
            token = generate_token(request, user)
 | 
						|
            return Response(
 | 
						|
                {
 | 
						|
                    'token': token,
 | 
						|
                    'user': self.serializer_class(user).data
 | 
						|
                }
 | 
						|
            )
 | 
						|
 | 
						|
        seed = uuid.uuid4().hex
 | 
						|
        cache.set(seed, user, 300)
 | 
						|
        return Response(
 | 
						|
            {
 | 
						|
                'code': 101,
 | 
						|
                'msg': _('Please carry seed value and '
 | 
						|
                         'conduct MFA secondary certification'),
 | 
						|
                'otp_url': reverse('api-users:user-otp-auth'),
 | 
						|
                'seed': seed,
 | 
						|
                'user': self.serializer_class(user).data
 | 
						|
            }, status=300
 | 
						|
        )
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    def check_user_valid(request):
 | 
						|
        username = request.data.get('username', '')
 | 
						|
        password = request.data.get('password', '')
 | 
						|
        public_key = request.data.get('public_key', '')
 | 
						|
        user, msg = check_user_valid(
 | 
						|
            username=username, password=password,
 | 
						|
            public_key=public_key
 | 
						|
        )
 | 
						|
        return user, msg
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    def write_login_log(request, data):
 | 
						|
        login_ip = request.data.get('remote_addr', None)
 | 
						|
        login_type = request.data.get('login_type', '')
 | 
						|
        user_agent = request.data.get('HTTP_USER_AGENT', '')
 | 
						|
 | 
						|
        if not login_ip:
 | 
						|
            login_ip = get_request_ip(request)
 | 
						|
 | 
						|
        tmp_data = {
 | 
						|
            'ip': login_ip,
 | 
						|
            'type': login_type,
 | 
						|
            'user_agent': user_agent,
 | 
						|
        }
 | 
						|
        data.update(tmp_data)
 | 
						|
 | 
						|
        write_login_log_async.delay(**data)
 | 
						|
 | 
						|
 | 
						|
class UserConnectionTokenApi(APIView):
 | 
						|
    permission_classes = (IsOrgAdminOrAppUser,)
 | 
						|
 | 
						|
    def post(self, request):
 | 
						|
        user_id = request.data.get('user', '')
 | 
						|
        asset_id = request.data.get('asset', '')
 | 
						|
        system_user_id = request.data.get('system_user', '')
 | 
						|
        token = str(uuid.uuid4())
 | 
						|
        user = get_object_or_404(User, id=user_id)
 | 
						|
        asset = get_object_or_404(Asset, id=asset_id)
 | 
						|
        system_user = get_object_or_404(SystemUser, id=system_user_id)
 | 
						|
        value = {
 | 
						|
            'user': user_id,
 | 
						|
            'username': user.username,
 | 
						|
            'asset': asset_id,
 | 
						|
            'hostname': asset.hostname,
 | 
						|
            'system_user': system_user_id,
 | 
						|
            'system_user_name': system_user.name
 | 
						|
        }
 | 
						|
        cache.set(token, value, timeout=20)
 | 
						|
        return Response({"token": token}, status=201)
 | 
						|
 | 
						|
    def get(self, request):
 | 
						|
        token = request.query_params.get('token')
 | 
						|
        user_only = request.query_params.get('user-only', None)
 | 
						|
        value = cache.get(token, None)
 | 
						|
 | 
						|
        if not value:
 | 
						|
            return Response('', status=404)
 | 
						|
 | 
						|
        if not user_only:
 | 
						|
            return Response(value)
 | 
						|
        else:
 | 
						|
            return Response({'user': value['user']})
 | 
						|
 | 
						|
    def get_permissions(self):
 | 
						|
        if self.request.query_params.get('user-only', None):
 | 
						|
            self.permission_classes = (AllowAny,)
 | 
						|
        return super().get_permissions()
 | 
						|
 | 
						|
 | 
						|
class UserToken(APIView):
 | 
						|
    permission_classes = (AllowAny,)
 | 
						|
 | 
						|
    def post(self, request):
 | 
						|
        if not request.user.is_authenticated:
 | 
						|
            username = request.data.get('username', '')
 | 
						|
            email = request.data.get('email', '')
 | 
						|
            password = request.data.get('password', '')
 | 
						|
            public_key = request.data.get('public_key', '')
 | 
						|
 | 
						|
            user, msg = check_user_valid(
 | 
						|
                username=username, email=email,
 | 
						|
                password=password, public_key=public_key)
 | 
						|
        else:
 | 
						|
            user = request.user
 | 
						|
            msg = None
 | 
						|
        if user:
 | 
						|
            token = generate_token(request, user)
 | 
						|
            return Response({'Token': token, 'Keyword': 'Bearer'}, status=200)
 | 
						|
        else:
 | 
						|
            return Response({'error': msg}, status=406)
 | 
						|
 | 
						|
 | 
						|
class UserOtpAuthApi(APIView):
 | 
						|
    permission_classes = (AllowAny,)
 | 
						|
    serializer_class = UserSerializer
 | 
						|
 | 
						|
    def post(self, request):
 | 
						|
        otp_code = request.data.get('otp_code', '')
 | 
						|
        seed = request.data.get('seed', '')
 | 
						|
 | 
						|
        user = cache.get(seed, None)
 | 
						|
        if not user:
 | 
						|
            return Response(
 | 
						|
                {'msg': _('Please verify the user name and password first')},
 | 
						|
                status=401
 | 
						|
            )
 | 
						|
 | 
						|
        if not check_otp_code(user.otp_secret_key, otp_code):
 | 
						|
            data = {
 | 
						|
                'username': user.username,
 | 
						|
                'mfa': int(user.otp_enabled),
 | 
						|
                'reason': LoginLog.REASON_MFA,
 | 
						|
                'status': False
 | 
						|
            }
 | 
						|
            self.write_login_log(request, data)
 | 
						|
            return Response({'msg': _('MFA certification failed')}, status=401)
 | 
						|
 | 
						|
        data = {
 | 
						|
            'username': user.username,
 | 
						|
            'mfa': int(user.otp_enabled),
 | 
						|
            'reason': LoginLog.REASON_NOTHING,
 | 
						|
            'status': True
 | 
						|
        }
 | 
						|
        self.write_login_log(request, data)
 | 
						|
        token = generate_token(request, user)
 | 
						|
        return Response(
 | 
						|
            {
 | 
						|
                'token': token,
 | 
						|
                'user': self.serializer_class(user).data
 | 
						|
             }
 | 
						|
        )
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    def write_login_log(request, data):
 | 
						|
        login_ip = request.data.get('remote_addr', None)
 | 
						|
        login_type = request.data.get('login_type', '')
 | 
						|
        user_agent = request.data.get('HTTP_USER_AGENT', '')
 | 
						|
 | 
						|
        if not login_ip:
 | 
						|
            login_ip = get_request_ip(request)
 | 
						|
 | 
						|
        tmp_data = {
 | 
						|
            'ip': login_ip,
 | 
						|
            'type': login_type,
 | 
						|
            'user_agent': user_agent
 | 
						|
        }
 | 
						|
        data.update(tmp_data)
 | 
						|
        write_login_log_async.delay(**data)
 |