mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-12-15 08:32:48 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1873702b25 | ||
|
|
dad45e7ace | ||
|
|
720f9cd397 | ||
|
|
81dee0c403 | ||
|
|
105ef791b8 | ||
|
|
a19c0bde60 | ||
|
|
3996daf4a7 | ||
|
|
ac235f788e | ||
|
|
67e334bf43 | ||
|
|
f7f9fb1bdf | ||
|
|
8979228e0b | ||
|
|
024beca690 | ||
|
|
5c0359e394 | ||
|
|
4ce4bde368 | ||
|
|
809bad271a | ||
|
|
d3bfc03849 | ||
|
|
04c0121b37 | ||
|
|
b97b50ab31 | ||
|
|
d8a8c8153b | ||
|
|
a68ad7be68 | ||
|
|
4041f1aeec | ||
|
|
59388655ea | ||
|
|
ef7463c588 | ||
|
|
7e7d6d94e6 |
4
Dockerfile-ee
Normal file
4
Dockerfile-ee
Normal file
@@ -0,0 +1,4 @@
|
||||
ARG VERSION
|
||||
FROM registry.fit2cloud.com/jumpserver/xpack:${VERSION} as build-xpack
|
||||
FROM jumpserver/core:${VERSION}
|
||||
COPY --from=build-xpack /opt/xpack /opt/jumpserver/apps/xpack
|
||||
@@ -38,8 +38,8 @@ ARG TOOLS=" \
|
||||
default-mysql-client \
|
||||
iputils-ping \
|
||||
locales \
|
||||
netcat \
|
||||
redis-server \
|
||||
procps \
|
||||
redis-tools \
|
||||
telnet \
|
||||
vim \
|
||||
unzip \
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import io
|
||||
import os
|
||||
import uuid
|
||||
from hashlib import md5
|
||||
|
||||
import sshpubkeys
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.db import models
|
||||
from django.db.models import QuerySet
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
from django.db.models import QuerySet
|
||||
|
||||
from common.utils import random_string, signer
|
||||
from common.db import fields
|
||||
from common.utils import random_string
|
||||
from common.utils import (
|
||||
ssh_key_string_to_obj, ssh_key_gen, get_logger, lazyproperty
|
||||
)
|
||||
from common.utils.encode import ssh_pubkey_gen
|
||||
from common.validators import alphanumeric
|
||||
from common.db import fields
|
||||
from common.utils.encode import (
|
||||
parse_ssh_public_key_str, parse_ssh_private_key_str
|
||||
)
|
||||
from orgs.mixins.models import OrgModelMixin
|
||||
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
@@ -64,16 +63,16 @@ class AuthMixin:
|
||||
|
||||
@property
|
||||
def ssh_key_fingerprint(self):
|
||||
public_key = None
|
||||
if self.public_key:
|
||||
public_key = self.public_key
|
||||
elif self.private_key:
|
||||
try:
|
||||
public_key = ssh_pubkey_gen(private_key=self.private_key, password=self.password)
|
||||
public_key = parse_ssh_public_key_str(self.private_key, password=self.password)
|
||||
except IOError as e:
|
||||
return str(e)
|
||||
else:
|
||||
if not public_key:
|
||||
return ''
|
||||
|
||||
public_key_obj = sshpubkeys.SSHKey(public_key)
|
||||
fingerprint = public_key_obj.hash_md5()
|
||||
return fingerprint
|
||||
@@ -88,24 +87,27 @@ class AuthMixin:
|
||||
|
||||
@property
|
||||
def private_key_file(self):
|
||||
if not self.private_key_obj:
|
||||
if not self.private_key:
|
||||
return None
|
||||
private_key_str = parse_ssh_private_key_str(self.private_key,
|
||||
password=self.password)
|
||||
if not private_key_str:
|
||||
return None
|
||||
project_dir = settings.PROJECT_DIR
|
||||
tmp_dir = os.path.join(project_dir, 'tmp')
|
||||
key_name = '.' + md5(self.private_key.encode('utf-8')).hexdigest()
|
||||
key_path = os.path.join(tmp_dir, key_name)
|
||||
if not os.path.exists(key_path):
|
||||
self.private_key_obj.write_private_key_file(key_path)
|
||||
with open(key_path, 'w') as f:
|
||||
f.write(private_key_str)
|
||||
os.chmod(key_path, 0o400)
|
||||
return key_path
|
||||
|
||||
def get_private_key(self):
|
||||
if not self.private_key_obj:
|
||||
if not self.private_key:
|
||||
return None
|
||||
string_io = io.StringIO()
|
||||
self.private_key_obj.write_private_key(string_io)
|
||||
private_key = string_io.getvalue()
|
||||
return private_key
|
||||
return parse_ssh_private_key_str(self.private_key,
|
||||
password=self.password)
|
||||
|
||||
@property
|
||||
def public_key_obj(self):
|
||||
@@ -234,4 +236,3 @@ class BaseUser(OrgModelMixin, AuthMixin):
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from io import StringIO
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from common.utils import ssh_pubkey_gen, ssh_private_key_gen, validate_ssh_private_key
|
||||
from common.drf.fields import EncryptedField
|
||||
from assets.models import Type
|
||||
from common.drf.fields import EncryptedField
|
||||
from common.utils import validate_ssh_private_key, parse_ssh_private_key_str, parse_ssh_public_key_str
|
||||
from .utils import validate_password_for_ansible
|
||||
|
||||
|
||||
class AuthSerializer(serializers.ModelSerializer):
|
||||
password = EncryptedField(required=False, allow_blank=True, allow_null=True, max_length=1024, label=_('Password'))
|
||||
private_key = EncryptedField(required=False, allow_blank=True, allow_null=True, max_length=16384, label=_('Private key'))
|
||||
private_key = EncryptedField(required=False, allow_blank=True, allow_null=True, max_length=16384,
|
||||
label=_('Private key'))
|
||||
|
||||
def gen_keys(self, private_key=None, password=None):
|
||||
if private_key is None:
|
||||
return None, None
|
||||
public_key = ssh_pubkey_gen(private_key=private_key, password=password)
|
||||
public_key = parse_ssh_public_key_str(text=private_key, password=password)
|
||||
return private_key, public_key
|
||||
|
||||
def save(self, **kwargs):
|
||||
@@ -57,10 +57,7 @@ class AuthSerializerMixin(serializers.ModelSerializer):
|
||||
if not valid:
|
||||
raise serializers.ValidationError(_("private key invalid or passphrase error"))
|
||||
|
||||
private_key = ssh_private_key_gen(private_key, password=passphrase)
|
||||
string_io = StringIO()
|
||||
private_key.write_private_key(string_io)
|
||||
private_key = string_io.getvalue()
|
||||
private_key = parse_ssh_private_key_str(private_key, password=passphrase)
|
||||
return private_key
|
||||
|
||||
def validate_public_key(self, public_key):
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
from rest_framework import serializers
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.db.models import Count
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from common.mixins.serializers import BulkSerializerMixin
|
||||
from common.utils import ssh_pubkey_gen
|
||||
from common.drf.fields import EncryptedField
|
||||
from common.drf.serializers import SecretReadableMixin
|
||||
from common.mixins.serializers import BulkSerializerMixin
|
||||
from common.utils import parse_ssh_public_key_str
|
||||
from common.validators import alphanumeric_re, alphanumeric_cn_re, alphanumeric_win_re
|
||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
from ..models import SystemUser, Asset
|
||||
from .utils import validate_password_for_ansible
|
||||
from .base import AuthSerializerMixin
|
||||
from .utils import validate_password_for_ansible
|
||||
from ..models import SystemUser, Asset
|
||||
|
||||
__all__ = [
|
||||
'SystemUserSerializer', 'MiniSystemUserSerializer',
|
||||
@@ -214,7 +214,7 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
||||
elif attrs.get('private_key'):
|
||||
private_key = attrs['private_key']
|
||||
password = attrs.get('password')
|
||||
public_key = ssh_pubkey_gen(private_key, password=password, username=username)
|
||||
public_key = parse_ssh_public_key_str(private_key, password=password)
|
||||
attrs['public_key'] = public_key
|
||||
return attrs
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from common.utils import validate_ssh_private_key, parse_ssh_private_key_str
|
||||
|
||||
|
||||
def validate_password_for_ansible(password):
|
||||
""" 校验 Ansible 不支持的特殊字符 """
|
||||
@@ -15,3 +17,9 @@ def validate_password_for_ansible(password):
|
||||
if '"' in password:
|
||||
raise serializers.ValidationError(_('Password can not contains `"` '))
|
||||
|
||||
|
||||
def validate_ssh_key(ssh_key, passphrase=None):
|
||||
valid = validate_ssh_private_key(ssh_key, password=passphrase)
|
||||
if not valid:
|
||||
raise serializers.ValidationError(_("private key invalid or passphrase error"))
|
||||
return parse_ssh_private_key_str(ssh_key, passphrase)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#
|
||||
import requests
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.utils.http import urlencode
|
||||
from django.conf import settings
|
||||
@@ -90,8 +91,12 @@ class OAuth2Backend(JMSModelBackend):
|
||||
request, path=reverse(settings.AUTH_OAUTH2_AUTH_LOGIN_CALLBACK_URL_NAME)
|
||||
)
|
||||
}
|
||||
access_token_url = '{url}?{query}'.format(
|
||||
url=settings.AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT, query=urlencode(query_dict)
|
||||
if '?' in settings.AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT:
|
||||
separator = '&'
|
||||
else:
|
||||
separator = '?'
|
||||
access_token_url = '{url}{separator}{query}'.format(
|
||||
url=settings.AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT, separator=separator, query=urlencode(query_dict)
|
||||
)
|
||||
token_method = settings.AUTH_OAUTH2_ACCESS_TOKEN_METHOD.lower()
|
||||
requests_func = getattr(requests, token_method, requests.get)
|
||||
@@ -118,8 +123,12 @@ class OAuth2Backend(JMSModelBackend):
|
||||
}
|
||||
|
||||
logger.debug(log_prompt.format('Get userinfo endpoint'))
|
||||
userinfo_url = '{url}?{query}'.format(
|
||||
url=settings.AUTH_OAUTH2_PROVIDER_USERINFO_ENDPOINT,
|
||||
if '?' in settings.AUTH_OAUTH2_PROVIDER_USERINFO_ENDPOINT:
|
||||
separator = '&'
|
||||
else:
|
||||
separator = '?'
|
||||
userinfo_url = '{url}{separator}{query}'.format(
|
||||
url=settings.AUTH_OAUTH2_PROVIDER_USERINFO_ENDPOINT, separator=separator,
|
||||
query=urlencode(query_dict)
|
||||
)
|
||||
userinfo_response = requests.get(userinfo_url, headers=headers)
|
||||
|
||||
@@ -26,8 +26,13 @@ class OAuth2AuthRequestView(View):
|
||||
)
|
||||
}
|
||||
|
||||
redirect_url = '{url}?{query}'.format(
|
||||
if '?' in settings.AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT:
|
||||
separator = '&'
|
||||
else:
|
||||
separator = '?'
|
||||
redirect_url = '{url}{separator}{query}'.format(
|
||||
url=settings.AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT,
|
||||
separator=separator,
|
||||
query=urlencode(query_dict)
|
||||
)
|
||||
logger.debug(log_prompt.format('Redirect login url'))
|
||||
|
||||
@@ -88,7 +88,7 @@ class OIDCAuthCodeBackend(OIDCBaseBackend):
|
||||
"""
|
||||
|
||||
@ssl_verification
|
||||
def authenticate(self, request, nonce=None, **kwargs):
|
||||
def authenticate(self, request, nonce=None, code_verifier=None, **kwargs):
|
||||
""" Authenticates users in case of the OpenID Connect Authorization code flow. """
|
||||
log_prompt = "Process authenticate [OIDCAuthCodeBackend]: {}"
|
||||
logger.debug(log_prompt.format('start'))
|
||||
@@ -134,6 +134,8 @@ class OIDCAuthCodeBackend(OIDCBaseBackend):
|
||||
request, path=reverse(settings.AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME)
|
||||
)
|
||||
}
|
||||
if settings.AUTH_OPENID_PKCE and code_verifier:
|
||||
token_payload['code_verifier'] = code_verifier
|
||||
if settings.AUTH_OPENID_CLIENT_AUTH_METHOD == 'client_secret_post':
|
||||
token_payload.update({
|
||||
'client_id': settings.AUTH_OPENID_CLIENT_ID,
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
|
||||
"""
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import time
|
||||
import secrets
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import auth
|
||||
@@ -38,6 +41,19 @@ class OIDCAuthRequestView(View):
|
||||
|
||||
http_method_names = ['get', ]
|
||||
|
||||
@staticmethod
|
||||
def gen_code_verifier(length=128):
|
||||
# length range 43 ~ 128
|
||||
return secrets.token_urlsafe(length-32)
|
||||
|
||||
@staticmethod
|
||||
def gen_code_challenge(code_verifier, code_challenge_method):
|
||||
if code_challenge_method == 'plain':
|
||||
return code_verifier
|
||||
h = hashlib.sha256(code_verifier.encode('ascii')).digest()
|
||||
b = base64.urlsafe_b64encode(h)
|
||||
return b.decode('ascii')[:-1]
|
||||
|
||||
def get(self, request):
|
||||
""" Processes GET requests. """
|
||||
|
||||
@@ -56,6 +72,16 @@ class OIDCAuthRequestView(View):
|
||||
)
|
||||
})
|
||||
|
||||
if settings.AUTH_OPENID_PKCE:
|
||||
code_verifier = self.gen_code_verifier()
|
||||
code_challenge_method = settings.AUTH_OPENID_CODE_CHALLENGE_METHOD or 'S256'
|
||||
code_challenge = self.gen_code_challenge(code_verifier, code_challenge_method)
|
||||
authentication_request_params.update({
|
||||
'code_challenge_method': code_challenge_method,
|
||||
'code_challenge': code_challenge
|
||||
})
|
||||
request.session['oidc_auth_code_verifier'] = code_verifier
|
||||
|
||||
# States should be used! They are recommended in order to maintain state between the
|
||||
# authentication request and the callback.
|
||||
if settings.AUTH_OPENID_USE_STATE:
|
||||
@@ -138,8 +164,9 @@ class OIDCAuthCallbackView(View):
|
||||
|
||||
# Authenticates the end-user.
|
||||
next_url = request.session.get('oidc_auth_next_url', None)
|
||||
code_verifier = request.session.get('oidc_auth_code_verifier', None)
|
||||
logger.debug(log_prompt.format('Process authenticate'))
|
||||
user = auth.authenticate(nonce=nonce, request=request)
|
||||
user = auth.authenticate(nonce=nonce, request=request, code_verifier=code_verifier)
|
||||
if user and user.is_valid:
|
||||
logger.debug(log_prompt.format('Login: {}'.format(user)))
|
||||
auth.login(self.request, user)
|
||||
|
||||
@@ -9,6 +9,10 @@ class FlowerService(BaseService):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@property
|
||||
def db_file(self):
|
||||
return os.path.join(BASE_DIR, 'data', 'flower')
|
||||
|
||||
@property
|
||||
def cmd(self):
|
||||
print("\n- Start Flower as Task Monitor")
|
||||
@@ -20,11 +24,11 @@ class FlowerService(BaseService):
|
||||
'-A', 'ops',
|
||||
'flower',
|
||||
'-logging=info',
|
||||
'-db={}'.format(self.db_file),
|
||||
'--url_prefix=/core/flower',
|
||||
'--auto_refresh=False',
|
||||
'--max_tasks=1000',
|
||||
'--persistent=True',
|
||||
'-db=/opt/jumpserver/data/flower.db',
|
||||
'--state_save_interval=600000'
|
||||
]
|
||||
return cmd
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import re
|
||||
import json
|
||||
from six import string_types
|
||||
import base64
|
||||
import os
|
||||
import time
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from io import StringIO
|
||||
from itertools import chain
|
||||
|
||||
import paramiko
|
||||
import sshpubkeys
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from django.conf import settings
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from itsdangerous import (
|
||||
TimedJSONWebSignatureSerializer, JSONWebSignatureSerializer,
|
||||
BadSignature, SignatureExpired
|
||||
)
|
||||
from django.conf import settings
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.db.models.fields.files import FileField
|
||||
from six import string_types
|
||||
|
||||
from .http import http_date
|
||||
|
||||
@@ -69,22 +68,25 @@ class Signer(metaclass=Singleton):
|
||||
return None
|
||||
|
||||
|
||||
_supported_paramiko_ssh_key_types = (
|
||||
paramiko.RSAKey,
|
||||
paramiko.DSSKey,
|
||||
paramiko.Ed25519Key,
|
||||
paramiko.ECDSAKey,
|
||||
)
|
||||
|
||||
|
||||
def ssh_key_string_to_obj(text, password=None):
|
||||
key = None
|
||||
for ssh_key_type in _supported_paramiko_ssh_key_types:
|
||||
if not issubclass(ssh_key_type, paramiko.PKey):
|
||||
continue
|
||||
try:
|
||||
key = paramiko.RSAKey.from_private_key(StringIO(text), password=password)
|
||||
key = ssh_key_type.from_private_key(StringIO(text), password=password)
|
||||
except paramiko.SSHException:
|
||||
pass
|
||||
else:
|
||||
return key
|
||||
|
||||
try:
|
||||
key = paramiko.DSSKey.from_private_key(StringIO(text), password=password)
|
||||
except paramiko.SSHException:
|
||||
pass
|
||||
else:
|
||||
return key
|
||||
|
||||
return key
|
||||
|
||||
|
||||
@@ -98,7 +100,7 @@ def ssh_private_key_gen(private_key, password=None):
|
||||
|
||||
def ssh_pubkey_gen(private_key=None, username='jumpserver', hostname='localhost', password=None):
|
||||
private_key = ssh_private_key_gen(private_key, password=password)
|
||||
if not isinstance(private_key, (paramiko.RSAKey, paramiko.DSSKey)):
|
||||
if not isinstance(private_key, _supported_paramiko_ssh_key_types):
|
||||
raise IOError('Invalid private key')
|
||||
|
||||
public_key = "%(key_type)s %(key_content)s %(username)s@%(hostname)s" % {
|
||||
@@ -137,17 +139,63 @@ def ssh_key_gen(length=2048, type='rsa', password=None, username='jumpserver', h
|
||||
|
||||
|
||||
def validate_ssh_private_key(text, password=None):
|
||||
if isinstance(text, bytes):
|
||||
try:
|
||||
text = text.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
return False
|
||||
key = parse_ssh_private_key_str(text, password=password)
|
||||
return bool(key)
|
||||
|
||||
key = ssh_key_string_to_obj(text, password=password)
|
||||
if key is None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def parse_ssh_private_key_str(text: bytes, password=None) -> str:
|
||||
private_key = _parse_ssh_private_key(text, password=password)
|
||||
if private_key is None:
|
||||
return ""
|
||||
private_key_bytes = private_key.private_bytes(serialization.Encoding.PEM,
|
||||
serialization.PrivateFormat.OpenSSH,
|
||||
serialization.NoEncryption())
|
||||
return private_key_bytes.decode('utf-8')
|
||||
|
||||
|
||||
def parse_ssh_public_key_str(text: bytes = "", password=None) -> str:
|
||||
private_key = _parse_ssh_private_key(text, password=password)
|
||||
if private_key is None:
|
||||
return ""
|
||||
public_key_bytes = private_key.public_key().public_bytes(
|
||||
serialization.Encoding.OpenSSH,
|
||||
serialization.PublicFormat.OpenSSH)
|
||||
return public_key_bytes.decode('utf-8')
|
||||
|
||||
|
||||
def _parse_ssh_private_key(text, password=None):
|
||||
"""
|
||||
text: bytes
|
||||
password: str
|
||||
return:private key types:
|
||||
ec.EllipticCurvePrivateKey,
|
||||
rsa.RSAPrivateKey,
|
||||
dsa.DSAPrivateKey,
|
||||
ed25519.Ed25519PrivateKey,
|
||||
"""
|
||||
if isinstance(text, str):
|
||||
try:
|
||||
text = text.encode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
return None
|
||||
if password is not None:
|
||||
if isinstance(password, str):
|
||||
try:
|
||||
password = password.encode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
return None
|
||||
|
||||
try:
|
||||
if is_openssh_format_key(text):
|
||||
return serialization.load_ssh_private_key(text, password=password)
|
||||
return serialization.load_pem_private_key(text, password=password)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def is_openssh_format_key(text: bytes):
|
||||
return text.startswith(b"-----BEGIN OPENSSH PRIVATE KEY-----")
|
||||
|
||||
|
||||
def validate_ssh_public_key(text):
|
||||
|
||||
@@ -18,25 +18,35 @@ def random_ip():
|
||||
return socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
|
||||
|
||||
|
||||
def random_string(length, lower=True, upper=True, digit=True, special_char=False):
|
||||
chars = string.ascii_letters
|
||||
if digit:
|
||||
chars += string.digits
|
||||
def random_string(length: int, lower=True, upper=True, digit=True, special_char=False):
|
||||
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]
|
||||
args_string_map = dict(zip(args_names, args_string))
|
||||
kwargs = dict(zip(args_names, args_values))
|
||||
kwargs_keys = list(kwargs.keys())
|
||||
kwargs_values = list(kwargs.values())
|
||||
args_true_count = len([i for i in kwargs_values if i])
|
||||
assert any(kwargs_values), f'Parameters {kwargs_keys} must have at least one `True`'
|
||||
assert length >= args_true_count, f'Expected length >= {args_true_count}, bug got {length}'
|
||||
|
||||
can_startswith_special_char = args_true_count == 1 and special_char
|
||||
|
||||
chars = ''.join([args_string_map[k] for k, v in kwargs.items() if v])
|
||||
|
||||
while True:
|
||||
password = list(random.choice(chars) for i in range(length))
|
||||
if upper and not any(c.upper() for c in password):
|
||||
continue
|
||||
if lower and not any(c.lower() for c in password):
|
||||
continue
|
||||
if digit and not any(c.isdigit() for c in password):
|
||||
continue
|
||||
for k, v in kwargs.items():
|
||||
if v and not (set(password) & set(args_string_map[k])):
|
||||
# 没有包含指定的字符, retry
|
||||
break
|
||||
else:
|
||||
if not can_startswith_special_char and password[0] in args_string_map['special_char']:
|
||||
# 首位不能为特殊字符, retry
|
||||
continue
|
||||
else:
|
||||
# 满足要求终止 while 循环
|
||||
break
|
||||
|
||||
if special_char:
|
||||
spc = random.choice(string_punctuation)
|
||||
i = random.choice(range(1, len(password)))
|
||||
password[i] = spc
|
||||
|
||||
password = ''.join(password)
|
||||
return password
|
||||
|
||||
@@ -202,6 +202,7 @@ class Config(dict):
|
||||
'REDIS_SSL_KEY': None,
|
||||
'REDIS_SSL_CERT': None,
|
||||
'REDIS_SSL_CA': None,
|
||||
'REDIS_SSL_REQUIRED': 'none',
|
||||
# Redis Sentinel
|
||||
'REDIS_SENTINEL_HOSTS': '',
|
||||
'REDIS_SENTINEL_PASSWORD': '',
|
||||
@@ -269,6 +270,8 @@ class Config(dict):
|
||||
'AUTH_OPENID_USER_ATTR_MAP': {
|
||||
'name': 'name', 'username': 'preferred_username', 'email': 'email'
|
||||
},
|
||||
'AUTH_OPENID_PKCE': False,
|
||||
'AUTH_OPENID_CODE_CHALLENGE_METHOD': 'S256',
|
||||
|
||||
# OpenID 新配置参数 (version >= 1.5.9)
|
||||
'AUTH_OPENID_PROVIDER_ENDPOINT': 'https://oidc.example.com/',
|
||||
|
||||
@@ -78,6 +78,8 @@ AUTH_OPENID_SHARE_SESSION = CONFIG.AUTH_OPENID_SHARE_SESSION
|
||||
AUTH_OPENID_IGNORE_SSL_VERIFICATION = CONFIG.AUTH_OPENID_IGNORE_SSL_VERIFICATION
|
||||
AUTH_OPENID_ALWAYS_UPDATE_USER = CONFIG.AUTH_OPENID_ALWAYS_UPDATE_USER
|
||||
AUTH_OPENID_USER_ATTR_MAP = CONFIG.AUTH_OPENID_USER_ATTR_MAP
|
||||
AUTH_OPENID_PKCE = CONFIG.AUTH_OPENID_PKCE
|
||||
AUTH_OPENID_CODE_CHALLENGE_METHOD = CONFIG.AUTH_OPENID_CODE_CHALLENGE_METHOD
|
||||
AUTH_OPENID_AUTH_LOGIN_URL_NAME = 'authentication:openid:login'
|
||||
AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME = 'authentication:openid:login-callback'
|
||||
AUTH_OPENID_AUTH_LOGOUT_URL_NAME = 'authentication:openid:logout'
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import os
|
||||
import platform
|
||||
|
||||
from redis.sentinel import SentinelManagedSSLConnection
|
||||
|
||||
|
||||
if platform.system() == 'Darwin' and platform.machine() == 'arm64':
|
||||
import pymysql
|
||||
|
||||
@@ -195,7 +198,7 @@ DATABASES = {
|
||||
}
|
||||
}
|
||||
|
||||
DB_CA_PATH = os.path.join(PROJECT_DIR, 'data', 'certs', 'db_ca.pem')
|
||||
DB_CA_PATH = os.path.join(CERTS_DIR, 'db_ca.pem')
|
||||
DB_USE_SSL = False
|
||||
if CONFIG.DB_ENGINE.lower() == 'mysql':
|
||||
DB_OPTIONS['init_command'] = "SET sql_mode='STRICT_TRANS_TABLES'"
|
||||
@@ -317,10 +320,19 @@ if REDIS_SENTINEL_SERVICE_NAME and REDIS_SENTINELS:
|
||||
'CLIENT_CLASS': 'django_redis.client.SentinelClient',
|
||||
'SENTINELS': REDIS_SENTINELS, 'PASSWORD': CONFIG.REDIS_PASSWORD,
|
||||
'SENTINEL_KWARGS': {
|
||||
'ssl': REDIS_USE_SSL,
|
||||
'ssl_cert_reqs': REDIS_SSL_REQUIRED,
|
||||
"ssl_keyfile": REDIS_SSL_KEY,
|
||||
"ssl_certfile": REDIS_SSL_CERT,
|
||||
"ssl_ca_certs": REDIS_SSL_CA,
|
||||
'password': REDIS_SENTINEL_PASSWORD,
|
||||
'socket_timeout': REDIS_SENTINEL_SOCKET_TIMEOUT
|
||||
}
|
||||
})
|
||||
if REDIS_USE_SSL:
|
||||
REDIS_OPTIONS['CONNECTION_POOL_KWARGS'].update({
|
||||
'connection_class': SentinelManagedSSLConnection
|
||||
})
|
||||
DJANGO_REDIS_CONNECTION_FACTORY = 'django_redis.pool.SentinelConnectionFactory'
|
||||
else:
|
||||
REDIS_LOCATION_NO_DB = '%(protocol)s://:%(password)s@%(host)s:%(port)s/{}' % {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import os
|
||||
import ssl
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from .base import (
|
||||
REDIS_SSL_CA, REDIS_SSL_CERT, REDIS_SSL_KEY, REDIS_SSL_REQUIRED, REDIS_USE_SSL,
|
||||
REDIS_SENTINEL_SERVICE_NAME, REDIS_SENTINELS, REDIS_SENTINEL_PASSWORD,
|
||||
REDIS_PROTOCOL, REDIS_SENTINEL_SERVICE_NAME, REDIS_SENTINELS, REDIS_SENTINEL_PASSWORD,
|
||||
REDIS_SENTINEL_SOCKET_TIMEOUT
|
||||
)
|
||||
from ..const import CONFIG, PROJECT_DIR
|
||||
@@ -81,41 +81,54 @@ BOOTSTRAP3 = {
|
||||
}
|
||||
|
||||
# Django channels support websocket
|
||||
if not REDIS_USE_SSL:
|
||||
redis_ssl = None
|
||||
else:
|
||||
redis_ssl = ssl.SSLContext()
|
||||
redis_ssl.check_hostname = bool(CONFIG.REDIS_SSL_REQUIRED)
|
||||
if REDIS_SSL_CA:
|
||||
redis_ssl.load_verify_locations(REDIS_SSL_CA)
|
||||
if REDIS_SSL_CERT and REDIS_SSL_KEY:
|
||||
redis_ssl.load_cert_chain(REDIS_SSL_CERT, REDIS_SSL_KEY)
|
||||
|
||||
REDIS_HOST = {
|
||||
REDIS_LAYERS_HOST = {
|
||||
'db': CONFIG.REDIS_DB_WS,
|
||||
'password': CONFIG.REDIS_PASSWORD or None,
|
||||
'ssl': redis_ssl,
|
||||
}
|
||||
|
||||
REDIS_LAYERS_SSL_PARAMS = {}
|
||||
if REDIS_USE_SSL:
|
||||
REDIS_LAYERS_SSL_PARAMS.update({
|
||||
'ssl': REDIS_USE_SSL,
|
||||
'ssl_cert_reqs': REDIS_SSL_REQUIRED,
|
||||
"ssl_keyfile": REDIS_SSL_KEY,
|
||||
"ssl_certfile": REDIS_SSL_CERT,
|
||||
"ssl_ca_certs": REDIS_SSL_CA
|
||||
})
|
||||
REDIS_LAYERS_HOST.update(REDIS_LAYERS_SSL_PARAMS)
|
||||
|
||||
if REDIS_SENTINEL_SERVICE_NAME and REDIS_SENTINELS:
|
||||
REDIS_HOST['sentinels'] = REDIS_SENTINELS
|
||||
REDIS_HOST['master_name'] = REDIS_SENTINEL_SERVICE_NAME
|
||||
REDIS_HOST['sentinel_kwargs'] = {
|
||||
REDIS_LAYERS_HOST['sentinels'] = REDIS_SENTINELS
|
||||
REDIS_LAYERS_HOST['master_name'] = REDIS_SENTINEL_SERVICE_NAME
|
||||
REDIS_LAYERS_HOST['sentinel_kwargs'] = {
|
||||
'password': REDIS_SENTINEL_PASSWORD,
|
||||
'socket_timeout': REDIS_SENTINEL_SOCKET_TIMEOUT
|
||||
'socket_timeout': REDIS_SENTINEL_SOCKET_TIMEOUT,
|
||||
'ssl': REDIS_USE_SSL,
|
||||
'ssl_cert_reqs': REDIS_SSL_REQUIRED,
|
||||
"ssl_keyfile": REDIS_SSL_KEY,
|
||||
"ssl_certfile": REDIS_SSL_CERT,
|
||||
"ssl_ca_certs": REDIS_SSL_CA
|
||||
}
|
||||
else:
|
||||
REDIS_HOST['address'] = (CONFIG.REDIS_HOST, CONFIG.REDIS_PORT)
|
||||
# More info see: https://github.com/django/channels_redis/issues/334
|
||||
# REDIS_LAYERS_HOST['address'] = (CONFIG.REDIS_HOST, CONFIG.REDIS_PORT)
|
||||
REDIS_LAYERS_ADDRESS = '{protocol}://:{password}@{host}:{port}/{db}'.format(
|
||||
protocol=REDIS_PROTOCOL, password=CONFIG.REDIS_PASSWORD,
|
||||
host=CONFIG.REDIS_HOST, port=CONFIG.REDIS_PORT, db=CONFIG.REDIS_DB_WS
|
||||
)
|
||||
REDIS_LAYERS_SSL_PARAMS.pop('ssl', None)
|
||||
REDIS_LAYERS_HOST['address'] = '{}?{}'.format(REDIS_LAYERS_ADDRESS, urlencode(REDIS_LAYERS_SSL_PARAMS))
|
||||
|
||||
|
||||
CHANNEL_LAYERS = {
|
||||
'default': {
|
||||
'BACKEND': 'common.cache.RedisChannelLayer',
|
||||
'CONFIG': {
|
||||
"hosts": [REDIS_HOST],
|
||||
"hosts": [REDIS_LAYERS_HOST],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ASGI_APPLICATION = 'jumpserver.routing.application'
|
||||
|
||||
# Dump all celery log to here
|
||||
@@ -132,13 +145,18 @@ if REDIS_SENTINEL_SERVICE_NAME and REDIS_SENTINELS:
|
||||
'master_name': REDIS_SENTINEL_SERVICE_NAME,
|
||||
'sentinel_kwargs': {
|
||||
'password': REDIS_SENTINEL_PASSWORD,
|
||||
'socket_timeout': REDIS_SENTINEL_SOCKET_TIMEOUT
|
||||
'socket_timeout': REDIS_SENTINEL_SOCKET_TIMEOUT,
|
||||
'ssl': REDIS_USE_SSL,
|
||||
'ssl_cert_reqs': REDIS_SSL_REQUIRED,
|
||||
"ssl_keyfile": REDIS_SSL_KEY,
|
||||
"ssl_certfile": REDIS_SSL_CERT,
|
||||
"ssl_ca_certs": REDIS_SSL_CA
|
||||
}
|
||||
}
|
||||
CELERY_BROKER_TRANSPORT_OPTIONS = CELERY_RESULT_BACKEND_TRANSPORT_OPTIONS = SENTINEL_OPTIONS
|
||||
else:
|
||||
CELERY_BROKER_URL = CELERY_BROKER_URL_FORMAT % {
|
||||
'protocol': 'rediss' if REDIS_USE_SSL else 'redis',
|
||||
'protocol': REDIS_PROTOCOL,
|
||||
'password': CONFIG.REDIS_PASSWORD,
|
||||
'host': CONFIG.REDIS_HOST,
|
||||
'port': CONFIG.REDIS_PORT,
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-17 17:34+0800\n"
|
||||
"POT-Creation-Date: 2022-12-13 15:01+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"
|
||||
@@ -24,13 +24,13 @@ msgstr "Acls"
|
||||
|
||||
#: acls/models/base.py:25 acls/serializers/login_asset_acl.py:47
|
||||
#: applications/models/application.py:219 assets/models/asset.py:138
|
||||
#: assets/models/base.py:175 assets/models/cluster.py:18
|
||||
#: assets/models/base.py:173 assets/models/cluster.py:18
|
||||
#: assets/models/cmd_filter.py:27 assets/models/domain.py:23
|
||||
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
|
||||
#: orgs/models.py:70 perms/models/base.py:83 rbac/models/role.py:29
|
||||
#: settings/models.py:33 settings/serializers/sms.py:6
|
||||
#: terminal/models/endpoint.py:14 terminal/models/endpoint.py:87
|
||||
#: terminal/models/storage.py:27 terminal/models/task.py:16
|
||||
#: terminal/models/storage.py:26 terminal/models/task.py:16
|
||||
#: terminal/models/terminal.py:101 users/forms/profile.py:33
|
||||
#: users/models/group.py:15 users/models/user.py:673
|
||||
#: xpack/plugins/cloud/models.py:27
|
||||
@@ -55,14 +55,14 @@ msgstr "アクティブ"
|
||||
|
||||
#: acls/models/base.py:32 applications/models/application.py:232
|
||||
#: assets/models/asset.py:143 assets/models/asset.py:231
|
||||
#: assets/models/backup.py:54 assets/models/base.py:180
|
||||
#: assets/models/backup.py:54 assets/models/base.py:178
|
||||
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:52
|
||||
#: assets/models/cmd_filter.py:100 assets/models/domain.py:24
|
||||
#: assets/models/domain.py:65 assets/models/group.py:23
|
||||
#: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:73
|
||||
#: perms/models/base.py:93 rbac/models/role.py:37 settings/models.py:38
|
||||
#: terminal/models/endpoint.py:22 terminal/models/endpoint.py:97
|
||||
#: terminal/models/storage.py:30 terminal/models/terminal.py:115
|
||||
#: terminal/models/storage.py:29 terminal/models/terminal.py:115
|
||||
#: tickets/models/comment.py:32 tickets/models/ticket/general.py:288
|
||||
#: users/models/group.py:16 users/models/user.py:712
|
||||
#: xpack/plugins/change_auth_plan/models/base.py:44
|
||||
@@ -155,7 +155,7 @@ msgid "Format for comma-delimited string, with * indicating a match all. "
|
||||
msgstr "コンマ区切り文字列の形式。* はすべて一致することを示します。"
|
||||
|
||||
#: acls/serializers/login_acl.py:15 acls/serializers/login_asset_acl.py:17
|
||||
#: acls/serializers/login_asset_acl.py:51 assets/models/base.py:176
|
||||
#: acls/serializers/login_asset_acl.py:51 assets/models/base.py:174
|
||||
#: assets/models/gathered_user.py:15 audits/models.py:139
|
||||
#: authentication/forms.py:25 authentication/forms.py:27
|
||||
#: authentication/models.py:260
|
||||
@@ -310,7 +310,7 @@ msgstr "カテゴリ"
|
||||
#: assets/models/cmd_filter.py:86 assets/models/user.py:251
|
||||
#: authentication/models.py:70 perms/models/application_permission.py:24
|
||||
#: perms/serializers/application/user_permission.py:34
|
||||
#: terminal/models/storage.py:59 terminal/models/storage.py:145
|
||||
#: terminal/models/storage.py:58 terminal/models/storage.py:147
|
||||
#: tickets/models/comment.py:26 tickets/models/flow.py:57
|
||||
#: tickets/models/ticket/apply_application.py:18
|
||||
#: tickets/models/ticket/general.py:273
|
||||
@@ -353,7 +353,7 @@ msgid "Type display"
|
||||
msgstr "タイプ表示"
|
||||
|
||||
#: applications/serializers/application.py:105 assets/models/asset.py:230
|
||||
#: assets/models/base.py:181 assets/models/cluster.py:26
|
||||
#: assets/models/base.py:179 assets/models/cluster.py:26
|
||||
#: assets/models/cmd_filter.py:53 assets/models/domain.py:26
|
||||
#: assets/models/gathered_user.py:19 assets/models/group.py:22
|
||||
#: assets/models/label.py:25 assets/serializers/account.py:18
|
||||
@@ -367,7 +367,7 @@ msgstr "タイプ表示"
|
||||
msgid "Date created"
|
||||
msgstr "作成された日付"
|
||||
|
||||
#: applications/serializers/application.py:106 assets/models/base.py:182
|
||||
#: applications/serializers/application.py:106 assets/models/base.py:180
|
||||
#: assets/models/cmd_filter.py:54 assets/models/gathered_user.py:20
|
||||
#: assets/serializers/account.py:21 assets/serializers/cmd_filter.py:29
|
||||
#: assets/serializers/cmd_filter.py:49 common/db/models.py:117
|
||||
@@ -650,7 +650,7 @@ msgstr "資産番号"
|
||||
msgid "Labels"
|
||||
msgstr "ラベル"
|
||||
|
||||
#: assets/models/asset.py:229 assets/models/base.py:183
|
||||
#: assets/models/asset.py:229 assets/models/base.py:181
|
||||
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:56
|
||||
#: assets/models/cmd_filter.py:103 assets/models/group.py:21
|
||||
#: common/db/models.py:114 common/mixins/models.py:49 orgs/models.py:71
|
||||
@@ -786,32 +786,32 @@ msgstr "成功は"
|
||||
msgid "Account backup execution"
|
||||
msgstr "アカウントバックアップの実行"
|
||||
|
||||
#: assets/models/base.py:30 assets/tasks/const.py:51 audits/const.py:5
|
||||
#: assets/models/base.py:28 assets/tasks/const.py:51 audits/const.py:5
|
||||
#: common/utils/ip/geoip/utils.py:31 common/utils/ip/geoip/utils.py:37
|
||||
#: common/utils/ip/utils.py:84
|
||||
msgid "Unknown"
|
||||
msgstr "不明"
|
||||
|
||||
#: assets/models/base.py:31
|
||||
#: assets/models/base.py:29
|
||||
msgid "Ok"
|
||||
msgstr "OK"
|
||||
|
||||
#: assets/models/base.py:32 audits/models.py:136
|
||||
#: assets/models/base.py:30 audits/models.py:136
|
||||
#: xpack/plugins/change_auth_plan/serializers/app.py:88
|
||||
#: xpack/plugins/change_auth_plan/serializers/asset.py:199
|
||||
#: xpack/plugins/cloud/const.py:41
|
||||
msgid "Failed"
|
||||
msgstr "失敗しました"
|
||||
|
||||
#: assets/models/base.py:38 assets/serializers/domain.py:47
|
||||
#: assets/models/base.py:36 assets/serializers/domain.py:47
|
||||
msgid "Connectivity"
|
||||
msgstr "接続性"
|
||||
|
||||
#: assets/models/base.py:40 authentication/models.py:263
|
||||
#: assets/models/base.py:38 authentication/models.py:263
|
||||
msgid "Date verified"
|
||||
msgstr "確認済みの日付"
|
||||
|
||||
#: assets/models/base.py:177 assets/serializers/base.py:15
|
||||
#: assets/models/base.py:175 assets/serializers/base.py:14
|
||||
#: assets/serializers/base.py:37 assets/serializers/system_user.py:29
|
||||
#: audits/signal_handlers.py:58 authentication/confirm/password.py:9
|
||||
#: authentication/forms.py:32
|
||||
@@ -829,14 +829,14 @@ msgstr "確認済みの日付"
|
||||
msgid "Password"
|
||||
msgstr "パスワード"
|
||||
|
||||
#: assets/models/base.py:178 assets/serializers/base.py:41
|
||||
#: assets/models/base.py:176 assets/serializers/base.py:41
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:53
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:130
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:206
|
||||
msgid "SSH private key"
|
||||
msgstr "SSH秘密鍵"
|
||||
|
||||
#: assets/models/base.py:179 xpack/plugins/change_auth_plan/models/asset.py:56
|
||||
#: assets/models/base.py:177 xpack/plugins/change_auth_plan/models/asset.py:56
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:126
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:202
|
||||
msgid "SSH public key"
|
||||
@@ -1192,7 +1192,7 @@ msgstr "ssh秘密鍵"
|
||||
msgid "Key password"
|
||||
msgstr "キーパスワード"
|
||||
|
||||
#: assets/serializers/base.py:58
|
||||
#: assets/serializers/base.py:58 assets/serializers/utils.py:24
|
||||
msgid "private key invalid or passphrase error"
|
||||
msgstr "秘密鍵が無効またはpassphraseエラー"
|
||||
|
||||
@@ -1305,15 +1305,15 @@ msgstr "組織名"
|
||||
msgid "Asset hostname"
|
||||
msgstr "資産ホスト名"
|
||||
|
||||
#: assets/serializers/utils.py:11
|
||||
#: assets/serializers/utils.py:13
|
||||
msgid "Password can not contains `{{` "
|
||||
msgstr "パスワードには '{{' を含まない"
|
||||
|
||||
#: assets/serializers/utils.py:14
|
||||
#: assets/serializers/utils.py:16
|
||||
msgid "Password can not contains `'` "
|
||||
msgstr "パスワードには `'` を含まない"
|
||||
|
||||
#: assets/serializers/utils.py:16
|
||||
#: assets/serializers/utils.py:18
|
||||
msgid "Password can not contains `\"` "
|
||||
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:413 ops/tasks.py:145 ops/tasks.py:148
|
||||
#: jumpserver/conf.py:416 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:412
|
||||
#: jumpserver/conf.py:415
|
||||
msgid "Create account successfully"
|
||||
msgstr "アカウントを正常に作成"
|
||||
|
||||
#: jumpserver/conf.py:414
|
||||
#: jumpserver/conf.py:417
|
||||
msgid "Your account has been created successfully"
|
||||
msgstr "アカウントが正常に作成されました"
|
||||
|
||||
@@ -3603,7 +3603,7 @@ msgstr "CAS"
|
||||
msgid "Enable CAS Auth"
|
||||
msgstr "CAS 認証の有効化"
|
||||
|
||||
#: settings/serializers/auth/cas.py:13 settings/serializers/auth/oidc.py:49
|
||||
#: settings/serializers/auth/cas.py:13 settings/serializers/auth/oidc.py:54
|
||||
msgid "Server url"
|
||||
msgstr "サービス側アドレス"
|
||||
|
||||
@@ -3723,11 +3723,11 @@ msgstr "クライアントID"
|
||||
msgid "Client Secret"
|
||||
msgstr "クライアント秘密"
|
||||
|
||||
#: settings/serializers/auth/oauth2.py:40 settings/serializers/auth/oidc.py:63
|
||||
#: settings/serializers/auth/oauth2.py:40 settings/serializers/auth/oidc.py:68
|
||||
msgid "Provider auth endpoint"
|
||||
msgstr "認証エンドポイントアドレス"
|
||||
|
||||
#: settings/serializers/auth/oauth2.py:43 settings/serializers/auth/oidc.py:66
|
||||
#: settings/serializers/auth/oauth2.py:43 settings/serializers/auth/oidc.py:71
|
||||
msgid "Provider token endpoint"
|
||||
msgstr "プロバイダートークンエンドポイント"
|
||||
|
||||
@@ -3735,15 +3735,15 @@ msgstr "プロバイダートークンエンドポイント"
|
||||
msgid "Client authentication method"
|
||||
msgstr "クライアント認証方式"
|
||||
|
||||
#: settings/serializers/auth/oauth2.py:50 settings/serializers/auth/oidc.py:72
|
||||
#: settings/serializers/auth/oauth2.py:50 settings/serializers/auth/oidc.py:77
|
||||
msgid "Provider userinfo endpoint"
|
||||
msgstr "プロバイダーuserinfoエンドポイント"
|
||||
|
||||
#: settings/serializers/auth/oauth2.py:53 settings/serializers/auth/oidc.py:75
|
||||
#: settings/serializers/auth/oauth2.py:53 settings/serializers/auth/oidc.py:80
|
||||
msgid "Provider end session endpoint"
|
||||
msgstr "プロバイダーのセッション終了エンドポイント"
|
||||
|
||||
#: settings/serializers/auth/oauth2.py:60 settings/serializers/auth/oidc.py:93
|
||||
#: settings/serializers/auth/oauth2.py:60 settings/serializers/auth/oidc.py:98
|
||||
#: settings/serializers/auth/saml2.py:35
|
||||
msgid "Always update user"
|
||||
msgstr "常にユーザーを更新"
|
||||
@@ -3772,51 +3772,59 @@ msgstr ""
|
||||
"ユーザー属性マッピングは、OpenIDのユーザー属性をjumpserverユーザーにマッピン"
|
||||
"グする方法、username, name,emailはjumpserverのユーザーが必要とする属性です"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:46
|
||||
#: settings/serializers/auth/oidc.py:41
|
||||
msgid "Enable PKCE"
|
||||
msgstr "启启PKCE"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:43
|
||||
msgid "Code challenge method"
|
||||
msgstr "Code暗号化方式です"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:51
|
||||
msgid "Use Keycloak"
|
||||
msgstr "Keycloakを使用する"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:52
|
||||
#: settings/serializers/auth/oidc.py:57
|
||||
msgid "Realm name"
|
||||
msgstr "レルム名"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:58
|
||||
#: settings/serializers/auth/oidc.py:63
|
||||
msgid "Enable OPENID Auth"
|
||||
msgstr "OIDC認証の有効化"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:60
|
||||
#: settings/serializers/auth/oidc.py:65
|
||||
msgid "Provider endpoint"
|
||||
msgstr "プロバイダーエンドポイント"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:69
|
||||
#: settings/serializers/auth/oidc.py:74
|
||||
msgid "Provider jwks endpoint"
|
||||
msgstr "プロバイダーjwksエンドポイント"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:78
|
||||
#: settings/serializers/auth/oidc.py:83
|
||||
msgid "Provider sign alg"
|
||||
msgstr "プロビダーサインalg"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:81
|
||||
#: settings/serializers/auth/oidc.py:86
|
||||
msgid "Provider sign key"
|
||||
msgstr "プロバイダ署名キー"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:83
|
||||
#: settings/serializers/auth/oidc.py:88
|
||||
msgid "Scopes"
|
||||
msgstr "スコープ"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:85
|
||||
#: settings/serializers/auth/oidc.py:90
|
||||
msgid "Id token max age"
|
||||
msgstr "IDトークンの最大年齢"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:88
|
||||
#: settings/serializers/auth/oidc.py:93
|
||||
msgid "Id token include claims"
|
||||
msgstr "IDトークンにはクレームが含まれます"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:90
|
||||
#: settings/serializers/auth/oidc.py:95
|
||||
msgid "Use state"
|
||||
msgstr "使用状態"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:91
|
||||
#: settings/serializers/auth/oidc.py:96
|
||||
msgid "Use nonce"
|
||||
msgstr "Nonceを使用"
|
||||
|
||||
@@ -5139,15 +5147,15 @@ msgstr "スレッド"
|
||||
msgid "Boot Time"
|
||||
msgstr "ブート時間"
|
||||
|
||||
#: terminal/models/storage.py:29
|
||||
#: terminal/models/storage.py:28
|
||||
msgid "Default storage"
|
||||
msgstr "デフォルトのストレージ"
|
||||
|
||||
#: terminal/models/storage.py:139 terminal/models/terminal.py:109
|
||||
#: terminal/models/storage.py:141 terminal/models/terminal.py:109
|
||||
msgid "Command storage"
|
||||
msgstr "コマンドストレージ"
|
||||
|
||||
#: terminal/models/storage.py:199 terminal/models/terminal.py:110
|
||||
#: terminal/models/storage.py:201 terminal/models/terminal.py:110
|
||||
msgid "Replay storage"
|
||||
msgstr "再生ストレージ"
|
||||
|
||||
@@ -5444,19 +5452,19 @@ msgstr ""
|
||||
"チケットのタイトル: {} チケット申請者: {} チケットプロセッサ: {} チケットID: "
|
||||
"{}"
|
||||
|
||||
#: tickets/handlers/base.py:84
|
||||
#: tickets/handlers/base.py:86
|
||||
msgid "Change field"
|
||||
msgstr "フィールドを変更"
|
||||
|
||||
#: tickets/handlers/base.py:84
|
||||
#: tickets/handlers/base.py:86
|
||||
msgid "Before change"
|
||||
msgstr "変更前"
|
||||
|
||||
#: tickets/handlers/base.py:84
|
||||
#: tickets/handlers/base.py:86
|
||||
msgid "After change"
|
||||
msgstr "変更後"
|
||||
|
||||
#: tickets/handlers/base.py:96
|
||||
#: tickets/handlers/base.py:98
|
||||
msgid "{} {} the ticket"
|
||||
msgstr "{} {} チケット"
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-17 17:34+0800\n"
|
||||
"POT-Creation-Date: 2022-12-13 15:00+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"
|
||||
@@ -23,13 +23,13 @@ msgstr "访问控制"
|
||||
|
||||
#: acls/models/base.py:25 acls/serializers/login_asset_acl.py:47
|
||||
#: applications/models/application.py:219 assets/models/asset.py:138
|
||||
#: assets/models/base.py:175 assets/models/cluster.py:18
|
||||
#: assets/models/base.py:173 assets/models/cluster.py:18
|
||||
#: assets/models/cmd_filter.py:27 assets/models/domain.py:23
|
||||
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
|
||||
#: orgs/models.py:70 perms/models/base.py:83 rbac/models/role.py:29
|
||||
#: settings/models.py:33 settings/serializers/sms.py:6
|
||||
#: terminal/models/endpoint.py:14 terminal/models/endpoint.py:87
|
||||
#: terminal/models/storage.py:27 terminal/models/task.py:16
|
||||
#: terminal/models/storage.py:26 terminal/models/task.py:16
|
||||
#: terminal/models/terminal.py:101 users/forms/profile.py:33
|
||||
#: users/models/group.py:15 users/models/user.py:673
|
||||
#: xpack/plugins/cloud/models.py:27
|
||||
@@ -54,14 +54,14 @@ msgstr "激活中"
|
||||
|
||||
#: acls/models/base.py:32 applications/models/application.py:232
|
||||
#: assets/models/asset.py:143 assets/models/asset.py:231
|
||||
#: assets/models/backup.py:54 assets/models/base.py:180
|
||||
#: assets/models/backup.py:54 assets/models/base.py:178
|
||||
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:52
|
||||
#: assets/models/cmd_filter.py:100 assets/models/domain.py:24
|
||||
#: assets/models/domain.py:65 assets/models/group.py:23
|
||||
#: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:73
|
||||
#: perms/models/base.py:93 rbac/models/role.py:37 settings/models.py:38
|
||||
#: terminal/models/endpoint.py:22 terminal/models/endpoint.py:97
|
||||
#: terminal/models/storage.py:30 terminal/models/terminal.py:115
|
||||
#: terminal/models/storage.py:29 terminal/models/terminal.py:115
|
||||
#: tickets/models/comment.py:32 tickets/models/ticket/general.py:288
|
||||
#: users/models/group.py:16 users/models/user.py:712
|
||||
#: xpack/plugins/change_auth_plan/models/base.py:44
|
||||
@@ -154,7 +154,7 @@ msgid "Format for comma-delimited string, with * indicating a match all. "
|
||||
msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
|
||||
|
||||
#: acls/serializers/login_acl.py:15 acls/serializers/login_asset_acl.py:17
|
||||
#: acls/serializers/login_asset_acl.py:51 assets/models/base.py:176
|
||||
#: acls/serializers/login_asset_acl.py:51 assets/models/base.py:174
|
||||
#: assets/models/gathered_user.py:15 audits/models.py:139
|
||||
#: authentication/forms.py:25 authentication/forms.py:27
|
||||
#: authentication/models.py:260
|
||||
@@ -305,7 +305,7 @@ msgstr "类别"
|
||||
#: assets/models/cmd_filter.py:86 assets/models/user.py:251
|
||||
#: authentication/models.py:70 perms/models/application_permission.py:24
|
||||
#: perms/serializers/application/user_permission.py:34
|
||||
#: terminal/models/storage.py:59 terminal/models/storage.py:145
|
||||
#: terminal/models/storage.py:58 terminal/models/storage.py:147
|
||||
#: tickets/models/comment.py:26 tickets/models/flow.py:57
|
||||
#: tickets/models/ticket/apply_application.py:18
|
||||
#: tickets/models/ticket/general.py:273
|
||||
@@ -348,7 +348,7 @@ msgid "Type display"
|
||||
msgstr "类型名称"
|
||||
|
||||
#: applications/serializers/application.py:105 assets/models/asset.py:230
|
||||
#: assets/models/base.py:181 assets/models/cluster.py:26
|
||||
#: assets/models/base.py:179 assets/models/cluster.py:26
|
||||
#: assets/models/cmd_filter.py:53 assets/models/domain.py:26
|
||||
#: assets/models/gathered_user.py:19 assets/models/group.py:22
|
||||
#: assets/models/label.py:25 assets/serializers/account.py:18
|
||||
@@ -362,7 +362,7 @@ msgstr "类型名称"
|
||||
msgid "Date created"
|
||||
msgstr "创建日期"
|
||||
|
||||
#: applications/serializers/application.py:106 assets/models/base.py:182
|
||||
#: applications/serializers/application.py:106 assets/models/base.py:180
|
||||
#: assets/models/cmd_filter.py:54 assets/models/gathered_user.py:20
|
||||
#: assets/serializers/account.py:21 assets/serializers/cmd_filter.py:29
|
||||
#: assets/serializers/cmd_filter.py:49 common/db/models.py:117
|
||||
@@ -643,7 +643,7 @@ msgstr "资产编号"
|
||||
msgid "Labels"
|
||||
msgstr "标签管理"
|
||||
|
||||
#: assets/models/asset.py:229 assets/models/base.py:183
|
||||
#: assets/models/asset.py:229 assets/models/base.py:181
|
||||
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:56
|
||||
#: assets/models/cmd_filter.py:103 assets/models/group.py:21
|
||||
#: common/db/models.py:114 common/mixins/models.py:49 orgs/models.py:71
|
||||
@@ -779,32 +779,32 @@ msgstr "是否成功"
|
||||
msgid "Account backup execution"
|
||||
msgstr "账号备份执行"
|
||||
|
||||
#: assets/models/base.py:30 assets/tasks/const.py:51 audits/const.py:5
|
||||
#: assets/models/base.py:28 assets/tasks/const.py:51 audits/const.py:5
|
||||
#: common/utils/ip/geoip/utils.py:31 common/utils/ip/geoip/utils.py:37
|
||||
#: common/utils/ip/utils.py:84
|
||||
msgid "Unknown"
|
||||
msgstr "未知"
|
||||
|
||||
#: assets/models/base.py:31
|
||||
#: assets/models/base.py:29
|
||||
msgid "Ok"
|
||||
msgstr "成功"
|
||||
|
||||
#: assets/models/base.py:32 audits/models.py:136
|
||||
#: assets/models/base.py:30 audits/models.py:136
|
||||
#: xpack/plugins/change_auth_plan/serializers/app.py:88
|
||||
#: xpack/plugins/change_auth_plan/serializers/asset.py:199
|
||||
#: xpack/plugins/cloud/const.py:41
|
||||
msgid "Failed"
|
||||
msgstr "失败"
|
||||
|
||||
#: assets/models/base.py:38 assets/serializers/domain.py:47
|
||||
#: assets/models/base.py:36 assets/serializers/domain.py:47
|
||||
msgid "Connectivity"
|
||||
msgstr "可连接性"
|
||||
|
||||
#: assets/models/base.py:40 authentication/models.py:263
|
||||
#: assets/models/base.py:38 authentication/models.py:263
|
||||
msgid "Date verified"
|
||||
msgstr "校验日期"
|
||||
|
||||
#: assets/models/base.py:177 assets/serializers/base.py:15
|
||||
#: assets/models/base.py:175 assets/serializers/base.py:14
|
||||
#: assets/serializers/base.py:37 assets/serializers/system_user.py:29
|
||||
#: audits/signal_handlers.py:58 authentication/confirm/password.py:9
|
||||
#: authentication/forms.py:32
|
||||
@@ -822,14 +822,14 @@ msgstr "校验日期"
|
||||
msgid "Password"
|
||||
msgstr "密码"
|
||||
|
||||
#: assets/models/base.py:178 assets/serializers/base.py:41
|
||||
#: assets/models/base.py:176 assets/serializers/base.py:41
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:53
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:130
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:206
|
||||
msgid "SSH private key"
|
||||
msgstr "SSH密钥"
|
||||
|
||||
#: assets/models/base.py:179 xpack/plugins/change_auth_plan/models/asset.py:56
|
||||
#: assets/models/base.py:177 xpack/plugins/change_auth_plan/models/asset.py:56
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:126
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:202
|
||||
msgid "SSH public key"
|
||||
@@ -1182,7 +1182,7 @@ msgstr "ssh私钥"
|
||||
msgid "Key password"
|
||||
msgstr "密钥密码"
|
||||
|
||||
#: assets/serializers/base.py:58
|
||||
#: assets/serializers/base.py:58 assets/serializers/utils.py:24
|
||||
msgid "private key invalid or passphrase error"
|
||||
msgstr "密钥不合法或密钥密码错误"
|
||||
|
||||
@@ -1295,15 +1295,15 @@ msgstr "组织名称"
|
||||
msgid "Asset hostname"
|
||||
msgstr "资产主机名"
|
||||
|
||||
#: assets/serializers/utils.py:11
|
||||
#: assets/serializers/utils.py:13
|
||||
msgid "Password can not contains `{{` "
|
||||
msgstr "密码不能包含 `{{` 字符"
|
||||
|
||||
#: assets/serializers/utils.py:14
|
||||
#: assets/serializers/utils.py:16
|
||||
msgid "Password can not contains `'` "
|
||||
msgstr "密码不能包含 `'` 字符"
|
||||
|
||||
#: assets/serializers/utils.py:16
|
||||
#: assets/serializers/utils.py:18
|
||||
msgid "Password can not contains `\"` "
|
||||
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:413 ops/tasks.py:145 ops/tasks.py:148
|
||||
#: jumpserver/conf.py:416 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:412
|
||||
#: jumpserver/conf.py:415
|
||||
msgid "Create account successfully"
|
||||
msgstr "创建账号成功"
|
||||
|
||||
#: jumpserver/conf.py:414
|
||||
#: jumpserver/conf.py:417
|
||||
msgid "Your account has been created successfully"
|
||||
msgstr "你的账号已创建成功"
|
||||
|
||||
@@ -3555,7 +3555,7 @@ msgstr "CAS"
|
||||
msgid "Enable CAS Auth"
|
||||
msgstr "启用 CAS 认证"
|
||||
|
||||
#: settings/serializers/auth/cas.py:13 settings/serializers/auth/oidc.py:49
|
||||
#: settings/serializers/auth/cas.py:13 settings/serializers/auth/oidc.py:54
|
||||
msgid "Server url"
|
||||
msgstr "服务端地址"
|
||||
|
||||
@@ -3675,11 +3675,11 @@ msgstr "客户端 ID"
|
||||
msgid "Client Secret"
|
||||
msgstr "客户端密钥"
|
||||
|
||||
#: settings/serializers/auth/oauth2.py:40 settings/serializers/auth/oidc.py:63
|
||||
#: settings/serializers/auth/oauth2.py:40 settings/serializers/auth/oidc.py:68
|
||||
msgid "Provider auth endpoint"
|
||||
msgstr "授权端点地址"
|
||||
|
||||
#: settings/serializers/auth/oauth2.py:43 settings/serializers/auth/oidc.py:66
|
||||
#: settings/serializers/auth/oauth2.py:43 settings/serializers/auth/oidc.py:71
|
||||
msgid "Provider token endpoint"
|
||||
msgstr "token 端点地址"
|
||||
|
||||
@@ -3687,15 +3687,15 @@ msgstr "token 端点地址"
|
||||
msgid "Client authentication method"
|
||||
msgstr "客户端认证方式"
|
||||
|
||||
#: settings/serializers/auth/oauth2.py:50 settings/serializers/auth/oidc.py:72
|
||||
#: settings/serializers/auth/oauth2.py:50 settings/serializers/auth/oidc.py:77
|
||||
msgid "Provider userinfo endpoint"
|
||||
msgstr "用户信息端点地址"
|
||||
|
||||
#: settings/serializers/auth/oauth2.py:53 settings/serializers/auth/oidc.py:75
|
||||
#: settings/serializers/auth/oauth2.py:53 settings/serializers/auth/oidc.py:80
|
||||
msgid "Provider end session endpoint"
|
||||
msgstr "注销会话端点地址"
|
||||
|
||||
#: settings/serializers/auth/oauth2.py:60 settings/serializers/auth/oidc.py:93
|
||||
#: settings/serializers/auth/oauth2.py:60 settings/serializers/auth/oidc.py:98
|
||||
#: settings/serializers/auth/saml2.py:35
|
||||
msgid "Always update user"
|
||||
msgstr "总是更新用户信息"
|
||||
@@ -3724,51 +3724,59 @@ msgstr ""
|
||||
"用户属性映射代表怎样将OpenID中用户属性映射到jumpserver用户上,username, name,"
|
||||
"email 是jumpserver的用户需要属性"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:46
|
||||
#: settings/serializers/auth/oidc.py:41
|
||||
msgid "Enable PKCE"
|
||||
msgstr "启用 PKCE"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:43
|
||||
msgid "Code challenge method"
|
||||
msgstr "Code加密方式"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:51
|
||||
msgid "Use Keycloak"
|
||||
msgstr "使用 Keycloak"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:52
|
||||
#: settings/serializers/auth/oidc.py:57
|
||||
msgid "Realm name"
|
||||
msgstr "域"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:58
|
||||
#: settings/serializers/auth/oidc.py:63
|
||||
msgid "Enable OPENID Auth"
|
||||
msgstr "启用 OIDC 认证"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:60
|
||||
#: settings/serializers/auth/oidc.py:65
|
||||
msgid "Provider endpoint"
|
||||
msgstr "端点地址"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:69
|
||||
#: settings/serializers/auth/oidc.py:74
|
||||
msgid "Provider jwks endpoint"
|
||||
msgstr "jwks 端点地址"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:78
|
||||
#: settings/serializers/auth/oidc.py:83
|
||||
msgid "Provider sign alg"
|
||||
msgstr "签名算法"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:81
|
||||
#: settings/serializers/auth/oidc.py:86
|
||||
msgid "Provider sign key"
|
||||
msgstr "签名 Key"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:83
|
||||
#: settings/serializers/auth/oidc.py:88
|
||||
msgid "Scopes"
|
||||
msgstr "连接范围"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:85
|
||||
#: settings/serializers/auth/oidc.py:90
|
||||
msgid "Id token max age"
|
||||
msgstr "令牌有效时间"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:88
|
||||
#: settings/serializers/auth/oidc.py:93
|
||||
msgid "Id token include claims"
|
||||
msgstr "声明"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:90
|
||||
#: settings/serializers/auth/oidc.py:95
|
||||
msgid "Use state"
|
||||
msgstr "使用状态"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:91
|
||||
#: settings/serializers/auth/oidc.py:96
|
||||
msgid "Use nonce"
|
||||
msgstr "临时使用"
|
||||
|
||||
@@ -5053,15 +5061,15 @@ msgstr "线程数"
|
||||
msgid "Boot Time"
|
||||
msgstr "运行时间"
|
||||
|
||||
#: terminal/models/storage.py:29
|
||||
#: terminal/models/storage.py:28
|
||||
msgid "Default storage"
|
||||
msgstr "默认存储"
|
||||
|
||||
#: terminal/models/storage.py:139 terminal/models/terminal.py:109
|
||||
#: terminal/models/storage.py:141 terminal/models/terminal.py:109
|
||||
msgid "Command storage"
|
||||
msgstr "命令存储"
|
||||
|
||||
#: terminal/models/storage.py:199 terminal/models/terminal.py:110
|
||||
#: terminal/models/storage.py:201 terminal/models/terminal.py:110
|
||||
msgid "Replay storage"
|
||||
msgstr "录像存储"
|
||||
|
||||
@@ -5351,19 +5359,19 @@ msgid ""
|
||||
msgstr ""
|
||||
"通过工单创建, 工单标题: {}, 工单申请人: {}, 工单处理人: {}, 工单 ID: {}"
|
||||
|
||||
#: tickets/handlers/base.py:84
|
||||
#: tickets/handlers/base.py:86
|
||||
msgid "Change field"
|
||||
msgstr "变更字段"
|
||||
|
||||
#: tickets/handlers/base.py:84
|
||||
#: tickets/handlers/base.py:86
|
||||
msgid "Before change"
|
||||
msgstr "变更前"
|
||||
|
||||
#: tickets/handlers/base.py:84
|
||||
#: tickets/handlers/base.py:86
|
||||
msgid "After change"
|
||||
msgstr "变更后"
|
||||
|
||||
#: tickets/handlers/base.py:96
|
||||
#: tickets/handlers/base.py:98
|
||||
msgid "{} {} the ticket"
|
||||
msgstr "{} {} 工单"
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ class CommonSettingSerializer(serializers.Serializer):
|
||||
help_text=_('User attr map present how to map OpenID user attr to '
|
||||
'jumpserver, username,name,email is jumpserver attr')
|
||||
)
|
||||
AUTH_OPENID_PKCE = serializers.BooleanField(required=False, label=_('Enable PKCE'))
|
||||
AUTH_OPENID_CODE_CHALLENGE_METHOD = serializers.ChoiceField(
|
||||
default='S256', label=_('Code challenge method'),
|
||||
choices=(('S256', 'HS256'), ('plain', 'Plain'))
|
||||
)
|
||||
|
||||
|
||||
class KeycloakSettingSerializer(CommonSettingSerializer):
|
||||
|
||||
@@ -19,7 +19,6 @@ from .terminal import Terminal
|
||||
from .command import Command
|
||||
from .. import const
|
||||
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
@@ -37,7 +36,7 @@ class CommonStorageModelMixin(models.Model):
|
||||
|
||||
def set_to_default(self):
|
||||
self.is_default = True
|
||||
self.save()
|
||||
self.save(update_fields=['is_default'])
|
||||
self.__class__.objects.select_for_update() \
|
||||
.filter(is_default=True) \
|
||||
.exclude(id=self.id) \
|
||||
@@ -128,7 +127,10 @@ class CommandStorage(CommonStorageModelMixin, CommonModelMixin):
|
||||
|
||||
def save(self, force_insert=False, force_update=False, using=None,
|
||||
update_fields=None):
|
||||
super().save()
|
||||
super().save(
|
||||
force_insert=force_insert, force_update=force_update,
|
||||
using=using, update_fields=update_fields
|
||||
)
|
||||
|
||||
if self.type in TYPE_ENGINE_MAPPING:
|
||||
engine_mod = import_module(TYPE_ENGINE_MAPPING[self.type])
|
||||
|
||||
@@ -16,10 +16,10 @@ logger = get_logger(__file__)
|
||||
|
||||
|
||||
@receiver(django_ready)
|
||||
def init_db_port_mapper(sender, **kwargs):
|
||||
def check_db_port_mapper(sender, **kwargs):
|
||||
logger.info('Init db port mapper')
|
||||
try:
|
||||
db_port_manager.init()
|
||||
db_port_manager.check()
|
||||
except (ProgrammingError,) as e:
|
||||
pass
|
||||
|
||||
|
||||
@@ -35,9 +35,22 @@ class DBPortManager(object):
|
||||
def magnus_listen_port_range(self):
|
||||
return settings.MAGNUS_PORTS
|
||||
|
||||
def init(self):
|
||||
@staticmethod
|
||||
def fetch_dbs():
|
||||
with tmp_to_root_org():
|
||||
db_ids = Application.objects.filter(category=AppCategory.db).values_list('id', flat=True)
|
||||
dbs = Application.objects.filter(category=AppCategory.db)
|
||||
return dbs
|
||||
|
||||
def check(self):
|
||||
dbs = self.fetch_dbs()
|
||||
for db in dbs:
|
||||
port = self.get_port_by_db(db, raise_exception=False)
|
||||
if not port:
|
||||
self.add(db)
|
||||
|
||||
def init(self):
|
||||
dbs = self.fetch_dbs()
|
||||
db_ids = dbs.values_list('id', flat=True)
|
||||
db_ids = [str(i) for i in db_ids]
|
||||
mapper = dict(zip(self.all_available_ports, list(db_ids)))
|
||||
self.set_mapper(mapper)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from html import escape
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
@@ -96,11 +98,19 @@ 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(
|
||||
render_to_string('tickets/ticket_approve_diff.html', context)
|
||||
)
|
||||
data = {
|
||||
'body': render_to_string('tickets/ticket_approve_diff.html', context),
|
||||
'body': body,
|
||||
'user': user,
|
||||
'user_display': str(user),
|
||||
'type': 'state',
|
||||
'state': state
|
||||
}
|
||||
return self.ticket.comments.create(**data)
|
||||
|
||||
@staticmethod
|
||||
def reject_html_script(unsafe_html):
|
||||
safe_html = escape(unsafe_html)
|
||||
return safe_html
|
||||
|
||||
@@ -63,7 +63,7 @@ jsonfield2==4.0.0.post0
|
||||
geoip2==4.5.0
|
||||
ipip-ipdb==1.6.1
|
||||
# Django environment
|
||||
Django==3.2.16
|
||||
Django==3.2.15
|
||||
django-bootstrap3==14.2.0
|
||||
django-filter==2.4.0
|
||||
django-formtools==2.2
|
||||
|
||||
@@ -39,6 +39,11 @@ if REDIS_SENTINEL_SERVICE_NAME and REDIS_SENTINELS:
|
||||
connection_params['sentinels'] = REDIS_SENTINELS
|
||||
sentinel_client = Sentinel(
|
||||
**connection_params, sentinel_kwargs={
|
||||
'ssl': settings.REDIS_USE_SSL,
|
||||
'ssl_cert_reqs': settings.REDIS_SSL_REQUIRED,
|
||||
'ssl_keyfile': settings.REDIS_SSL_KEY,
|
||||
'ssl_certfile': settings.REDIS_SSL_CERT,
|
||||
'ssl_ca_certs': settings.REDIS_SSL_CA,
|
||||
'password': REDIS_SENTINEL_PASSWORD,
|
||||
'socket_timeout': REDIS_SENTINEL_SOCKET_TIMEOUT
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user