Compare commits

..

37 Commits

Author SHA1 Message Date
fit2bot
c28cca7ca2 feat: Update v2.28.20 2023-09-26 14:51:33 +08:00
ibuler
29ab6bbee7 fix: pubkey auth require svc sign 2023-09-25 23:31:41 +08:00
ibuler
3352f78e01 perf: 修改验证码多次验证 2023-09-25 23:28:17 +08:00
吴小白
39affc4989 perf: 添加 patch 命令 2023-09-22 10:25:00 +08:00
ibuler
42337f0d00 perf: 修复随机 error 2023-09-19 18:18:51 +08:00
Bai
31d2bdd3bb perf: 优化任务列表支持通过 celery_task_id 查询执行的任务 2023-08-10 12:59:35 +05:00
fit2bot
ee9687743e fix: 锁定依赖 Cython==0.29.35 --no-build-isolation pymssql==2.1.5 (#11063)
* fix: 增加 pip 参数 --use-deprecated=legacy-resolver

* fix: 升级依赖 pymssql==2.2.7 oracledb==1.2.2

* fix: 升级依赖 git+https://github.com/marcwimmer/pymssql@fix_cython_compiler_version

* fix: 锁定依赖 Cython==0.29.22

* fix: 锁定依赖 Cython==0.29.35 --no-build-isolation pymssql==2.1.5

* fix: 锁定依赖 Cython==0.29.35 --no-build-isolation pymssql==2.1.5

* fix: 锁定依赖

* fix: 锁定依赖

* fix: 锁定依赖

* fix: 锁定依赖

* fix: 锁定依赖

---------

Co-authored-by: Bai <baijiangjie@gmail.com>
2023-07-25 16:59:29 +08:00
老广
d5a9942159 Merge pull request #11002 from jumpserver/pr@v2.28@ticket
fix: 工单时区问题 rdp文件添加disableconnectionsharing 参数
2023-07-18 18:15:50 +08:00
feng
3b92e9e516 fix: 工单时区问题 rdp文件添加disableconnectionsharing 参数 2023-07-18 06:34:38 -03:00
Bai
0132eafeb6 fix: 修复导入LDAP数据库超时导致 Lock wait timeout 的问题 2023-07-18 10:49:02 +08:00
Bai
fb286f4665 fix: 增加 TypeError 捕获 2023-07-11 11:13:13 +08:00
Bai
daeba109fd fix: 修复 beat 定时任务重复执行的问题 2023-07-11 11:13:13 +08:00
ibuler
06411b080e perf: 优化rbac 迁移 2023-06-20 16:34:34 +08:00
老广
5da6a1b9b6 Merge pull request #10771 from jumpserver/pr@v2.28@fix_image
fix: 修正基础镜像名称
2023-06-19 16:22:11 +08:00
吴小白
988f33634e fix: 修正基础镜像名称 2023-06-19 16:20:43 +08:00
吴小白
a645dc09ae fix: 修正快速安装脚本版本参数 2023-06-13 14:22:10 +08:00
feng
4e4e58480f fix: authbook 没有的值取 systemuser 2023-06-08 15:36:19 +08:00
feng
4a1f3ed727 fix: 特权操作时 同时加载密钥密码 2023-06-08 10:20:20 +08:00
Bai
a10bb29a1e fix: 修复终端端点使用资产标签匹配机制时 500 的问题 2023-05-24 17:37:15 +08:00
老广
73aeb021cc Merge pull request #10490 from O-Jiangweidong/pr@v2.28@fix_mfa_bypass
fix: 修复某待审核用户返回时,登录其他用户可绕开mfa的问题
2023-05-19 10:40:52 +08:00
jiangweidong
3d66fe4758 fix: 缩进 2023-05-18 14:58:07 +08:00
jiangweidong
ec2071a6ca fix: 修复某待审核用户返回时,登录其他用户可绕开mfa的问题 2023-05-18 14:48:22 +08:00
fit2bot
beb43aa726 fix: 工单comment改成md格式 (#10419)
Co-authored-by: feng <1304903146@qq.com>
2023-05-10 15:19:31 +08:00
Bai
8a77a7b8b5 perf: 优化系统用户和资产,只有协议包含时才进行关联和推送 2023-05-08 19:07:59 +08:00
Bai
7eed182627 perf: 优化系统用户和资产,只有协议包含时才进行关联和推送 2023-05-08 18:14:19 +08:00
feng626
ec847d3ecb Fix v2.28.7 ssh key (#10399)
* feat: Update v2.28.7

* fix: 修复旧 ssh 私钥,解析失败的问题

* perf: 解决历史版本中因保存密码,造成 ssh 私钥解析失败问题

* fix: 动态用户可执行批量任务

---------

Co-authored-by: fit2bot <fit2bot@fit2cloud.com>
Co-authored-by: Eric <xplzv@126.com>
2023-05-08 14:28:23 +08:00
Eric
a0994e2e12 fix: 修复旧 ssh 私钥,解析失败的问题 2023-04-27 17:49:52 +08:00
ibuler
17e3ddda05 perf: 支持 rdp console 2023-04-25 14:35:28 +08:00
Bai
6e2e92be5e fix: perf: 修改OAuth2的access_token前缀格式 2023-04-20 14:59:54 +08:00
老广
e90d8c8561 Merge pull request #10121 from jumpserver/pr@v2.28@fix_ldapuserimport_v2.28
fix: 修复 LDAP 导入用户时指定其他组织,还会导入到 Default 组织的问题
2023-04-03 17:09:58 +08:00
Bai
cf972942fa fix: 修复 LDAP 导入用户时指定其他组织,还会导入到 Default 组织的问题 2023-04-03 16:54:14 +08:00
老广
72e35d5553 Merge pull request #10046 from jumpserver/pr@v2.28@fix_systemuserlist
fix: 去掉系统用户序列类中的资产数量和应用数量
2023-03-23 14:13:33 +08:00
Bai
0ba84e7e18 fix: 去掉系统用户序列类中的资产数量和应用数量 2023-03-23 11:52:18 +08:00
Bai
fbc5ae1b9b fix: 修复日志记录到syslog时中文编码问题 2023-03-15 19:45:40 +08:00
halo
2fcf045826 fix: 修复celery api 报错 2023-03-15 15:36:54 +08:00
Bai
32cba4f2a1 fix: 修复语言切换问题 2023-03-14 14:40:23 +08:00
Jiangjie.Bai
b76aa3b259 feat: 支持飞书国际版(lark) (#9916) 2023-03-10 15:49:15 +08:00
40 changed files with 397 additions and 202 deletions

View File

@@ -21,7 +21,7 @@ jobs:
TAG=$(basename ${GITHUB_REF})
VERSION=${TAG/v/}
wget https://raw.githubusercontent.com/jumpserver/installer/master/quick_start.sh
sed -i "s@Version=.*@Version=v${VERSION}@g" quick_start.sh
sed -i "s@VERSION=dev@VERSION=v${VERSION}@g" quick_start.sh
echo "::set-output name=TAG::$TAG"
echo "::set-output name=VERSION::$VERSION"
- name: Create Release

View File

@@ -1,4 +1,4 @@
FROM python:3.8-slim as stage-build
FROM python:3.8-slim-bullseye as stage-build
ARG TARGETARCH
ARG VERSION
@@ -8,7 +8,7 @@ WORKDIR /opt/jumpserver
ADD . .
RUN cd utils && bash -ixeu build.sh
FROM python:3.8-slim
FROM python:3.8-slim-bullseye
ARG TARGETARCH
MAINTAINER JumpServer Team <ibuler@qq.com>
@@ -38,6 +38,7 @@ ARG TOOLS=" \
default-mysql-client \
iputils-ping \
locales \
patch \
procps \
redis-tools \
telnet \
@@ -87,8 +88,10 @@ RUN --mount=type=cache,target=/root/.cache/pip \
&& pip config set global.index-url ${PIP_MIRROR} \
&& pip install --upgrade pip \
&& pip install --upgrade setuptools wheel \
&& pip install Cython==0.29.35 \
&& pip install --no-build-isolation pymssql \
&& pip install $(grep -E 'jms|jumpserver' requirements/requirements.txt) -i ${PIP_JMS_MIRROR} \
&& pip install -r requirements/requirements.txt
&& pip install -r requirements/requirements.txt --use-deprecated=legacy-resolver
COPY --from=stage-build /opt/jumpserver/release/jumpserver /opt/jumpserver
RUN echo > /opt/jumpserver/config.yml \

2
GITSHA
View File

@@ -1 +1 @@
3f9a17347d8d6ac785054088f7113ab9a9c506cb
29ab6bbee7f1ac2dee1826e42b65039f260f6fd0

View File

@@ -2,9 +2,8 @@
# -*- coding: utf-8 -*-
#
import uuid
import logging
from functools import reduce
import uuid
from collections import OrderedDict
from django.db import models
@@ -14,7 +13,6 @@ from rest_framework.exceptions import ValidationError
from common.db.fields import JsonDictTextField
from common.utils import lazyproperty
from orgs.mixins.models import OrgModelMixin, OrgManager
from .base import AbsConnectivity
__all__ = ['Asset', 'ProtocolsMixin', 'Platform', 'AssetQuerySet']

View File

@@ -89,8 +89,7 @@ class AuthMixin:
def private_key_file(self):
if not self.private_key:
return None
private_key_str = parse_ssh_private_key_str(self.private_key,
password=self.password)
private_key_str = self.get_private_key()
if not private_key_str:
return None
project_dir = settings.PROJECT_DIR
@@ -106,8 +105,11 @@ class AuthMixin:
def get_private_key(self):
if not self.private_key:
return None
return parse_ssh_private_key_str(self.private_key,
password=self.password)
private_key_str = parse_ssh_private_key_str(self.private_key, password=self.password)
if not private_key_str and self.password:
# 由于历史原因,密码可能是真实的密码,而非私钥的 passphrase所以这里再尝试一次
private_key_str = parse_ssh_private_key_str(self.private_key)
return private_key_str
@property
def public_key_obj(self):

View File

@@ -4,16 +4,16 @@
import logging
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.cache import cache
from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from common.utils import signer, get_object_or_none
from .base import BaseUser
from common.utils import signer, get_object_or_none, is_uuid
from .asset import Asset
from .authbook import AuthBook
from .base import BaseUser
__all__ = ['AdminUser', 'SystemUser']
logger = logging.getLogger(__name__)
@@ -187,22 +187,22 @@ class AuthMixin:
if username == '':
username = self.username
authbook = AuthBook.objects.filter(
asset=asset, username=username, systemuser__isnull=True
).order_by('-date_created').first()
not_stu_query = Q(asset=asset, username=username, systemuser__isnull=True)
stu_query = Q(asset=asset, systemuser=self)
not_stu_qs = AuthBook.objects.filter(not_stu_query).order_by('-date_created')
stu_qs = AuthBook.objects.filter(stu_query).order_by('-date_created')
authbook = not_stu_qs.first()
if not authbook:
authbook = AuthBook.objects.filter(
asset=asset, systemuser=self
).order_by('-date_created').first()
authbook = stu_qs.first()
if not authbook:
return None
authbook.load_auth()
self.password = authbook.password
self.private_key = authbook.private_key
self.public_key = authbook.public_key
self.password = authbook.password or self.password or ''
self.private_key = authbook.private_key or self.private_key or ''
self.public_key = authbook.public_key or self.public_key or ''
def load_asset_more_auth(self, asset_id=None, username=None, user_id=None):
from users.models import User
@@ -249,12 +249,19 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
users = models.ManyToManyField('users.User', blank=True, verbose_name=_("Users"))
groups = models.ManyToManyField('users.UserGroup', blank=True, verbose_name=_("User groups"))
type = models.CharField(max_length=16, choices=Type.choices, default=Type.common, verbose_name=_('Type'))
priority = models.IntegerField(default=81, verbose_name=_("Priority"), help_text=_("1-100, the lower the value will be match first"), validators=[MinValueValidator(1), MaxValueValidator(100)])
protocol = models.CharField(max_length=16, choices=ProtocolMixin.Protocol.choices, default='ssh', verbose_name=_('Protocol'))
priority = models.IntegerField(
default=81, verbose_name=_("Priority"),
help_text=_("1-100, the lower the value will be match first"),
validators=[MinValueValidator(1), MaxValueValidator(100)]
)
protocol = models.CharField(max_length=16, choices=ProtocolMixin.Protocol.choices, default='ssh',
verbose_name=_('Protocol'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=LOGIN_AUTO, max_length=10, verbose_name=_('Login mode'))
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
login_mode = models.CharField(
choices=LOGIN_MODE_CHOICES, default=LOGIN_AUTO, max_length=10, verbose_name=_('Login mode')
)
sftp_root = models.CharField(default='tmp', max_length=128, verbose_name=_("SFTP Root"))
token = models.TextField(default='', verbose_name=_('Token'))
home = models.CharField(max_length=4096, default='', verbose_name=_('Home'), blank=True)
@@ -262,7 +269,9 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
ad_domain = models.CharField(default='', max_length=256)
# linux su 命令 (switch user)
su_enabled = models.BooleanField(default=False, verbose_name=_('User switch'))
su_from = models.ForeignKey('self', on_delete=models.SET_NULL, related_name='su_to', null=True, verbose_name=_("Switch from"))
su_from = models.ForeignKey(
'self', on_delete=models.SET_NULL, related_name='su_to', null=True, verbose_name=_("Switch from")
)
def __str__(self):
username = self.username
@@ -322,9 +331,20 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
assets = Asset.objects.filter(id__in=asset_ids)
return assets
def filter_contain_protocol_assets(self, assets_or_ids):
if not assets_or_ids:
return assets_or_ids
if is_uuid(assets_or_ids[0]):
assets = Asset.objects.filter(id__in=assets_or_ids)
else:
assets = assets_or_ids
assets = [asset for asset in assets if self.protocol in asset.protocols_as_dict]
return assets
def add_related_assets(self, assets_or_ids):
self.assets.add(*tuple(assets_or_ids))
self.add_related_assets_to_su_from_if_need(assets_or_ids)
assets = self.filter_contain_protocol_assets(assets_or_ids)
self.assets.add(*tuple(assets))
self.add_related_assets_to_su_from_if_need(assets)
def add_related_assets_to_su_from_if_need(self, assets_or_ids):
if self.protocol not in [self.Protocol.ssh.value]:

View File

@@ -36,9 +36,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
token = EncryptedField(
label=_('Token'), required=False, write_only=True, style={'base_template': 'textarea.html'}
)
applications_amount = serializers.IntegerField(
source='apps_amount', read_only=True, label=_('Apps amount')
)
class Meta:
model = SystemUser
@@ -53,7 +50,7 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
'su_enabled', 'su_from',
'date_created', 'date_updated', 'comment', 'created_by',
]
fields_m2m = ['cmd_filters', 'assets_amount', 'applications_amount', 'nodes']
fields_m2m = ['cmd_filters', 'nodes']
fields = fields_small + fields_m2m
extra_kwargs = {
'cmd_filters': {"required": False, 'label': _('Command filter')},
@@ -241,7 +238,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """
queryset = queryset \
.annotate(assets_amount=Count("assets")) \
.prefetch_related('nodes', 'cmd_filters')
return queryset

View File

@@ -50,6 +50,10 @@ def clean_ansible_task_hosts(assets, system_user=None):
for asset in assets:
if not check_asset_can_run_ansible(asset):
continue
# 资产平台不包含系统用户的协议, 不推送
if system_user and system_user.protocol not in asset.protocols_as_dict:
logger.info(_('Asset protocol not support system user protocol, skipped: {}').format(system_user.protocol))
continue
cleaned_assets.append(asset)
if not cleaned_assets:
logger.info(_("No assets matched, stop task"))

View File

@@ -1,26 +1,27 @@
import abc
import os
import json
import base64
import json
import os
import urllib.parse
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from rest_framework.exceptions import PermissionDenied
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.request import Request
from rest_framework.response import Response
from common.drf.api import JMSModelViewSet
from common.http import is_true
from orgs.mixins.api import RootOrgViewMixin
from perms.models.base import Action
from terminal.models import EndpointRule
from ..models import ConnectionToken
from ..serializers import (
ConnectionTokenSerializer, ConnectionTokenSecretSerializer,
SuperConnectionTokenSerializer, ConnectionTokenDisplaySerializer,
)
from ..models import ConnectionToken
__all__ = ['ConnectionTokenViewSet', 'SuperConnectionTokenViewSet']
@@ -124,6 +125,7 @@ class ConnectionTokenMixin:
'bookmarktype:i': '3',
'use redirection server name:i': '0',
'smart sizing:i': '1',
'disableconnectionsharing:i': '1',
# 'drivestoredirect:s': '*',
# 'domain:s': ''
# 'alternate shell:s:': '||MySQLWorkbench',
@@ -165,6 +167,9 @@ class ConnectionTokenMixin:
rdp_options['session bpp:i'] = os.getenv('JUMPSERVER_COLOR_DEPTH', '32')
rdp_options['audiomode:i'] = self.parse_env_bool('JUMPSERVER_DISABLE_AUDIO', 'false', '2', '0')
if token.asset and token.asset.platform.meta.get('console', None) == 'true':
rdp_options['administrative session:i:'] = '1'
if token.asset:
name = token.asset.hostname
elif token.application and token.application.category_remote_app:

View File

@@ -119,7 +119,7 @@ class OAuth2Backend(JMSModelBackend):
headers = {
'Accept': 'application/json',
'Authorization': 'token {}'.format(response_data.get('access_token', ''))
'Authorization': 'Bearer {}'.format(response_data.get('access_token', ''))
}
logger.debug(log_prompt.format('Get userinfo endpoint'))

View File

@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
#
from django.contrib.auth import get_user_model
from django.conf import settings
from django.contrib.auth import get_user_model
from common.permissions import ServiceAccountSignaturePermission
from .base import JMSBaseAuthBackend
UserModel = get_user_model()
@@ -18,6 +19,10 @@ class PublicKeyAuthBackend(JMSBaseAuthBackend):
def authenticate(self, request, username=None, public_key=None, **kwargs):
if not public_key:
return None
permission = ServiceAccountSignaturePermission()
if not permission.has_permission(request, None):
return None
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
@@ -26,7 +31,7 @@ class PublicKeyAuthBackend(JMSBaseAuthBackend):
return None
else:
if user.check_public_key(public_key) and \
self.user_can_authenticate(user):
self.user_can_authenticate(user):
return user
def get_user(self, user_id):

View File

@@ -212,7 +212,8 @@ class MFAMixin:
self._do_check_user_mfa(code, mfa_type, user=user)
def check_user_mfa_if_need(self, user):
if self.request.session.get('auth_mfa'):
if self.request.session.get('auth_mfa') and \
self.request.session.get('auth_mfa_username') == user.username:
return
if not user.mfa_enabled:
return
@@ -220,15 +221,16 @@ class MFAMixin:
active_mfa_names = user.active_mfa_backends_mapper.keys()
raise errors.MFARequiredError(mfa_types=tuple(active_mfa_names))
def mark_mfa_ok(self, mfa_type):
def mark_mfa_ok(self, mfa_type, user):
self.request.session['auth_mfa'] = 1
self.request.session['auth_mfa_username'] = user.username
self.request.session['auth_mfa_time'] = time.time()
self.request.session['auth_mfa_required'] = 0
self.request.session['auth_mfa_type'] = mfa_type
MFABlockUtils(self.request.user.username, self.get_request_ip()).clean_failed_count()
MFABlockUtils(user.username, self.get_request_ip()).clean_failed_count()
def clean_mfa_mark(self):
keys = ['auth_mfa', 'auth_mfa_time', 'auth_mfa_required', 'auth_mfa_type']
keys = ['auth_mfa', 'auth_mfa_time', 'auth_mfa_required', 'auth_mfa_type', 'auth_mfa_username']
for k in keys:
self.request.session.pop(k, '')
@@ -263,7 +265,7 @@ class MFAMixin:
ok, msg = mfa_backend.check_code(code)
if ok:
self.mark_mfa_ok(mfa_type)
self.mark_mfa_ok(mfa_type, user)
return
raise errors.MFAFailedError(
@@ -519,8 +521,14 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost
def set_browser_default_language_if_need(self, response):
# en, ja, zh-CN,zh;q=0.9
default_lang = self.request.headers.get('Accept-Language')
if 'zh' in default_lang:
default_lang = 'zh'
lang = response.cookies.get(settings.LANGUAGE_COOKIE_NAME) or default_lang
browser_lang = self.request.headers.get('Accept-Language', '')
# 浏览器首选语言
if browser_lang.startswith('en'):
browser_lang = 'en'
elif browser_lang.startswith('ja'):
browser_lang = 'ja'
else:
browser_lang = 'zh'
request_lang = self.request.LANGUAGE_CODE
lang = request_lang or browser_lang
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang)

View File

@@ -60,7 +60,7 @@ class FeiShuQRMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, View):
'state': state,
'redirect_uri': redirect_uri,
}
url = URL.AUTHEN + '?' + urlencode(params)
url = URL().authen + '?' + urlencode(params)
return url
@staticmethod

View File

@@ -81,3 +81,38 @@ class UserConfirmation(permissions.BasePermission):
min_level = ConfirmType.values.index(confirm_type) + 1
name = 'UserConfirmationLevel{}TTL{}'.format(min_level, ttl)
return type(name, (cls,), {'min_level': min_level, 'ttl': ttl, 'confirm_type': confirm_type})
class ServiceAccountSignaturePermission(permissions.BasePermission):
def has_permission(self, request, view):
from authentication.models import AccessKey
from common.utils.crypto import get_aes_crypto
signature = request.META.get('HTTP_X_JMS_SVC', '')
if not signature or not signature.startswith('Sign'):
return False
data = signature[4:].strip()
if not data or ':' not in data:
return False
ak_id, time_sign = data.split(':', 1)
if not ak_id or not time_sign:
return False
ak = AccessKey.objects.filter(id=ak_id).first()
if not ak or not ak.is_active:
return False
if not ak.user or not ak.user.is_active or not ak.user.is_service_account:
return False
aes = get_aes_crypto(str(ak.secret).replace('-', ''), mode='ECB')
try:
timestamp = aes.decrypt(time_sign)
if not timestamp or not timestamp.isdigit():
return False
timestamp = int(timestamp)
interval = abs(int(time.time()) - timestamp)
if interval > 30:
return False
return True
except Exception:
return False
def has_object_permission(self, request, view, obj):
return False

View File

@@ -3,6 +3,7 @@ import json
from django.utils.translation import ugettext_lazy as _
from rest_framework.exceptions import APIException
from django.conf import settings
from common.utils.common import get_logger
from common.sdk.im.utils import digest
from common.sdk.im.mixin import RequestMixin, BaseRequest
@@ -11,14 +12,30 @@ logger = get_logger(__name__)
class URL:
AUTHEN = 'https://open.feishu.cn/open-apis/authen/v1/index'
GET_TOKEN = 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/'
# https://open.feishu.cn/document/ukTMukTMukTM/uEDO4UjLxgDO14SM4gTN
GET_USER_INFO_BY_CODE = 'https://open.feishu.cn/open-apis/authen/v1/access_token'
@property
def host(self):
if settings.FEISHU_VERSION == 'feishu':
h = 'https://open.feishu.cn'
else:
h = 'https://open.larksuite.com'
return h
SEND_MESSAGE = 'https://open.feishu.cn/open-apis/im/v1/messages'
@property
def authen(self):
return f'{self.host}/open-apis/authen/v1/index'
@property
def get_token(self):
return f'{self.host}/open-apis/auth/v3/tenant_access_token/internal/'
@property
def get_user_info_by_code(self):
return f'{self.host}/open-apis/authen/v1/access_token'
@property
def send_message(self):
return f'{self.host}/open-apis/im/v1/messages'
class ErrorCode:
@@ -51,7 +68,7 @@ class FeishuRequests(BaseRequest):
def request_access_token(self):
data = {'app_id': self._app_id, 'app_secret': self._app_secret}
response = self.raw_request('post', url=URL.GET_TOKEN, data=data)
response = self.raw_request('post', url=URL().get_token, data=data)
self.check_errcode_is_0(response)
access_token = response['tenant_access_token']
@@ -86,7 +103,7 @@ class FeiShu(RequestMixin):
'code': code
}
data = self._requests.post(URL.GET_USER_INFO_BY_CODE, json=body, check_errcode_is_0=False)
data = self._requests.post(URL().get_user_info_by_code, json=body, check_errcode_is_0=False)
self._requests.check_errcode_is_0(data)
return data['data']['user_id']
@@ -107,7 +124,7 @@ class FeiShu(RequestMixin):
try:
logger.info(f'Feishu send text: user_ids={user_ids} msg={msg}')
self._requests.post(URL.SEND_MESSAGE, params=params, json=body)
self._requests.post(URL().send_message, params=params, json=body)
except APIException as e:
# 只处理可预知的错误
logger.exception(e)

View File

@@ -173,6 +173,8 @@ def _parse_ssh_private_key(text, password=None):
dsa.DSAPrivateKey,
ed25519.Ed25519PrivateKey,
"""
if not bool(password):
password = None
if isinstance(text, str):
try:
text = text.encode("utf-8")
@@ -274,4 +276,4 @@ def ensure_last_char_is_ascii(data):
def data_to_json(data, sort_keys=True, indent=2, cls=None):
if cls is None:
cls = DjangoJSONEncoder
return json.dumps(data, sort_keys=sort_keys, indent=indent, cls=cls)
return json.dumps(data, ensure_ascii=False, sort_keys=sort_keys, indent=indent, cls=cls)

View File

@@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
#
import struct
import random
import socket
import string
import struct
string_punctuation = '!#$%&()*+,-.:;<=>?@[]^_~'
@@ -19,6 +18,7 @@ def random_ip():
def random_string(length: int, lower=True, upper=True, digit=True, special_char=False):
random.seed()
args_names = ['lower', 'upper', 'digit', 'special_char']
args_values = [lower, upper, digit, special_char]
args_string = [string.ascii_lowercase, string.ascii_uppercase, string.digits, string_punctuation]

View File

@@ -1,14 +1,13 @@
from django.core.cache import cache
from django.conf import settings
from django.core.mail import send_mail
from celery import shared_task
from django.conf import settings
from django.core.cache import cache
from django.core.mail import send_mail
from common.sdk.sms.exceptions import CodeError, CodeExpired, CodeSendTooFrequently
from common.sdk.sms.endpoint import SMS
from common.exceptions import JMSException
from common.utils.random import random_string
from common.sdk.sms.endpoint import SMS
from common.sdk.sms.exceptions import CodeError, CodeExpired, CodeSendTooFrequently
from common.utils import get_logger
from common.utils.random import random_string
logger = get_logger(__file__)
@@ -27,6 +26,7 @@ class SendAndVerifyCodeUtil(object):
self.timeout = timeout
self.backend = backend
self.key = key or self.KEY_TMPL.format(target)
self.verify_key = self.key + '_verify'
self.other_args = kwargs
def gen_and_send_async(self):
@@ -47,6 +47,11 @@ class SendAndVerifyCodeUtil(object):
raise
def verify(self, code):
times = cache.get(self.verify_key, 0)
if times >= 3:
self.__clear()
raise CodeExpired
cache.set(self.verify_key, times + 1, timeout=self.timeout)
right = cache.get(self.key)
if not right:
raise CodeExpired
@@ -59,6 +64,7 @@ class SendAndVerifyCodeUtil(object):
def __clear(self):
cache.delete(self.key)
cache.delete(self.verify_key)
def __ttl(self):
return cache.ttl(self.key)

View File

@@ -376,6 +376,7 @@ class Config(dict):
'AUTH_FEISHU': False,
'FEISHU_APP_ID': '',
'FEISHU_APP_SECRET': '',
'FEISHU_VERSION': 'feishu',
'LOGIN_REDIRECT_TO_BACKEND': '', # 'OPENID / CAS / SAML2
'LOGIN_REDIRECT_MSG_ENABLED': True,

View File

@@ -137,6 +137,7 @@ DINGTALK_APPSECRET = CONFIG.DINGTALK_APPSECRET
AUTH_FEISHU = CONFIG.AUTH_FEISHU
FEISHU_APP_ID = CONFIG.FEISHU_APP_ID
FEISHU_APP_SECRET = CONFIG.FEISHU_APP_SECRET
FEISHU_VERSION = CONFIG.FEISHU_VERSION
# Saml2 auth
AUTH_SAML2 = CONFIG.AUTH_SAML2

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7db985efdf818137dafe489f339955f4d71a245ffd6becc8f6efac539a625682
size 133463
oid sha256:0cc37a87259f2cd3794ad628ee40497bd97beaee7219be3d9773e1e544c3e352
size 134193

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-03-02 12:21+0800\n"
"POT-Creation-Date: 2023-05-08 18:10+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -131,7 +131,7 @@ msgstr "システムユーザー"
#: assets/models/asset.py:386 assets/models/authbook.py:19
#: assets/models/backup.py:31 assets/models/cmd_filter.py:42
#: assets/models/gathered_user.py:14 assets/serializers/label.py:30
#: assets/serializers/system_user.py:268 audits/models.py:40
#: assets/serializers/system_user.py:264 audits/models.py:40
#: authentication/models.py:66 authentication/models.py:90
#: perms/models/asset_permission.py:23 terminal/backends/command/models.py:21
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:47
@@ -269,7 +269,7 @@ msgid "Application"
msgstr "アプリケーション"
#: applications/models/account.py:15 assets/models/authbook.py:20
#: assets/models/cmd_filter.py:46 assets/models/user.py:343 audits/models.py:41
#: assets/models/cmd_filter.py:46 assets/models/user.py:354 audits/models.py:41
#: authentication/models.py:83 perms/models/application_permission.py:33
#: perms/models/asset_permission.py:25 terminal/backends/command/models.py:22
#: terminal/backends/command/serializers.py:36 terminal/models/session.py:49
@@ -280,7 +280,7 @@ msgid "System user"
msgstr "システムユーザー"
#: applications/models/account.py:17 assets/models/authbook.py:21
#: settings/serializers/auth/cas.py:20
#: settings/serializers/auth/cas.py:20 settings/serializers/auth/feishu.py:20
msgid "Version"
msgstr "バージョン"
@@ -445,7 +445,7 @@ msgid "Application path"
msgstr "アプリケーションパス"
#: applications/serializers/attrs/application_category/remote_app.py:44
#: assets/serializers/system_user.py:167
#: assets/serializers/system_user.py:164
#: tickets/serializers/ticket/apply_application.py:38
#: tickets/serializers/ticket/common.py:59
#: xpack/plugins/change_auth_plan/serializers/asset.py:67
@@ -634,7 +634,7 @@ msgid "Is active"
msgstr "アクティブです。"
#: assets/models/asset.py:222 assets/models/cluster.py:19
#: assets/models/user.py:240 assets/models/user.py:395
#: assets/models/user.py:240 assets/models/user.py:406
msgid "Admin user"
msgstr "管理ユーザー"
@@ -889,7 +889,7 @@ msgstr "デフォルトクラスター"
msgid "User group"
msgstr "ユーザーグループ"
#: assets/models/cmd_filter.py:64 assets/serializers/system_user.py:59
#: assets/models/cmd_filter.py:64 assets/serializers/system_user.py:56
msgid "Command filter"
msgstr "コマンドフィルター"
@@ -1015,7 +1015,7 @@ msgstr "フルバリュー"
msgid "Parent key"
msgstr "親キー"
#: assets/models/node.py:566 assets/serializers/system_user.py:267
#: assets/models/node.py:566 assets/serializers/system_user.py:263
#: xpack/plugins/cloud/models.py:95 xpack/plugins/cloud/serializers/task.py:75
msgid "Node"
msgstr "ノード"
@@ -1095,7 +1095,7 @@ msgstr "ユーザースイッチ"
msgid "Switch from"
msgstr "から切り替え"
#: assets/models/user.py:345
#: assets/models/user.py:356
msgid "Can match system user"
msgstr "システムユーザーに一致できます"
@@ -1205,7 +1205,7 @@ msgid "Pattern"
msgstr "パターン"
#: assets/serializers/domain.py:14 assets/serializers/label.py:12
#: assets/serializers/system_user.py:63
#: assets/serializers/system_user.py:60
#: perms/serializers/asset/permission.py:49
msgid "Assets amount"
msgstr "資産額"
@@ -1234,74 +1234,69 @@ msgstr "同じレベルのノード名を同じにすることはできません
msgid "SSH key fingerprint"
msgstr "SSHキー指紋"
#: assets/serializers/system_user.py:40
#: perms/serializers/application/permission.py:46
msgid "Apps amount"
msgstr "アプリの量"
#: assets/serializers/system_user.py:62
#: assets/serializers/system_user.py:59
#: perms/serializers/asset/permission.py:50
msgid "Nodes amount"
msgstr "ノード量"
#: assets/serializers/system_user.py:64 assets/serializers/system_user.py:269
#: assets/serializers/system_user.py:61 assets/serializers/system_user.py:265
msgid "Login mode display"
msgstr "ログインモード表示"
#: assets/serializers/system_user.py:66
#: assets/serializers/system_user.py:63
msgid "Ad domain"
msgstr "広告ドメイン"
#: assets/serializers/system_user.py:67
#: assets/serializers/system_user.py:64
msgid "Is asset protocol"
msgstr "資産プロトコルです"
#: assets/serializers/system_user.py:68
#: assets/serializers/system_user.py:65
msgid "Only ssh and automatic login system users are supported"
msgstr "sshと自動ログインシステムのユーザーのみがサポートされています"
#: assets/serializers/system_user.py:108
#: assets/serializers/system_user.py:105
msgid "Username same with user with protocol {} only allow 1"
msgstr "プロトコル {} のユーザーと同じユーザー名は1のみ許可します"
#: assets/serializers/system_user.py:121 common/validators.py:14
#: assets/serializers/system_user.py:118 common/validators.py:14
msgid "Special char not allowed"
msgstr "特別なcharは許可されていません"
#: assets/serializers/system_user.py:131
#: assets/serializers/system_user.py:128
msgid "* Automatic login mode must fill in the username."
msgstr "* 自動ログインモードはユーザー名を入力する必要があります。"
#: assets/serializers/system_user.py:146
#: assets/serializers/system_user.py:143
msgid "Path should starts with /"
msgstr "パスは/で始まる必要があります"
#: assets/serializers/system_user.py:158
#: assets/serializers/system_user.py:155
msgid "Password or private key required"
msgstr "パスワードまたは秘密鍵が必要"
#: assets/serializers/system_user.py:172
#: assets/serializers/system_user.py:169
msgid "Only ssh protocol system users are allowed"
msgstr "Sshプロトコルシステムユーザーのみが許可されています"
#: assets/serializers/system_user.py:176
#: assets/serializers/system_user.py:173
msgid "The protocol must be consistent with the current user: {}"
msgstr "プロトコルは現在のユーザーと一致している必要があります: {}"
#: assets/serializers/system_user.py:180
#: assets/serializers/system_user.py:177
msgid "Only system users with automatic login are allowed"
msgstr "自動ログインを持つシステムユーザーのみが許可されます"
#: assets/serializers/system_user.py:288
#: assets/serializers/system_user.py:284
msgid "System user name"
msgstr "システムユーザー名"
#: assets/serializers/system_user.py:289 orgs/mixins/serializers.py:26
#: assets/serializers/system_user.py:285 orgs/mixins/serializers.py:26
#: rbac/serializers/rolebinding.py:23
msgid "Org name"
msgstr "組織名"
#: assets/serializers/system_user.py:298
#: assets/serializers/system_user.py:294
msgid "Asset hostname"
msgstr "資産ホスト名"
@@ -1429,6 +1424,10 @@ msgid "For security, do not push user {}"
msgstr "セキュリティのために、ユーザー {} をプッシュしないでください"
#: assets/tasks/utils.py:55
msgid "Asset protocol not support system user protocol, skipped: {}"
msgstr "アセット プロトコルはシステム ユーザー プロトコルをサポートしていません。次をスキップします: {}"
#: assets/tasks/utils.py:59
msgid "No assets matched, stop task"
msgstr "一致する資産がない、タスクを停止"
@@ -1664,7 +1663,8 @@ msgstr "企業微信"
#: audits/signal_handlers.py:62 authentication/views/feishu.py:144
#: authentication/views/login.py:85 notifications/backends/__init__.py:14
#: settings/serializers/auth/feishu.py:10 users/models/user.py:736
#: settings/serializers/auth/feishu.py:10
#: settings/serializers/auth/feishu.py:13 users/models/user.py:736
msgid "FeiShu"
msgstr "本を飛ばす"
@@ -2263,7 +2263,7 @@ msgstr "コードエラー"
#: authentication/templates/authentication/_msg_reset_password_code.html:9
#: authentication/templates/authentication/_msg_rest_password_success.html:2
#: authentication/templates/authentication/_msg_rest_public_key_success.html:2
#: jumpserver/conf.py:416 ops/tasks.py:145 ops/tasks.py:148
#: jumpserver/conf.py:417 ops/tasks.py:145 ops/tasks.py:148
#: perms/templates/perms/_msg_item_permissions_expire.html:3
#: perms/templates/perms/_msg_permed_items_expire.html:3
#: tickets/templates/tickets/approve_check_password.html:33
@@ -2747,11 +2747,11 @@ msgstr "特殊文字を含むべきではない"
msgid "The mobile phone number format is incorrect"
msgstr "携帯電話番号の形式が正しくありません"
#: jumpserver/conf.py:415
#: jumpserver/conf.py:416
msgid "Create account successfully"
msgstr "アカウントを正常に作成"
#: jumpserver/conf.py:417
#: jumpserver/conf.py:418
msgid "Your account has been created successfully"
msgstr "アカウントが正常に作成されました"
@@ -3198,6 +3198,10 @@ msgstr "ユーザーグループの量"
msgid "System users amount"
msgstr "システムユーザー数"
#: perms/serializers/application/permission.py:46
msgid "Apps amount"
msgstr "アプリの量"
#: perms/serializers/application/permission.py:79
msgid ""
"The application list contains applications that are different from the "
@@ -3636,7 +3640,7 @@ msgstr "そうでない場合はユーザーを作成"
msgid "Enable DingTalk Auth"
msgstr "ピン認証の有効化"
#: settings/serializers/auth/feishu.py:14
#: settings/serializers/auth/feishu.py:16
msgid "Enable FeiShu Auth"
msgstr "飛本認証の有効化"
@@ -5046,23 +5050,23 @@ msgstr "リプレイ"
msgid "Date end"
msgstr "終了日"
#: terminal/models/session.py:261
#: terminal/models/session.py:265
msgid "Session record"
msgstr "セッション記録"
#: terminal/models/session.py:263
#: terminal/models/session.py:267
msgid "Can monitor session"
msgstr "セッションを監視できます"
#: terminal/models/session.py:264
#: terminal/models/session.py:268
msgid "Can share session"
msgstr "セッションを共有できます"
#: terminal/models/session.py:265
#: terminal/models/session.py:269
msgid "Can terminate session"
msgstr "セッションを終了できます"
#: terminal/models/session.py:266
#: terminal/models/session.py:270
msgid "Can validate session action perm"
msgstr "セッションアクションのパーマを検証できます"

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b143c62843946c3e18b623d05065f12e9d3c578efe5cd0d2016056d2b8448ae8
size 109495
oid sha256:62e47d2577f103b524a1ae75e3357cdeb5f90d89f89c58a8c8fa8f63d67749f8
size 110097

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-03-02 12:21+0800\n"
"POT-Creation-Date: 2023-05-08 18:10+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n"
@@ -130,7 +130,7 @@ msgstr "系统用户"
#: assets/models/asset.py:386 assets/models/authbook.py:19
#: assets/models/backup.py:31 assets/models/cmd_filter.py:42
#: assets/models/gathered_user.py:14 assets/serializers/label.py:30
#: assets/serializers/system_user.py:268 audits/models.py:40
#: assets/serializers/system_user.py:264 audits/models.py:40
#: authentication/models.py:66 authentication/models.py:90
#: perms/models/asset_permission.py:23 terminal/backends/command/models.py:21
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:47
@@ -264,7 +264,7 @@ msgid "Application"
msgstr "应用程序"
#: applications/models/account.py:15 assets/models/authbook.py:20
#: assets/models/cmd_filter.py:46 assets/models/user.py:343 audits/models.py:41
#: assets/models/cmd_filter.py:46 assets/models/user.py:354 audits/models.py:41
#: authentication/models.py:83 perms/models/application_permission.py:33
#: perms/models/asset_permission.py:25 terminal/backends/command/models.py:22
#: terminal/backends/command/serializers.py:36 terminal/models/session.py:49
@@ -275,7 +275,7 @@ msgid "System user"
msgstr "系统用户"
#: applications/models/account.py:17 assets/models/authbook.py:21
#: settings/serializers/auth/cas.py:20
#: settings/serializers/auth/cas.py:20 settings/serializers/auth/feishu.py:20
msgid "Version"
msgstr "版本"
@@ -440,7 +440,7 @@ msgid "Application path"
msgstr "应用路径"
#: applications/serializers/attrs/application_category/remote_app.py:44
#: assets/serializers/system_user.py:167
#: assets/serializers/system_user.py:164
#: tickets/serializers/ticket/apply_application.py:38
#: tickets/serializers/ticket/common.py:59
#: xpack/plugins/change_auth_plan/serializers/asset.py:67
@@ -627,7 +627,7 @@ msgid "Is active"
msgstr "激活"
#: assets/models/asset.py:222 assets/models/cluster.py:19
#: assets/models/user.py:240 assets/models/user.py:395
#: assets/models/user.py:240 assets/models/user.py:406
msgid "Admin user"
msgstr "特权用户"
@@ -882,7 +882,7 @@ msgstr "默认Cluster"
msgid "User group"
msgstr "用户组"
#: assets/models/cmd_filter.py:64 assets/serializers/system_user.py:59
#: assets/models/cmd_filter.py:64 assets/serializers/system_user.py:56
msgid "Command filter"
msgstr "命令过滤器"
@@ -1008,7 +1008,7 @@ msgstr "全称"
msgid "Parent key"
msgstr "ssh私钥"
#: assets/models/node.py:566 assets/serializers/system_user.py:267
#: assets/models/node.py:566 assets/serializers/system_user.py:263
#: xpack/plugins/cloud/models.py:95 xpack/plugins/cloud/serializers/task.py:75
msgid "Node"
msgstr "节点"
@@ -1088,7 +1088,7 @@ msgstr "用户切换"
msgid "Switch from"
msgstr "切换自"
#: assets/models/user.py:345
#: assets/models/user.py:356
msgid "Can match system user"
msgstr "可以匹配系统用户"
@@ -1195,7 +1195,7 @@ msgid "Pattern"
msgstr "模式"
#: assets/serializers/domain.py:14 assets/serializers/label.py:12
#: assets/serializers/system_user.py:63
#: assets/serializers/system_user.py:60
#: perms/serializers/asset/permission.py:49
msgid "Assets amount"
msgstr "资产数量"
@@ -1224,74 +1224,69 @@ msgstr "同级别节点名字不能重复"
msgid "SSH key fingerprint"
msgstr "密钥指纹"
#: assets/serializers/system_user.py:40
#: perms/serializers/application/permission.py:46
msgid "Apps amount"
msgstr "应用数量"
#: assets/serializers/system_user.py:62
#: assets/serializers/system_user.py:59
#: perms/serializers/asset/permission.py:50
msgid "Nodes amount"
msgstr "节点数量"
#: assets/serializers/system_user.py:64 assets/serializers/system_user.py:269
#: assets/serializers/system_user.py:61 assets/serializers/system_user.py:265
msgid "Login mode display"
msgstr "认证方式名称"
#: assets/serializers/system_user.py:66
#: assets/serializers/system_user.py:63
msgid "Ad domain"
msgstr "Ad 网域"
#: assets/serializers/system_user.py:67
#: assets/serializers/system_user.py:64
msgid "Is asset protocol"
msgstr "资产协议"
#: assets/serializers/system_user.py:68
#: assets/serializers/system_user.py:65
msgid "Only ssh and automatic login system users are supported"
msgstr "仅支持ssh协议和自动登录的系统用户"
#: assets/serializers/system_user.py:108
#: assets/serializers/system_user.py:105
msgid "Username same with user with protocol {} only allow 1"
msgstr "用户名和用户相同的一种协议只允许存在一个"
#: assets/serializers/system_user.py:121 common/validators.py:14
#: assets/serializers/system_user.py:118 common/validators.py:14
msgid "Special char not allowed"
msgstr "不能包含特殊字符"
#: assets/serializers/system_user.py:131
#: assets/serializers/system_user.py:128
msgid "* Automatic login mode must fill in the username."
msgstr "自动登录模式,必须填写用户名"
#: assets/serializers/system_user.py:146
#: assets/serializers/system_user.py:143
msgid "Path should starts with /"
msgstr "路径应该以 / 开头"
#: assets/serializers/system_user.py:158
#: assets/serializers/system_user.py:155
msgid "Password or private key required"
msgstr "密码或密钥密码需要一个"
#: assets/serializers/system_user.py:172
#: assets/serializers/system_user.py:169
msgid "Only ssh protocol system users are allowed"
msgstr "仅允许ssh协议的系统用户"
#: assets/serializers/system_user.py:176
#: assets/serializers/system_user.py:173
msgid "The protocol must be consistent with the current user: {}"
msgstr "协议必须和当前用户保持一致: {}"
#: assets/serializers/system_user.py:180
#: assets/serializers/system_user.py:177
msgid "Only system users with automatic login are allowed"
msgstr "仅允许自动登录的系统用户"
#: assets/serializers/system_user.py:288
#: assets/serializers/system_user.py:284
msgid "System user name"
msgstr "系统用户名称"
#: assets/serializers/system_user.py:289 orgs/mixins/serializers.py:26
#: assets/serializers/system_user.py:285 orgs/mixins/serializers.py:26
#: rbac/serializers/rolebinding.py:23
msgid "Org name"
msgstr "组织名称"
#: assets/serializers/system_user.py:298
#: assets/serializers/system_user.py:294
msgid "Asset hostname"
msgstr "资产主机名"
@@ -1415,6 +1410,10 @@ msgid "For security, do not push user {}"
msgstr "为了安全,禁止推送用户 {}"
#: assets/tasks/utils.py:55
msgid "Asset protocol not support system user protocol, skipped: {}"
msgstr "资产协议不支持系统用户协议,跳过: {}"
#: assets/tasks/utils.py:59
msgid "No assets matched, stop task"
msgstr "没有匹配到资产,结束任务"
@@ -1650,7 +1649,8 @@ msgstr "企业微信"
#: audits/signal_handlers.py:62 authentication/views/feishu.py:144
#: authentication/views/login.py:85 notifications/backends/__init__.py:14
#: settings/serializers/auth/feishu.py:10 users/models/user.py:736
#: settings/serializers/auth/feishu.py:10
#: settings/serializers/auth/feishu.py:13 users/models/user.py:736
msgid "FeiShu"
msgstr "飞书"
@@ -2233,7 +2233,7 @@ msgstr "代码错误"
#: authentication/templates/authentication/_msg_reset_password_code.html:9
#: authentication/templates/authentication/_msg_rest_password_success.html:2
#: authentication/templates/authentication/_msg_rest_public_key_success.html:2
#: jumpserver/conf.py:416 ops/tasks.py:145 ops/tasks.py:148
#: jumpserver/conf.py:417 ops/tasks.py:145 ops/tasks.py:148
#: perms/templates/perms/_msg_item_permissions_expire.html:3
#: perms/templates/perms/_msg_permed_items_expire.html:3
#: tickets/templates/tickets/approve_check_password.html:33
@@ -2708,11 +2708,11 @@ msgstr "不能包含特殊字符"
msgid "The mobile phone number format is incorrect"
msgstr "手机号格式不正确"
#: jumpserver/conf.py:415
#: jumpserver/conf.py:416
msgid "Create account successfully"
msgstr "创建账号成功"
#: jumpserver/conf.py:417
#: jumpserver/conf.py:418
msgid "Your account has been created successfully"
msgstr "你的账号已创建成功"
@@ -3153,6 +3153,10 @@ msgstr "用户组数量"
msgid "System users amount"
msgstr "系统用户数量"
#: perms/serializers/application/permission.py:46
msgid "Apps amount"
msgstr "应用数量"
#: perms/serializers/application/permission.py:79
msgid ""
"The application list contains applications that are different from the "
@@ -3588,7 +3592,7 @@ msgstr "创建用户(如果不存在)"
msgid "Enable DingTalk Auth"
msgstr "启用钉钉认证"
#: settings/serializers/auth/feishu.py:14
#: settings/serializers/auth/feishu.py:16
msgid "Enable FeiShu Auth"
msgstr "启用飞书认证"
@@ -4960,23 +4964,23 @@ msgstr "回放"
msgid "Date end"
msgstr "结束日期"
#: terminal/models/session.py:261
#: terminal/models/session.py:265
msgid "Session record"
msgstr "会话记录"
#: terminal/models/session.py:263
#: terminal/models/session.py:267
msgid "Can monitor session"
msgstr "可以监控会话"
#: terminal/models/session.py:264
#: terminal/models/session.py:268
msgid "Can share session"
msgstr "可以分享会话"
#: terminal/models/session.py:265
#: terminal/models/session.py:269
msgid "Can terminate session"
msgstr "可以终断会话"
#: terminal/models/session.py:266
#: terminal/models/session.py:270
msgid "Can validate session action perm"
msgstr "可以验证会话动作权限"

View File

@@ -24,7 +24,7 @@ __all__ = [
class TaskViewSet(OrgBulkModelViewSet):
model = Task
filterset_fields = ("name",)
filterset_fields = ("name", "adhoc__execution__celery_task_id")
search_fields = filterset_fields
serializer_class = TaskSerializer
@@ -54,6 +54,7 @@ class TaskRun(generics.RetrieveAPIView):
class AdHocViewSet(viewsets.ModelViewSet):
queryset = AdHoc.objects.all()
filterset_fields = ('execution__celery_task_id', )
serializer_class = AdHocSerializer
def get_serializer_class(self):

View File

@@ -82,7 +82,7 @@ class CeleryResultApi(generics.RetrieveAPIView):
def get_object(self):
pk = self.kwargs.get('pk')
return AsyncResult(pk)
return AsyncResult(str(pk))
class CeleryPeriodTaskViewSet(CommonApiMixin, viewsets.ModelViewSet):

View File

View File

@@ -0,0 +1,80 @@
import logging
from celery.utils.log import get_logger
from django.db import close_old_connections
from django.core.exceptions import ObjectDoesNotExist
from django.db.utils import DatabaseError, InterfaceError
from django_celery_beat.schedulers import DatabaseScheduler as DJDatabaseScheduler
logger = get_logger(__name__)
debug, info, warning = logger.debug, logger.info, logger.warning
__all__ = ['DatabaseScheduler']
class DatabaseScheduler(DJDatabaseScheduler):
def sync(self):
if logger.isEnabledFor(logging.DEBUG):
debug('Writing entries...')
_tried = set()
_failed = set()
try:
close_old_connections()
while self._dirty:
name = self._dirty.pop()
try:
# 源码
# self.schedule[name].save()
# _tried.add(name)
"""
::Debug Description (2023.07.10)::
如果调用 self.schedule 可能会导致 self.save() 方法之前重新获取数据库中的数据, 而不是临时设置的 last_run_at 数据
如果这里调用 self.schedule
那么可能会导致调用 save 的 self.schedule[name] 的 last_run_at 是从数据库中获取回来的老数据
而不是任务执行后临时设置的 last_run_at (在 __next__() 方法中设置的)
当 `max_interval` 间隔之后, 下一个任务检测周期还是会再次执行任务
::Demo::
任务信息:
beat config: max_interval = 60s
任务名称: cap
任务执行周期: 每 3 分钟执行一次
任务最后执行时间: 18:00
任务第一次执行: 18:03 (执行时设置 last_run_at = 18:03, 此时在内存中)
任务执行完成后,
检测到需要 sync, sync 中调用了 self.schedule,
self.schedule 中发现 schedule_changed() 为 True, 需要调用 all_as_schedule()
此时sync 中调用的 self.schedule[name] 的 last_run_at 是 18:00
这时候在 self.sync() 进行 self.save()
beat: Waking up 60s ...
任务第二次执行: 18:04 (因为获取回来的 last_run_at 是 18:00, entry.is_due() == True)
::解决方法::
所以这里为了避免从数据库中获取,直接使用 _schedule #
"""
self._schedule[name].save()
_tried.add(name)
except (KeyError, TypeError, ObjectDoesNotExist):
_failed.add(name)
except DatabaseError as exc:
logger.exception('Database error while sync: %r', exc)
except InterfaceError:
warning(
'DatabaseScheduler: InterfaceError in sync(), '
'waiting to retry in next call...'
)
finally:
# retry later, only for the failed ones
self._dirty |= _failed

View File

@@ -2,9 +2,9 @@
#
from django.conf import settings
from .ansible.inventory import BaseInventory
from common.utils import get_logger
from .ansible.inventory import BaseInventory
__all__ = [
'JMSInventory', 'JMSCustomInventory',
@@ -110,7 +110,10 @@ class JMSInventory(JMSBaseInventory):
if self.system_user:
self.system_user.load_asset_special_auth(asset=asset, username=self.run_as)
return self.system_user._to_secret_json()
info = self.system_user._to_secret_json()
if self.run_as:
info['username'] = self.run_as
return info
else:
return {}

View File

@@ -17,7 +17,7 @@ class AdHocExecutionSerializer(serializers.ModelSerializer):
fields_mini = ['id']
fields_small = fields_mini + [
'hosts_amount', 'timedelta', 'result', 'summary', 'short_id',
'is_finished', 'is_success',
'is_finished', 'is_success', 'celery_task_id',
'date_start', 'date_finished',
]
fields_fk = ['task', 'task_display', 'adhoc', 'adhoc_short_id',]

View File

@@ -149,7 +149,7 @@ class BuiltinRole:
'User': cls.system_user.get_role(),
'Auditor': cls.system_auditor.get_role()
}
return cls.system_role_mapper[name]
return cls.system_role_mapper.get(name, cls.system_role_mapper['User'])
@classmethod
def get_org_role_by_old_name(cls, name):
@@ -159,7 +159,7 @@ class BuiltinRole:
'User': cls.org_user.get_role(),
'Auditor': cls.org_auditor.get_role(),
}
return cls.org_role_mapper[name]
return cls.org_role_mapper.get(name, cls.org_role_mapper['User'])
@classmethod
def sync_to_db(cls, show_msg=False):

View File

@@ -9,7 +9,13 @@ __all__ = ['FeiShuSettingSerializer']
class FeiShuSettingSerializer(serializers.Serializer):
PREFIX_TITLE = '%s-%s' % (_('Authentication'), _('FeiShu'))
VERSION_CHOICES = (
('feishu', _('FeiShu')),
('lark', 'Lark')
)
AUTH_FEISHU = serializers.BooleanField(default=False, label=_('Enable FeiShu Auth'))
FEISHU_APP_ID = serializers.CharField(max_length=256, required=True, label='App ID')
FEISHU_APP_SECRET = EncryptedField(max_length=256, required=False, label='App Secret')
AUTH_FEISHU = serializers.BooleanField(default=False, label=_('Enable FeiShu Auth'))
FEISHU_VERSION = serializers.ChoiceField(
choices=VERSION_CHOICES, default='feishu', label=_('Version')
)

View File

@@ -79,7 +79,7 @@ class Endpoint(JMSModel):
return None
endpoints = cls.objects.filter(name__in=values).order_by('-date_updated')
for endpoint in endpoints:
if endpoint.is_valid_for(protocol):
if endpoint.is_valid_for(instance, protocol):
return endpoint

View File

@@ -1,14 +1,13 @@
from html import escape
from django.utils.translation import ugettext as _
import html2text
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
from common.utils import get_logger
from tickets.const import TicketState, TicketType
from tickets.utils import (
send_ticket_processed_mail_to_applicant,
send_ticket_applied_mail_to_assignees
)
from tickets.const import TicketState, TicketType
logger = get_logger(__name__)
@@ -98,7 +97,7 @@ class BaseHandler:
approve_info = _('{} {} the ticket').format(user_display, state_display)
context = self._diff_prev_approve_context(state)
context.update({'approve_info': approve_info})
body = self.reject_html_script(
body = self.safe_html_script(
render_to_string('tickets/ticket_approve_diff.html', context)
)
data = {
@@ -111,6 +110,7 @@ class BaseHandler:
return self.ticket.comments.create(**data)
@staticmethod
def reject_html_script(unsafe_html):
safe_html = escape(unsafe_html)
return safe_html
def safe_html_script(unsafe_html):
unsafe_html = unsafe_html.replace('\n', '')
html_str = html2text.html2text(unsafe_html)
return html_str

View File

@@ -1,5 +1,5 @@
from django.db.transaction import atomic
from django.db.models import Model
from django.db.transaction import atomic
from django.utils.translation import ugettext as _
from rest_framework import serializers
@@ -69,8 +69,6 @@ class BaseApplyAssetApplicationSerializer(serializers.Serializer):
error = _('The expiration date should be greater than the start date')
raise serializers.ValidationError({'apply_date_expired': error})
attrs['apply_date_start'] = apply_date_start
attrs['apply_date_expired'] = apply_date_expired
return attrs
@atomic

View File

@@ -18,7 +18,6 @@
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
{% endif %}

View File

@@ -1,30 +1,27 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
import uuid
import base64
import string
import random
import datetime
import uuid
from typing import Callable
from django.db import models
from django.conf import settings
from django.utils import timezone
from django.core.cache import cache
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.hashers import check_password
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import AbstractUser
from django.core.cache import cache
from django.db import models
from django.shortcuts import reverse
from django.utils import timezone
from django.utils.module_loading import import_string
from django.utils.translation import ugettext_lazy as _
from orgs.utils import current_org
from orgs.models import Organization
from rbac.const import Scope
from common.db import fields, models as jms_models
from common.utils import (
date_expired_default, get_logger, lazyproperty, random_string, bulk_create_with_signal
)
from orgs.utils import current_org
from rbac.const import Scope
from ..signals import post_user_change_password, post_user_leave_org, pre_user_leave_org
__all__ = ['User', 'UserPasswordHistory']
@@ -518,8 +515,7 @@ class TokenMixin:
return self.access_keys.first()
def generate_reset_token(self):
letter = string.ascii_letters + string.digits
token = ''.join([random.choice(letter) for _ in range(50)])
token = random_string(50)
self.set_cache(token)
return token

View File

@@ -4,6 +4,7 @@
from celery import shared_task
from django.conf import settings
from django.utils import timezone
from django.db import transaction
from users.notifications import PasswordExpirationReminderMsg
from ops.celery.utils import (

View File

@@ -128,9 +128,9 @@ kubernetes==21.7.0
# DB requirements
mysqlclient==2.1.0
PyMySQL==1.0.2
oracledb==1.0.1
oracledb==1.2.2
psycopg2-binary==2.9.1
pymssql==2.1.5
# pymssql==2.2.7
django-mysql==3.9.0
django-redis==5.2.0
python-redis-lock==3.7.0

View File

@@ -54,7 +54,7 @@ else:
connection_params['port'] = settings.REDIS_PORT
redis_client = Redis(**connection_params)
scheduler = "django_celery_beat.schedulers:DatabaseScheduler"
scheduler = "ops.celery.beat.schedulers:DatabaseScheduler"
processes = []
cmd = [
'celery',