mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-12-24 21:12:35 +00:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c28cca7ca2 | ||
|
|
29ab6bbee7 | ||
|
|
3352f78e01 | ||
|
|
39affc4989 | ||
|
|
42337f0d00 | ||
|
|
31d2bdd3bb | ||
|
|
ee9687743e | ||
|
|
d5a9942159 | ||
|
|
3b92e9e516 | ||
|
|
0132eafeb6 | ||
|
|
fb286f4665 | ||
|
|
daeba109fd | ||
|
|
06411b080e | ||
|
|
5da6a1b9b6 | ||
|
|
988f33634e | ||
|
|
a645dc09ae | ||
|
|
4e4e58480f | ||
|
|
4a1f3ed727 | ||
|
|
a10bb29a1e | ||
|
|
73aeb021cc | ||
|
|
3d66fe4758 | ||
|
|
ec2071a6ca | ||
|
|
beb43aa726 | ||
|
|
8a77a7b8b5 | ||
|
|
7eed182627 | ||
|
|
ec847d3ecb | ||
|
|
a0994e2e12 | ||
|
|
17e3ddda05 | ||
|
|
6e2e92be5e | ||
|
|
e90d8c8561 | ||
|
|
cf972942fa | ||
|
|
72e35d5553 | ||
|
|
0ba84e7e18 | ||
|
|
fbc5ae1b9b | ||
|
|
2fcf045826 | ||
|
|
32cba4f2a1 | ||
|
|
b76aa3b259 |
2
.github/workflows/release-drafter.yml
vendored
2
.github/workflows/release-drafter.yml
vendored
@@ -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
|
||||
|
||||
@@ -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
2
GITSHA
@@ -1 +1 @@
|
||||
3f9a17347d8d6ac785054088f7113ab9a9c506cb
|
||||
29ab6bbee7f1ac2dee1826e42b65039f260f6fd0
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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]:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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'))
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7db985efdf818137dafe489f339955f4d71a245ffd6becc8f6efac539a625682
|
||||
size 133463
|
||||
oid sha256:0cc37a87259f2cd3794ad628ee40497bd97beaee7219be3d9773e1e544c3e352
|
||||
size 134193
|
||||
|
||||
@@ -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 "セッションアクションのパーマを検証できます"
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b143c62843946c3e18b623d05065f12e9d3c578efe5cd0d2016056d2b8448ae8
|
||||
size 109495
|
||||
oid sha256:62e47d2577f103b524a1ae75e3357cdeb5f90d89f89c58a8c8fa8f63d67749f8
|
||||
size 110097
|
||||
|
||||
@@ -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 "可以验证会话动作权限"
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
0
apps/ops/celery/beat/__init__.py
Normal file
0
apps/ops/celery/beat/__init__.py
Normal file
80
apps/ops/celery/beat/schedulers.py
Normal file
80
apps/ops/celery/beat/schedulers.py
Normal 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
|
||||
@@ -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 {}
|
||||
|
||||
|
||||
@@ -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',]
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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')
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user