mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-12-16 17:12:53 +00:00
Compare commits
55 Commits
pr@dev@ter
...
v2.28.16
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f78acef13 | ||
|
|
5da6a1b9b6 | ||
|
|
988f33634e | ||
|
|
a645dc09ae | ||
|
|
4e4e58480f | ||
|
|
4a1f3ed727 | ||
|
|
a10bb29a1e | ||
|
|
73aeb021cc | ||
|
|
3d66fe4758 | ||
|
|
ec2071a6ca | ||
|
|
beb43aa726 | ||
|
|
8a77a7b8b5 | ||
|
|
7eed182627 | ||
|
|
ec847d3ecb | ||
|
|
a0994e2e12 | ||
|
|
17e3ddda05 | ||
|
|
6e2e92be5e | ||
|
|
e90d8c8561 | ||
|
|
cf972942fa | ||
|
|
72e35d5553 | ||
|
|
0ba84e7e18 | ||
|
|
fbc5ae1b9b | ||
|
|
2fcf045826 | ||
|
|
32cba4f2a1 | ||
|
|
b76aa3b259 | ||
|
|
3f9a17347d | ||
|
|
c01d1973d9 | ||
|
|
b216a9abc0 | ||
|
|
c628ba1c4b | ||
|
|
ebbae36c49 | ||
|
|
69ef25666e | ||
|
|
d0475397d0 | ||
|
|
dad45e7ace | ||
|
|
720f9cd397 | ||
|
|
81dee0c403 | ||
|
|
105ef791b8 | ||
|
|
a19c0bde60 | ||
|
|
3996daf4a7 | ||
|
|
ac235f788e | ||
|
|
67e334bf43 | ||
|
|
f7f9fb1bdf | ||
|
|
8979228e0b | ||
|
|
024beca690 | ||
|
|
5c0359e394 | ||
|
|
4ce4bde368 | ||
|
|
809bad271a | ||
|
|
d3bfc03849 | ||
|
|
04c0121b37 | ||
|
|
b97b50ab31 | ||
|
|
d8a8c8153b | ||
|
|
a68ad7be68 | ||
|
|
4041f1aeec | ||
|
|
59388655ea | ||
|
|
ef7463c588 | ||
|
|
7e7d6d94e6 |
2
.github/workflows/release-drafter.yml
vendored
2
.github/workflows/release-drafter.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
TAG=$(basename ${GITHUB_REF})
|
TAG=$(basename ${GITHUB_REF})
|
||||||
VERSION=${TAG/v/}
|
VERSION=${TAG/v/}
|
||||||
wget https://raw.githubusercontent.com/jumpserver/installer/master/quick_start.sh
|
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=TAG::$TAG"
|
||||||
echo "::set-output name=VERSION::$VERSION"
|
echo "::set-output name=VERSION::$VERSION"
|
||||||
- name: Create Release
|
- 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 TARGETARCH
|
||||||
|
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
@@ -8,7 +8,7 @@ WORKDIR /opt/jumpserver
|
|||||||
ADD . .
|
ADD . .
|
||||||
RUN cd utils && bash -ixeu build.sh
|
RUN cd utils && bash -ixeu build.sh
|
||||||
|
|
||||||
FROM python:3.8-slim
|
FROM python:3.8-slim-bullseye
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
MAINTAINER JumpServer Team <ibuler@qq.com>
|
MAINTAINER JumpServer Team <ibuler@qq.com>
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ ARG TOOLS=" \
|
|||||||
default-mysql-client \
|
default-mysql-client \
|
||||||
iputils-ping \
|
iputils-ping \
|
||||||
locales \
|
locales \
|
||||||
netcat \
|
procps \
|
||||||
redis-server \
|
redis-tools \
|
||||||
telnet \
|
telnet \
|
||||||
vim \
|
vim \
|
||||||
unzip \
|
unzip \
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from common.drf.serializers import BulkModelSerializer
|
from common.drf.serializers import BulkModelSerializer
|
||||||
from common.drf.serializers import MethodSerializer
|
from common.drf.serializers import MethodSerializer
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from urllib3.exceptions import MaxRetryError
|
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
from kubernetes import client
|
||||||
from kubernetes.client import api_client
|
from kubernetes.client import api_client
|
||||||
from kubernetes.client.api import core_v1_api
|
from kubernetes.client.api import core_v1_api
|
||||||
from kubernetes import client
|
|
||||||
from kubernetes.client.exceptions import ApiException
|
|
||||||
|
|
||||||
from rest_framework.generics import get_object_or_404
|
from rest_framework.generics import get_object_or_404
|
||||||
|
|
||||||
from common.utils import get_logger
|
|
||||||
from common.tree import TreeNode
|
|
||||||
from assets.models import SystemUser
|
from assets.models import SystemUser
|
||||||
|
from common.tree import TreeNode
|
||||||
|
from common.utils import get_logger
|
||||||
from .. import const
|
from .. import const
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
@@ -23,7 +19,8 @@ class KubernetesClient:
|
|||||||
self.url = url
|
self.url = url
|
||||||
self.token = token
|
self.token = token
|
||||||
|
|
||||||
def get_api(self):
|
@property
|
||||||
|
def api(self):
|
||||||
configuration = client.Configuration()
|
configuration = client.Configuration()
|
||||||
configuration.host = self.url
|
configuration.host = self.url
|
||||||
configuration.verify_ssl = False
|
configuration.verify_ssl = False
|
||||||
@@ -32,63 +29,46 @@ class KubernetesClient:
|
|||||||
api = core_v1_api.CoreV1Api(c)
|
api = core_v1_api.CoreV1Api(c)
|
||||||
return api
|
return api
|
||||||
|
|
||||||
def get_namespace_list(self):
|
def get_namespaces(self):
|
||||||
api = self.get_api()
|
namespaces = []
|
||||||
namespace_list = []
|
resp = self.api.list_namespace()
|
||||||
for ns in api.list_namespace().items:
|
for ns in resp.items:
|
||||||
namespace_list.append(ns.metadata.name)
|
namespaces.append(ns.metadata.name)
|
||||||
return namespace_list
|
return namespaces
|
||||||
|
|
||||||
def get_services(self):
|
def get_pods(self, namespace):
|
||||||
api = self.get_api()
|
pods = []
|
||||||
ret = api.list_service_for_all_namespaces(watch=False)
|
resp = self.api.list_namespaced_pod(namespace)
|
||||||
for i in ret.items:
|
for pd in resp.items:
|
||||||
print("%s \t%s \t%s \t%s \t%s \n" % (
|
pods.append(pd.metadata.name)
|
||||||
i.kind, i.metadata.namespace, i.metadata.name, i.spec.cluster_ip, i.spec.ports))
|
return pods
|
||||||
|
|
||||||
def get_pod_info(self, namespace, pod):
|
def get_containers(self, namespace, pod_name):
|
||||||
api = self.get_api()
|
containers = []
|
||||||
resp = api.read_namespaced_pod(namespace=namespace, name=pod)
|
resp = self.api.read_namespaced_pod(pod_name, namespace)
|
||||||
return resp
|
for container in resp.spec.containers:
|
||||||
|
containers.append(container.name)
|
||||||
|
return containers
|
||||||
|
|
||||||
def get_pod_logs(self, namespace, pod):
|
@classmethod
|
||||||
api = self.get_api()
|
def run(cls, asset, secret, tp='namespace'):
|
||||||
log_content = api.read_namespaced_pod_log(pod, namespace, pretty=True, tail_lines=200)
|
k8s_url = f'{asset.address}'
|
||||||
return log_content
|
k8s = cls(k8s_url, secret)
|
||||||
|
func_name = f'get_{tp}s'
|
||||||
|
if hasattr(k8s, func_name):
|
||||||
|
return getattr(k8s, func_name)()
|
||||||
|
return []
|
||||||
|
|
||||||
def get_pods(self):
|
@classmethod
|
||||||
api = self.get_api()
|
def get_kubernetes_data(cls, app_id, system_user_id, tp, *args):
|
||||||
try:
|
|
||||||
ret = api.list_pod_for_all_namespaces(watch=False, _request_timeout=(3, 3))
|
|
||||||
except MaxRetryError:
|
|
||||||
logger.warning('Kubernetes connection timed out')
|
|
||||||
return
|
|
||||||
except ApiException as e:
|
|
||||||
if e.status == 401:
|
|
||||||
logger.warning('Kubernetes User not authenticated')
|
|
||||||
else:
|
|
||||||
logger.warning(e)
|
|
||||||
return
|
|
||||||
data = {}
|
|
||||||
for i in ret.items:
|
|
||||||
namespace = i.metadata.namespace
|
|
||||||
pod_info = {
|
|
||||||
'pod_name': i.metadata.name,
|
|
||||||
'containers': [j.name for j in i.spec.containers]
|
|
||||||
}
|
|
||||||
if namespace in data:
|
|
||||||
data[namespace].append(pod_info)
|
|
||||||
else:
|
|
||||||
data[namespace] = [pod_info, ]
|
|
||||||
return data
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_kubernetes_data(app_id, system_user_id):
|
|
||||||
from ..models import Application
|
from ..models import Application
|
||||||
app = get_object_or_404(Application, id=app_id)
|
app = get_object_or_404(Application, id=app_id)
|
||||||
system_user = get_object_or_404(SystemUser, id=system_user_id)
|
system_user = get_object_or_404(SystemUser, id=system_user_id)
|
||||||
k8s = KubernetesClient(app.attrs['cluster'], system_user.token)
|
k8s = cls(app.attrs['cluster'], system_user.token)
|
||||||
return k8s.get_pods()
|
func_name = f'get_{tp}s'
|
||||||
|
if hasattr(k8s, func_name):
|
||||||
|
return getattr(k8s, func_name)(*args)
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
class KubernetesTree:
|
class KubernetesTree:
|
||||||
@@ -118,11 +98,10 @@ class KubernetesTree:
|
|||||||
)
|
)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def as_namespace_pod_tree_node(self, name, meta, type, counts=0, is_container=False):
|
def as_namespace_pod_tree_node(self, name, meta, type, is_container=False):
|
||||||
from ..models import ApplicationTreeNodeMixin
|
from ..models import ApplicationTreeNodeMixin
|
||||||
i = ApplicationTreeNodeMixin.create_tree_id(self.tree_id, type, name)
|
i = ApplicationTreeNodeMixin.create_tree_id(self.tree_id, type, name)
|
||||||
meta.update({type: name})
|
meta.update({type: name})
|
||||||
name = name if is_container else f'{name}({counts})'
|
|
||||||
node = self.create_tree_node(
|
node = self.create_tree_node(
|
||||||
i, self.tree_id, name, type, meta, icon='cloud', is_container=is_container
|
i, self.tree_id, name, type, meta, icon='cloud', is_container=is_container
|
||||||
)
|
)
|
||||||
@@ -157,30 +136,30 @@ class KubernetesTree:
|
|||||||
system_user_id = parent_info.get('system_user_id')
|
system_user_id = parent_info.get('system_user_id')
|
||||||
|
|
||||||
tree_nodes = []
|
tree_nodes = []
|
||||||
data = KubernetesClient.get_kubernetes_data(app_id, system_user_id)
|
|
||||||
if not data:
|
|
||||||
return tree_nodes
|
|
||||||
|
|
||||||
if pod_name:
|
if pod_name:
|
||||||
for container in next(
|
tp = 'container'
|
||||||
filter(
|
containers = KubernetesClient.get_kubernetes_data(
|
||||||
lambda x: x['pod_name'] == pod_name, data[namespace]
|
app_id, system_user_id, tp, namespace, pod_name
|
||||||
)
|
)
|
||||||
)['containers']:
|
for container in containers:
|
||||||
container_node = self.as_namespace_pod_tree_node(
|
container_node = self.as_namespace_pod_tree_node(
|
||||||
container, parent_info, 'container', is_container=True
|
container, parent_info, tp, is_container=True
|
||||||
)
|
)
|
||||||
tree_nodes.append(container_node)
|
tree_nodes.append(container_node)
|
||||||
elif namespace:
|
elif namespace:
|
||||||
for pod in data[namespace]:
|
tp = 'pod'
|
||||||
pod_nodes = self.as_namespace_pod_tree_node(
|
pods = KubernetesClient.get_kubernetes_data(app_id, system_user_id, tp, namespace)
|
||||||
pod['pod_name'], parent_info, 'pod', len(pod['containers'])
|
for pod in pods:
|
||||||
|
pod_node = self.as_namespace_pod_tree_node(
|
||||||
|
pod, parent_info, tp
|
||||||
)
|
)
|
||||||
tree_nodes.append(pod_nodes)
|
tree_nodes.append(pod_node)
|
||||||
elif system_user_id:
|
elif system_user_id:
|
||||||
for namespace, pods in data.items():
|
tp = 'namespace'
|
||||||
|
namespaces = KubernetesClient.get_kubernetes_data(app_id, system_user_id, tp)
|
||||||
|
for namespace in namespaces:
|
||||||
namespace_node = self.as_namespace_pod_tree_node(
|
namespace_node = self.as_namespace_pod_tree_node(
|
||||||
namespace, parent_info, 'namespace', len(pods)
|
namespace, parent_info, tp
|
||||||
)
|
)
|
||||||
tree_nodes.append(namespace_node)
|
tree_nodes.append(namespace_node)
|
||||||
return tree_nodes
|
return tree_nodes
|
||||||
|
|||||||
@@ -2,9 +2,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
import uuid
|
|
||||||
import logging
|
import logging
|
||||||
from functools import reduce
|
import uuid
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
@@ -14,7 +13,6 @@ from rest_framework.exceptions import ValidationError
|
|||||||
from common.db.fields import JsonDictTextField
|
from common.db.fields import JsonDictTextField
|
||||||
from common.utils import lazyproperty
|
from common.utils import lazyproperty
|
||||||
from orgs.mixins.models import OrgModelMixin, OrgManager
|
from orgs.mixins.models import OrgModelMixin, OrgManager
|
||||||
|
|
||||||
from .base import AbsConnectivity
|
from .base import AbsConnectivity
|
||||||
|
|
||||||
__all__ = ['Asset', 'ProtocolsMixin', 'Platform', 'AssetQuerySet']
|
__all__ = ['Asset', 'ProtocolsMixin', 'Platform', 'AssetQuerySet']
|
||||||
|
|||||||
@@ -1,28 +1,27 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
import io
|
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
|
|
||||||
import sshpubkeys
|
import sshpubkeys
|
||||||
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import QuerySet
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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 (
|
from common.utils import (
|
||||||
ssh_key_string_to_obj, ssh_key_gen, get_logger, lazyproperty
|
ssh_key_string_to_obj, ssh_key_gen, get_logger, lazyproperty
|
||||||
)
|
)
|
||||||
from common.utils.encode import ssh_pubkey_gen
|
from common.utils.encode import (
|
||||||
from common.validators import alphanumeric
|
parse_ssh_public_key_str, parse_ssh_private_key_str
|
||||||
from common.db import fields
|
)
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
@@ -64,16 +63,16 @@ class AuthMixin:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def ssh_key_fingerprint(self):
|
def ssh_key_fingerprint(self):
|
||||||
|
public_key = None
|
||||||
if self.public_key:
|
if self.public_key:
|
||||||
public_key = self.public_key
|
public_key = self.public_key
|
||||||
elif self.private_key:
|
elif self.private_key:
|
||||||
try:
|
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:
|
except IOError as e:
|
||||||
return str(e)
|
return str(e)
|
||||||
else:
|
if not public_key:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
public_key_obj = sshpubkeys.SSHKey(public_key)
|
public_key_obj = sshpubkeys.SSHKey(public_key)
|
||||||
fingerprint = public_key_obj.hash_md5()
|
fingerprint = public_key_obj.hash_md5()
|
||||||
return fingerprint
|
return fingerprint
|
||||||
@@ -88,24 +87,29 @@ class AuthMixin:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def private_key_file(self):
|
def private_key_file(self):
|
||||||
if not self.private_key_obj:
|
if not self.private_key:
|
||||||
|
return None
|
||||||
|
private_key_str = self.get_private_key()
|
||||||
|
if not private_key_str:
|
||||||
return None
|
return None
|
||||||
project_dir = settings.PROJECT_DIR
|
project_dir = settings.PROJECT_DIR
|
||||||
tmp_dir = os.path.join(project_dir, 'tmp')
|
tmp_dir = os.path.join(project_dir, 'tmp')
|
||||||
key_name = '.' + md5(self.private_key.encode('utf-8')).hexdigest()
|
key_name = '.' + md5(self.private_key.encode('utf-8')).hexdigest()
|
||||||
key_path = os.path.join(tmp_dir, key_name)
|
key_path = os.path.join(tmp_dir, key_name)
|
||||||
if not os.path.exists(key_path):
|
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)
|
os.chmod(key_path, 0o400)
|
||||||
return key_path
|
return key_path
|
||||||
|
|
||||||
def get_private_key(self):
|
def get_private_key(self):
|
||||||
if not self.private_key_obj:
|
if not self.private_key:
|
||||||
return None
|
return None
|
||||||
string_io = io.StringIO()
|
private_key_str = parse_ssh_private_key_str(self.private_key, password=self.password)
|
||||||
self.private_key_obj.write_private_key(string_io)
|
if not private_key_str and self.password:
|
||||||
private_key = string_io.getvalue()
|
# 由于历史原因,密码可能是真实的密码,而非私钥的 passphrase,所以这里再尝试一次
|
||||||
return private_key
|
private_key_str = parse_ssh_private_key_str(self.private_key)
|
||||||
|
return private_key_str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def public_key_obj(self):
|
def public_key_obj(self):
|
||||||
@@ -234,4 +238,3 @@ class BaseUser(OrgModelMixin, AuthMixin):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|||||||
@@ -4,16 +4,16 @@
|
|||||||
|
|
||||||
import logging
|
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.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 common.utils import signer, get_object_or_none, is_uuid
|
||||||
from .base import BaseUser
|
|
||||||
from .asset import Asset
|
from .asset import Asset
|
||||||
from .authbook import AuthBook
|
from .authbook import AuthBook
|
||||||
|
from .base import BaseUser
|
||||||
|
|
||||||
__all__ = ['AdminUser', 'SystemUser']
|
__all__ = ['AdminUser', 'SystemUser']
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -187,22 +187,22 @@ class AuthMixin:
|
|||||||
if username == '':
|
if username == '':
|
||||||
username = self.username
|
username = self.username
|
||||||
|
|
||||||
authbook = AuthBook.objects.filter(
|
not_stu_query = Q(asset=asset, username=username, systemuser__isnull=True)
|
||||||
asset=asset, username=username, systemuser__isnull=True
|
stu_query = Q(asset=asset, systemuser=self)
|
||||||
).order_by('-date_created').first()
|
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:
|
if not authbook:
|
||||||
authbook = AuthBook.objects.filter(
|
authbook = stu_qs.first()
|
||||||
asset=asset, systemuser=self
|
|
||||||
).order_by('-date_created').first()
|
|
||||||
|
|
||||||
if not authbook:
|
if not authbook:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
authbook.load_auth()
|
authbook.load_auth()
|
||||||
self.password = authbook.password
|
|
||||||
self.private_key = authbook.private_key
|
self.password = authbook.password or self.password or ''
|
||||||
self.public_key = authbook.public_key
|
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):
|
def load_asset_more_auth(self, asset_id=None, username=None, user_id=None):
|
||||||
from users.models import User
|
from users.models import User
|
||||||
@@ -249,12 +249,19 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
|
|||||||
users = models.ManyToManyField('users.User', blank=True, verbose_name=_("Users"))
|
users = models.ManyToManyField('users.User', blank=True, verbose_name=_("Users"))
|
||||||
groups = models.ManyToManyField('users.UserGroup', blank=True, verbose_name=_("User groups"))
|
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'))
|
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)])
|
priority = models.IntegerField(
|
||||||
protocol = models.CharField(max_length=16, choices=ProtocolMixin.Protocol.choices, default='ssh', verbose_name=_('Protocol'))
|
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'))
|
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
|
||||||
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
|
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
|
||||||
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
|
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'))
|
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"))
|
sftp_root = models.CharField(default='tmp', max_length=128, verbose_name=_("SFTP Root"))
|
||||||
token = models.TextField(default='', verbose_name=_('Token'))
|
token = models.TextField(default='', verbose_name=_('Token'))
|
||||||
home = models.CharField(max_length=4096, default='', verbose_name=_('Home'), blank=True)
|
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)
|
ad_domain = models.CharField(default='', max_length=256)
|
||||||
# linux su 命令 (switch user)
|
# linux su 命令 (switch user)
|
||||||
su_enabled = models.BooleanField(default=False, verbose_name=_('User switch'))
|
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):
|
def __str__(self):
|
||||||
username = self.username
|
username = self.username
|
||||||
@@ -322,9 +331,20 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
|
|||||||
assets = Asset.objects.filter(id__in=asset_ids)
|
assets = Asset.objects.filter(id__in=asset_ids)
|
||||||
return assets
|
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):
|
def add_related_assets(self, assets_or_ids):
|
||||||
self.assets.add(*tuple(assets_or_ids))
|
assets = self.filter_contain_protocol_assets(assets_or_ids)
|
||||||
self.add_related_assets_to_su_from_if_need(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):
|
def add_related_assets_to_su_from_if_need(self, assets_or_ids):
|
||||||
if self.protocol not in [self.Protocol.ssh.value]:
|
if self.protocol not in [self.Protocol.ssh.value]:
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
from io import StringIO
|
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
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 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
|
from .utils import validate_password_for_ansible
|
||||||
|
|
||||||
|
|
||||||
class AuthSerializer(serializers.ModelSerializer):
|
class AuthSerializer(serializers.ModelSerializer):
|
||||||
password = EncryptedField(required=False, allow_blank=True, allow_null=True, max_length=1024, label=_('Password'))
|
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):
|
def gen_keys(self, private_key=None, password=None):
|
||||||
if private_key is None:
|
if private_key is None:
|
||||||
return None, 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
|
return private_key, public_key
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
@@ -57,10 +57,7 @@ class AuthSerializerMixin(serializers.ModelSerializer):
|
|||||||
if not valid:
|
if not valid:
|
||||||
raise serializers.ValidationError(_("private key invalid or passphrase error"))
|
raise serializers.ValidationError(_("private key invalid or passphrase error"))
|
||||||
|
|
||||||
private_key = ssh_private_key_gen(private_key, password=passphrase)
|
private_key = parse_ssh_private_key_str(private_key, password=passphrase)
|
||||||
string_io = StringIO()
|
|
||||||
private_key.write_private_key(string_io)
|
|
||||||
private_key = string_io.getvalue()
|
|
||||||
return private_key
|
return private_key
|
||||||
|
|
||||||
def validate_public_key(self, public_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.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.fields import EncryptedField
|
||||||
from common.drf.serializers import SecretReadableMixin
|
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 common.validators import alphanumeric_re, alphanumeric_cn_re, alphanumeric_win_re
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
from ..models import SystemUser, Asset
|
|
||||||
from .utils import validate_password_for_ansible
|
|
||||||
from .base import AuthSerializerMixin
|
from .base import AuthSerializerMixin
|
||||||
|
from .utils import validate_password_for_ansible
|
||||||
|
from ..models import SystemUser, Asset
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'SystemUserSerializer', 'MiniSystemUserSerializer',
|
'SystemUserSerializer', 'MiniSystemUserSerializer',
|
||||||
@@ -36,9 +36,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
|||||||
token = EncryptedField(
|
token = EncryptedField(
|
||||||
label=_('Token'), required=False, write_only=True, style={'base_template': 'textarea.html'}
|
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:
|
class Meta:
|
||||||
model = SystemUser
|
model = SystemUser
|
||||||
@@ -53,7 +50,7 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
|||||||
'su_enabled', 'su_from',
|
'su_enabled', 'su_from',
|
||||||
'date_created', 'date_updated', 'comment', 'created_by',
|
'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
|
fields = fields_small + fields_m2m
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'cmd_filters': {"required": False, 'label': _('Command filter')},
|
'cmd_filters': {"required": False, 'label': _('Command filter')},
|
||||||
@@ -214,7 +211,7 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
|||||||
elif attrs.get('private_key'):
|
elif attrs.get('private_key'):
|
||||||
private_key = attrs['private_key']
|
private_key = attrs['private_key']
|
||||||
password = attrs.get('password')
|
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
|
attrs['public_key'] = public_key
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
@@ -241,7 +238,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
|||||||
def setup_eager_loading(cls, queryset):
|
def setup_eager_loading(cls, queryset):
|
||||||
""" Perform necessary eager loading of data. """
|
""" Perform necessary eager loading of data. """
|
||||||
queryset = queryset \
|
queryset = queryset \
|
||||||
.annotate(assets_amount=Count("assets")) \
|
|
||||||
.prefetch_related('nodes', 'cmd_filters')
|
.prefetch_related('nodes', 'cmd_filters')
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from common.utils import validate_ssh_private_key, parse_ssh_private_key_str
|
||||||
|
|
||||||
|
|
||||||
def validate_password_for_ansible(password):
|
def validate_password_for_ansible(password):
|
||||||
""" 校验 Ansible 不支持的特殊字符 """
|
""" 校验 Ansible 不支持的特殊字符 """
|
||||||
@@ -15,3 +17,9 @@ def validate_password_for_ansible(password):
|
|||||||
if '"' in password:
|
if '"' in password:
|
||||||
raise serializers.ValidationError(_('Password can not contains `"` '))
|
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)
|
||||||
|
|||||||
@@ -50,6 +50,10 @@ def clean_ansible_task_hosts(assets, system_user=None):
|
|||||||
for asset in assets:
|
for asset in assets:
|
||||||
if not check_asset_can_run_ansible(asset):
|
if not check_asset_can_run_ansible(asset):
|
||||||
continue
|
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)
|
cleaned_assets.append(asset)
|
||||||
if not cleaned_assets:
|
if not cleaned_assets:
|
||||||
logger.info(_("No assets matched, stop task"))
|
logger.info(_("No assets matched, stop task"))
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ import csv
|
|||||||
import codecs
|
import codecs
|
||||||
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from settings.serializers import SettingsSerializer
|
from settings.serializers import SettingsSerializer
|
||||||
|
from common.utils.timezone import as_current_tz
|
||||||
from common.utils import validate_ip, get_ip_city, get_logger
|
from common.utils import validate_ip, get_ip_city, get_logger
|
||||||
from common.db import fields
|
|
||||||
from .const import DEFAULT_CITY
|
from .const import DEFAULT_CITY
|
||||||
|
|
||||||
|
|
||||||
@@ -78,6 +79,8 @@ def model_to_dict_for_operate_log(
|
|||||||
f.verbose_name = 'id'
|
f.verbose_name = 'id'
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
value = [str(v) for v in value]
|
value = [str(v) for v in value]
|
||||||
|
elif isinstance(value, datetime):
|
||||||
|
value = as_current_tz(value).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
if include_model_fields or getattr(f, 'primary_key', False):
|
if include_model_fields or getattr(f, 'primary_key', False):
|
||||||
data[str(f.verbose_name)] = value
|
data[str(f.verbose_name)] = value
|
||||||
|
|||||||
@@ -1,26 +1,27 @@
|
|||||||
import abc
|
import abc
|
||||||
import os
|
|
||||||
import json
|
|
||||||
import base64
|
import base64
|
||||||
|
import json
|
||||||
|
import os
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import get_object_or_404
|
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 import status
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from common.drf.api import JMSModelViewSet
|
from common.drf.api import JMSModelViewSet
|
||||||
from common.http import is_true
|
from common.http import is_true
|
||||||
from orgs.mixins.api import RootOrgViewMixin
|
from orgs.mixins.api import RootOrgViewMixin
|
||||||
from perms.models.base import Action
|
from perms.models.base import Action
|
||||||
from terminal.models import EndpointRule
|
from terminal.models import EndpointRule
|
||||||
|
from ..models import ConnectionToken
|
||||||
from ..serializers import (
|
from ..serializers import (
|
||||||
ConnectionTokenSerializer, ConnectionTokenSecretSerializer,
|
ConnectionTokenSerializer, ConnectionTokenSecretSerializer,
|
||||||
SuperConnectionTokenSerializer, ConnectionTokenDisplaySerializer,
|
SuperConnectionTokenSerializer, ConnectionTokenDisplaySerializer,
|
||||||
)
|
)
|
||||||
from ..models import ConnectionToken
|
|
||||||
|
|
||||||
__all__ = ['ConnectionTokenViewSet', 'SuperConnectionTokenViewSet']
|
__all__ = ['ConnectionTokenViewSet', 'SuperConnectionTokenViewSet']
|
||||||
|
|
||||||
@@ -165,6 +166,9 @@ class ConnectionTokenMixin:
|
|||||||
rdp_options['session bpp:i'] = os.getenv('JUMPSERVER_COLOR_DEPTH', '32')
|
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')
|
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:
|
if token.asset:
|
||||||
name = token.asset.hostname
|
name = token.asset.hostname
|
||||||
elif token.application and token.application.category_remote_app:
|
elif token.application and token.application.category_remote_app:
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class TicketStatusApi(mixins.AuthMixin, APIView):
|
|||||||
try:
|
try:
|
||||||
self.check_user_login_confirm()
|
self.check_user_login_confirm()
|
||||||
self.request.session['auth_third_party_done'] = 1
|
self.request.session['auth_third_party_done'] = 1
|
||||||
|
self.request.session.pop('auth_third_party_required', '')
|
||||||
return Response({"msg": "ok"})
|
return Response({"msg": "ok"})
|
||||||
except errors.LoginConfirmOtherError as e:
|
except errors.LoginConfirmOtherError as e:
|
||||||
reason = e.msg
|
reason = e.msg
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#
|
#
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.utils.http import urlencode
|
from django.utils.http import urlencode
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -90,8 +91,12 @@ class OAuth2Backend(JMSModelBackend):
|
|||||||
request, path=reverse(settings.AUTH_OAUTH2_AUTH_LOGIN_CALLBACK_URL_NAME)
|
request, path=reverse(settings.AUTH_OAUTH2_AUTH_LOGIN_CALLBACK_URL_NAME)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
access_token_url = '{url}?{query}'.format(
|
if '?' in settings.AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT:
|
||||||
url=settings.AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT, query=urlencode(query_dict)
|
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()
|
token_method = settings.AUTH_OAUTH2_ACCESS_TOKEN_METHOD.lower()
|
||||||
requests_func = getattr(requests, token_method, requests.get)
|
requests_func = getattr(requests, token_method, requests.get)
|
||||||
@@ -114,12 +119,16 @@ class OAuth2Backend(JMSModelBackend):
|
|||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'Accept': 'application/json',
|
'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'))
|
logger.debug(log_prompt.format('Get userinfo endpoint'))
|
||||||
userinfo_url = '{url}?{query}'.format(
|
if '?' in settings.AUTH_OAUTH2_PROVIDER_USERINFO_ENDPOINT:
|
||||||
url=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)
|
query=urlencode(query_dict)
|
||||||
)
|
)
|
||||||
userinfo_response = requests.get(userinfo_url, headers=headers)
|
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,
|
url=settings.AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT,
|
||||||
|
separator=separator,
|
||||||
query=urlencode(query_dict)
|
query=urlencode(query_dict)
|
||||||
)
|
)
|
||||||
logger.debug(log_prompt.format('Redirect login url'))
|
logger.debug(log_prompt.format('Redirect login url'))
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class OIDCAuthCodeBackend(OIDCBaseBackend):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@ssl_verification
|
@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. """
|
""" Authenticates users in case of the OpenID Connect Authorization code flow. """
|
||||||
log_prompt = "Process authenticate [OIDCAuthCodeBackend]: {}"
|
log_prompt = "Process authenticate [OIDCAuthCodeBackend]: {}"
|
||||||
logger.debug(log_prompt.format('start'))
|
logger.debug(log_prompt.format('start'))
|
||||||
@@ -134,6 +134,8 @@ class OIDCAuthCodeBackend(OIDCBaseBackend):
|
|||||||
request, path=reverse(settings.AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME)
|
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':
|
if settings.AUTH_OPENID_CLIENT_AUTH_METHOD == 'client_secret_post':
|
||||||
token_payload.update({
|
token_payload.update({
|
||||||
'client_id': settings.AUTH_OPENID_CLIENT_ID,
|
'client_id': settings.AUTH_OPENID_CLIENT_ID,
|
||||||
|
|||||||
@@ -9,7 +9,10 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
import time
|
import time
|
||||||
|
import secrets
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
@@ -38,6 +41,19 @@ class OIDCAuthRequestView(View):
|
|||||||
|
|
||||||
http_method_names = ['get', ]
|
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):
|
def get(self, request):
|
||||||
""" Processes GET requests. """
|
""" 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
|
# States should be used! They are recommended in order to maintain state between the
|
||||||
# authentication request and the callback.
|
# authentication request and the callback.
|
||||||
if settings.AUTH_OPENID_USE_STATE:
|
if settings.AUTH_OPENID_USE_STATE:
|
||||||
@@ -138,8 +164,9 @@ class OIDCAuthCallbackView(View):
|
|||||||
|
|
||||||
# Authenticates the end-user.
|
# Authenticates the end-user.
|
||||||
next_url = request.session.get('oidc_auth_next_url', None)
|
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'))
|
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:
|
if user and user.is_valid:
|
||||||
logger.debug(log_prompt.format('Login: {}'.format(user)))
|
logger.debug(log_prompt.format('Login: {}'.format(user)))
|
||||||
auth.login(self.request, user)
|
auth.login(self.request, user)
|
||||||
|
|||||||
@@ -61,6 +61,18 @@ class ThirdPartyLoginMiddleware(mixins.AuthMixin):
|
|||||||
return response
|
return response
|
||||||
if not request.session.get('auth_third_party_required'):
|
if not request.session.get('auth_third_party_required'):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
white_urls = [
|
||||||
|
'jsi18n/', '/static/',
|
||||||
|
'login/guard', 'login/wait-confirm',
|
||||||
|
'login-confirm-ticket/status',
|
||||||
|
'settings/public/open',
|
||||||
|
'core/auth/login', 'core/auth/logout'
|
||||||
|
]
|
||||||
|
for url in white_urls:
|
||||||
|
if request.path.find(url) > -1:
|
||||||
|
return response
|
||||||
|
|
||||||
ip = get_request_ip(request)
|
ip = get_request_ip(request)
|
||||||
try:
|
try:
|
||||||
self.request = request
|
self.request = request
|
||||||
@@ -88,7 +100,6 @@ class ThirdPartyLoginMiddleware(mixins.AuthMixin):
|
|||||||
guard_url = "%s?%s" % (guard_url, args)
|
guard_url = "%s?%s" % (guard_url, args)
|
||||||
response = redirect(guard_url)
|
response = redirect(guard_url)
|
||||||
finally:
|
finally:
|
||||||
request.session.pop('auth_third_party_required', '')
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -212,7 +212,8 @@ class MFAMixin:
|
|||||||
self._do_check_user_mfa(code, mfa_type, user=user)
|
self._do_check_user_mfa(code, mfa_type, user=user)
|
||||||
|
|
||||||
def check_user_mfa_if_need(self, 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
|
return
|
||||||
if not user.mfa_enabled:
|
if not user.mfa_enabled:
|
||||||
return
|
return
|
||||||
@@ -220,14 +221,16 @@ class MFAMixin:
|
|||||||
active_mfa_names = user.active_mfa_backends_mapper.keys()
|
active_mfa_names = user.active_mfa_backends_mapper.keys()
|
||||||
raise errors.MFARequiredError(mfa_types=tuple(active_mfa_names))
|
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'] = 1
|
||||||
|
self.request.session['auth_mfa_username'] = user.username
|
||||||
self.request.session['auth_mfa_time'] = time.time()
|
self.request.session['auth_mfa_time'] = time.time()
|
||||||
self.request.session['auth_mfa_required'] = 0
|
self.request.session['auth_mfa_required'] = 0
|
||||||
self.request.session['auth_mfa_type'] = mfa_type
|
self.request.session['auth_mfa_type'] = mfa_type
|
||||||
|
MFABlockUtils(user.username, self.get_request_ip()).clean_failed_count()
|
||||||
|
|
||||||
def clean_mfa_mark(self):
|
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:
|
for k in keys:
|
||||||
self.request.session.pop(k, '')
|
self.request.session.pop(k, '')
|
||||||
|
|
||||||
@@ -262,7 +265,7 @@ class MFAMixin:
|
|||||||
ok, msg = mfa_backend.check_code(code)
|
ok, msg = mfa_backend.check_code(code)
|
||||||
|
|
||||||
if ok:
|
if ok:
|
||||||
self.mark_mfa_ok(mfa_type)
|
self.mark_mfa_ok(mfa_type, user)
|
||||||
return
|
return
|
||||||
|
|
||||||
raise errors.MFAFailedError(
|
raise errors.MFAFailedError(
|
||||||
@@ -369,7 +372,7 @@ class AuthACLMixin:
|
|||||||
def check_user_login_confirm(self):
|
def check_user_login_confirm(self):
|
||||||
ticket = self.get_ticket()
|
ticket = self.get_ticket()
|
||||||
if not ticket:
|
if not ticket:
|
||||||
raise errors.LoginConfirmOtherError('', "Not found")
|
raise errors.LoginConfirmOtherError('', "Not found", '')
|
||||||
elif ticket.is_state(ticket.State.approved):
|
elif ticket.is_state(ticket.State.approved):
|
||||||
self.request.session["auth_confirm_required"] = ''
|
self.request.session["auth_confirm_required"] = ''
|
||||||
return
|
return
|
||||||
@@ -512,4 +515,20 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost
|
|||||||
args = self.request.META.get('QUERY_STRING', '')
|
args = self.request.META.get('QUERY_STRING', '')
|
||||||
if args:
|
if args:
|
||||||
guard_url = "%s?%s" % (guard_url, args)
|
guard_url = "%s?%s" % (guard_url, args)
|
||||||
return redirect(guard_url)
|
response = redirect(guard_url)
|
||||||
|
self.set_browser_default_language_if_need(response)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def set_browser_default_language_if_need(self, response):
|
||||||
|
# en, ja, zh-CN,zh;q=0.9
|
||||||
|
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,
|
'state': state,
|
||||||
'redirect_uri': redirect_uri,
|
'redirect_uri': redirect_uri,
|
||||||
}
|
}
|
||||||
url = URL.AUTHEN + '?' + urlencode(params)
|
url = URL().authen + '?' + urlencode(params)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -32,11 +32,14 @@ class UserLoginMFAView(mixins.AuthMixin, FormView):
|
|||||||
return super().get(*args, **kwargs)
|
return super().get(*args, **kwargs)
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
from users.utils import MFABlockUtils
|
||||||
code = form.cleaned_data.get('code')
|
code = form.cleaned_data.get('code')
|
||||||
mfa_type = form.cleaned_data.get('mfa_type')
|
mfa_type = form.cleaned_data.get('mfa_type')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._do_check_user_mfa(code, mfa_type)
|
self._do_check_user_mfa(code, mfa_type)
|
||||||
|
user, ip = self.get_user_from_session(), self.get_request_ip()
|
||||||
|
MFABlockUtils(user.username, ip).clean_failed_count()
|
||||||
return redirect_to_guard_view('mfa_ok')
|
return redirect_to_guard_view('mfa_ok')
|
||||||
except (errors.MFAFailedError, errors.BlockMFAError) as e:
|
except (errors.MFAFailedError, errors.BlockMFAError) as e:
|
||||||
form.add_error('code', e.msg)
|
form.add_error('code', e.msg)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from werkzeug.local import Local
|
from werkzeug.local import Local
|
||||||
|
|
||||||
thread_local = Local()
|
thread_local = Local()
|
||||||
encrypted_field_set = set()
|
encrypted_field_set = {'password'}
|
||||||
|
|
||||||
|
|
||||||
def _find(attr):
|
def _find(attr):
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ class FlowerService(BaseService):
|
|||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def db_file(self):
|
||||||
|
return os.path.join(BASE_DIR, 'data', 'flower')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cmd(self):
|
def cmd(self):
|
||||||
print("\n- Start Flower as Task Monitor")
|
print("\n- Start Flower as Task Monitor")
|
||||||
@@ -20,11 +24,11 @@ class FlowerService(BaseService):
|
|||||||
'-A', 'ops',
|
'-A', 'ops',
|
||||||
'flower',
|
'flower',
|
||||||
'-logging=info',
|
'-logging=info',
|
||||||
|
'-db={}'.format(self.db_file),
|
||||||
'--url_prefix=/core/flower',
|
'--url_prefix=/core/flower',
|
||||||
'--auto_refresh=False',
|
'--auto_refresh=False',
|
||||||
'--max_tasks=1000',
|
'--max_tasks=1000',
|
||||||
'--persistent=True',
|
'--persistent=True',
|
||||||
'-db=/opt/jumpserver/data/flower.db',
|
|
||||||
'--state_save_interval=600000'
|
'--state_save_interval=600000'
|
||||||
]
|
]
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import json
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework.exceptions import APIException
|
from rest_framework.exceptions import APIException
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from common.utils.common import get_logger
|
from common.utils.common import get_logger
|
||||||
from common.sdk.im.utils import digest
|
from common.sdk.im.utils import digest
|
||||||
from common.sdk.im.mixin import RequestMixin, BaseRequest
|
from common.sdk.im.mixin import RequestMixin, BaseRequest
|
||||||
@@ -11,14 +12,30 @@ logger = get_logger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class URL:
|
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
|
# 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:
|
class ErrorCode:
|
||||||
@@ -51,7 +68,7 @@ class FeishuRequests(BaseRequest):
|
|||||||
|
|
||||||
def request_access_token(self):
|
def request_access_token(self):
|
||||||
data = {'app_id': self._app_id, 'app_secret': self._app_secret}
|
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)
|
self.check_errcode_is_0(response)
|
||||||
|
|
||||||
access_token = response['tenant_access_token']
|
access_token = response['tenant_access_token']
|
||||||
@@ -86,7 +103,7 @@ class FeiShu(RequestMixin):
|
|||||||
'code': code
|
'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)
|
self._requests.check_errcode_is_0(data)
|
||||||
return data['data']['user_id']
|
return data['data']['user_id']
|
||||||
@@ -107,7 +124,7 @@ class FeiShu(RequestMixin):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info(f'Feishu send text: user_ids={user_ids} msg={msg}')
|
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:
|
except APIException as e:
|
||||||
# 只处理可预知的错误
|
# 只处理可预知的错误
|
||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
import re
|
|
||||||
import json
|
|
||||||
from six import string_types
|
|
||||||
import base64
|
import base64
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import time
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from itertools import chain
|
|
||||||
|
|
||||||
import paramiko
|
import paramiko
|
||||||
import sshpubkeys
|
import sshpubkeys
|
||||||
|
from cryptography.hazmat.primitives import serialization
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
from itsdangerous import (
|
from itsdangerous import (
|
||||||
TimedJSONWebSignatureSerializer, JSONWebSignatureSerializer,
|
TimedJSONWebSignatureSerializer, JSONWebSignatureSerializer,
|
||||||
BadSignature, SignatureExpired
|
BadSignature, SignatureExpired
|
||||||
)
|
)
|
||||||
from django.conf import settings
|
from six import string_types
|
||||||
from django.core.serializers.json import DjangoJSONEncoder
|
|
||||||
from django.db.models.fields.files import FileField
|
|
||||||
|
|
||||||
from .http import http_date
|
from .http import http_date
|
||||||
|
|
||||||
@@ -69,22 +68,25 @@ class Signer(metaclass=Singleton):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
_supported_paramiko_ssh_key_types = (
|
||||||
|
paramiko.RSAKey,
|
||||||
|
paramiko.DSSKey,
|
||||||
|
paramiko.Ed25519Key,
|
||||||
|
paramiko.ECDSAKey,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def ssh_key_string_to_obj(text, password=None):
|
def ssh_key_string_to_obj(text, password=None):
|
||||||
key = None
|
key = None
|
||||||
try:
|
for ssh_key_type in _supported_paramiko_ssh_key_types:
|
||||||
key = paramiko.RSAKey.from_private_key(StringIO(text), password=password)
|
if not issubclass(ssh_key_type, paramiko.PKey):
|
||||||
except paramiko.SSHException:
|
continue
|
||||||
pass
|
try:
|
||||||
else:
|
key = ssh_key_type.from_private_key(StringIO(text), password=password)
|
||||||
return key
|
except paramiko.SSHException:
|
||||||
|
pass
|
||||||
try:
|
else:
|
||||||
key = paramiko.DSSKey.from_private_key(StringIO(text), password=password)
|
return key
|
||||||
except paramiko.SSHException:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
return key
|
|
||||||
|
|
||||||
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):
|
def ssh_pubkey_gen(private_key=None, username='jumpserver', hostname='localhost', password=None):
|
||||||
private_key = ssh_private_key_gen(private_key, password=password)
|
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')
|
raise IOError('Invalid private key')
|
||||||
|
|
||||||
public_key = "%(key_type)s %(key_content)s %(username)s@%(hostname)s" % {
|
public_key = "%(key_type)s %(key_content)s %(username)s@%(hostname)s" % {
|
||||||
@@ -137,17 +139,65 @@ def ssh_key_gen(length=2048, type='rsa', password=None, username='jumpserver', h
|
|||||||
|
|
||||||
|
|
||||||
def validate_ssh_private_key(text, password=None):
|
def validate_ssh_private_key(text, password=None):
|
||||||
if isinstance(text, bytes):
|
key = parse_ssh_private_key_str(text, password=password)
|
||||||
try:
|
return bool(key)
|
||||||
text = text.decode("utf-8")
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
key = ssh_key_string_to_obj(text, password=password)
|
|
||||||
if key is None:
|
def parse_ssh_private_key_str(text: bytes, password=None) -> str:
|
||||||
return False
|
private_key = _parse_ssh_private_key(text, password=password)
|
||||||
else:
|
if private_key is None:
|
||||||
return True
|
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 not bool(password):
|
||||||
|
password = None
|
||||||
|
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):
|
def validate_ssh_public_key(text):
|
||||||
@@ -226,4 +276,4 @@ def ensure_last_char_is_ascii(data):
|
|||||||
def data_to_json(data, sort_keys=True, indent=2, cls=None):
|
def data_to_json(data, sort_keys=True, indent=2, cls=None):
|
||||||
if cls is None:
|
if cls is None:
|
||||||
cls = DjangoJSONEncoder
|
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)
|
||||||
|
|||||||
@@ -18,25 +18,35 @@ def random_ip():
|
|||||||
return socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
|
return socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
|
||||||
|
|
||||||
|
|
||||||
def random_string(length, lower=True, upper=True, digit=True, special_char=False):
|
def random_string(length: int, lower=True, upper=True, digit=True, special_char=False):
|
||||||
chars = string.ascii_letters
|
args_names = ['lower', 'upper', 'digit', 'special_char']
|
||||||
if digit:
|
args_values = [lower, upper, digit, special_char]
|
||||||
chars += string.digits
|
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:
|
while True:
|
||||||
password = list(random.choice(chars) for i in range(length))
|
password = list(random.choice(chars) for i in range(length))
|
||||||
if upper and not any(c.upper() for c in password):
|
for k, v in kwargs.items():
|
||||||
continue
|
if v and not (set(password) & set(args_string_map[k])):
|
||||||
if lower and not any(c.lower() for c in password):
|
# 没有包含指定的字符, retry
|
||||||
continue
|
break
|
||||||
if digit and not any(c.isdigit() for c in password):
|
else:
|
||||||
continue
|
if not can_startswith_special_char and password[0] in args_string_map['special_char']:
|
||||||
break
|
# 首位不能为特殊字符, retry
|
||||||
|
continue
|
||||||
if special_char:
|
else:
|
||||||
spc = random.choice(string_punctuation)
|
# 满足要求终止 while 循环
|
||||||
i = random.choice(range(1, len(password)))
|
break
|
||||||
password[i] = spc
|
|
||||||
|
|
||||||
password = ''.join(password)
|
password = ''.join(password)
|
||||||
return password
|
return password
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ class Config(dict):
|
|||||||
'REDIS_SSL_KEY': None,
|
'REDIS_SSL_KEY': None,
|
||||||
'REDIS_SSL_CERT': None,
|
'REDIS_SSL_CERT': None,
|
||||||
'REDIS_SSL_CA': None,
|
'REDIS_SSL_CA': None,
|
||||||
|
'REDIS_SSL_REQUIRED': 'none',
|
||||||
# Redis Sentinel
|
# Redis Sentinel
|
||||||
'REDIS_SENTINEL_HOSTS': '',
|
'REDIS_SENTINEL_HOSTS': '',
|
||||||
'REDIS_SENTINEL_PASSWORD': '',
|
'REDIS_SENTINEL_PASSWORD': '',
|
||||||
@@ -269,6 +270,8 @@ class Config(dict):
|
|||||||
'AUTH_OPENID_USER_ATTR_MAP': {
|
'AUTH_OPENID_USER_ATTR_MAP': {
|
||||||
'name': 'name', 'username': 'preferred_username', 'email': 'email'
|
'name': 'name', 'username': 'preferred_username', 'email': 'email'
|
||||||
},
|
},
|
||||||
|
'AUTH_OPENID_PKCE': False,
|
||||||
|
'AUTH_OPENID_CODE_CHALLENGE_METHOD': 'S256',
|
||||||
|
|
||||||
# OpenID 新配置参数 (version >= 1.5.9)
|
# OpenID 新配置参数 (version >= 1.5.9)
|
||||||
'AUTH_OPENID_PROVIDER_ENDPOINT': 'https://oidc.example.com/',
|
'AUTH_OPENID_PROVIDER_ENDPOINT': 'https://oidc.example.com/',
|
||||||
@@ -373,6 +376,7 @@ class Config(dict):
|
|||||||
'AUTH_FEISHU': False,
|
'AUTH_FEISHU': False,
|
||||||
'FEISHU_APP_ID': '',
|
'FEISHU_APP_ID': '',
|
||||||
'FEISHU_APP_SECRET': '',
|
'FEISHU_APP_SECRET': '',
|
||||||
|
'FEISHU_VERSION': 'feishu',
|
||||||
|
|
||||||
'LOGIN_REDIRECT_TO_BACKEND': '', # 'OPENID / CAS / SAML2
|
'LOGIN_REDIRECT_TO_BACKEND': '', # 'OPENID / CAS / SAML2
|
||||||
'LOGIN_REDIRECT_MSG_ENABLED': True,
|
'LOGIN_REDIRECT_MSG_ENABLED': True,
|
||||||
|
|||||||
@@ -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_IGNORE_SSL_VERIFICATION = CONFIG.AUTH_OPENID_IGNORE_SSL_VERIFICATION
|
||||||
AUTH_OPENID_ALWAYS_UPDATE_USER = CONFIG.AUTH_OPENID_ALWAYS_UPDATE_USER
|
AUTH_OPENID_ALWAYS_UPDATE_USER = CONFIG.AUTH_OPENID_ALWAYS_UPDATE_USER
|
||||||
AUTH_OPENID_USER_ATTR_MAP = CONFIG.AUTH_OPENID_USER_ATTR_MAP
|
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_URL_NAME = 'authentication:openid:login'
|
||||||
AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME = 'authentication:openid:login-callback'
|
AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME = 'authentication:openid:login-callback'
|
||||||
AUTH_OPENID_AUTH_LOGOUT_URL_NAME = 'authentication:openid:logout'
|
AUTH_OPENID_AUTH_LOGOUT_URL_NAME = 'authentication:openid:logout'
|
||||||
@@ -135,6 +137,7 @@ DINGTALK_APPSECRET = CONFIG.DINGTALK_APPSECRET
|
|||||||
AUTH_FEISHU = CONFIG.AUTH_FEISHU
|
AUTH_FEISHU = CONFIG.AUTH_FEISHU
|
||||||
FEISHU_APP_ID = CONFIG.FEISHU_APP_ID
|
FEISHU_APP_ID = CONFIG.FEISHU_APP_ID
|
||||||
FEISHU_APP_SECRET = CONFIG.FEISHU_APP_SECRET
|
FEISHU_APP_SECRET = CONFIG.FEISHU_APP_SECRET
|
||||||
|
FEISHU_VERSION = CONFIG.FEISHU_VERSION
|
||||||
|
|
||||||
# Saml2 auth
|
# Saml2 auth
|
||||||
AUTH_SAML2 = CONFIG.AUTH_SAML2
|
AUTH_SAML2 = CONFIG.AUTH_SAML2
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
|
from redis.sentinel import SentinelManagedSSLConnection
|
||||||
|
|
||||||
|
|
||||||
if platform.system() == 'Darwin' and platform.machine() == 'arm64':
|
if platform.system() == 'Darwin' and platform.machine() == 'arm64':
|
||||||
import pymysql
|
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
|
DB_USE_SSL = False
|
||||||
if CONFIG.DB_ENGINE.lower() == 'mysql':
|
if CONFIG.DB_ENGINE.lower() == 'mysql':
|
||||||
DB_OPTIONS['init_command'] = "SET sql_mode='STRICT_TRANS_TABLES'"
|
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',
|
'CLIENT_CLASS': 'django_redis.client.SentinelClient',
|
||||||
'SENTINELS': REDIS_SENTINELS, 'PASSWORD': CONFIG.REDIS_PASSWORD,
|
'SENTINELS': REDIS_SENTINELS, 'PASSWORD': CONFIG.REDIS_PASSWORD,
|
||||||
'SENTINEL_KWARGS': {
|
'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,
|
'password': REDIS_SENTINEL_PASSWORD,
|
||||||
'socket_timeout': REDIS_SENTINEL_SOCKET_TIMEOUT
|
'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'
|
DJANGO_REDIS_CONNECTION_FACTORY = 'django_redis.pool.SentinelConnectionFactory'
|
||||||
else:
|
else:
|
||||||
REDIS_LOCATION_NO_DB = '%(protocol)s://:%(password)s@%(host)s:%(port)s/{}' % {
|
REDIS_LOCATION_NO_DB = '%(protocol)s://:%(password)s@%(host)s:%(port)s/{}' % {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
import os
|
import os
|
||||||
import ssl
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
REDIS_SSL_CA, REDIS_SSL_CERT, REDIS_SSL_KEY, REDIS_SSL_REQUIRED, REDIS_USE_SSL,
|
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
|
REDIS_SENTINEL_SOCKET_TIMEOUT
|
||||||
)
|
)
|
||||||
from ..const import CONFIG, PROJECT_DIR
|
from ..const import CONFIG, PROJECT_DIR
|
||||||
@@ -81,41 +81,54 @@ BOOTSTRAP3 = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Django channels support websocket
|
# Django channels support websocket
|
||||||
if not REDIS_USE_SSL:
|
REDIS_LAYERS_HOST = {
|
||||||
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 = {
|
|
||||||
'db': CONFIG.REDIS_DB_WS,
|
'db': CONFIG.REDIS_DB_WS,
|
||||||
'password': CONFIG.REDIS_PASSWORD or None,
|
'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:
|
if REDIS_SENTINEL_SERVICE_NAME and REDIS_SENTINELS:
|
||||||
REDIS_HOST['sentinels'] = REDIS_SENTINELS
|
REDIS_LAYERS_HOST['sentinels'] = REDIS_SENTINELS
|
||||||
REDIS_HOST['master_name'] = REDIS_SENTINEL_SERVICE_NAME
|
REDIS_LAYERS_HOST['master_name'] = REDIS_SENTINEL_SERVICE_NAME
|
||||||
REDIS_HOST['sentinel_kwargs'] = {
|
REDIS_LAYERS_HOST['sentinel_kwargs'] = {
|
||||||
'password': REDIS_SENTINEL_PASSWORD,
|
'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:
|
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 = {
|
CHANNEL_LAYERS = {
|
||||||
'default': {
|
'default': {
|
||||||
'BACKEND': 'common.cache.RedisChannelLayer',
|
'BACKEND': 'common.cache.RedisChannelLayer',
|
||||||
'CONFIG': {
|
'CONFIG': {
|
||||||
"hosts": [REDIS_HOST],
|
"hosts": [REDIS_LAYERS_HOST],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
ASGI_APPLICATION = 'jumpserver.routing.application'
|
ASGI_APPLICATION = 'jumpserver.routing.application'
|
||||||
|
|
||||||
# Dump all celery log to here
|
# Dump all celery log to here
|
||||||
@@ -132,13 +145,18 @@ if REDIS_SENTINEL_SERVICE_NAME and REDIS_SENTINELS:
|
|||||||
'master_name': REDIS_SENTINEL_SERVICE_NAME,
|
'master_name': REDIS_SENTINEL_SERVICE_NAME,
|
||||||
'sentinel_kwargs': {
|
'sentinel_kwargs': {
|
||||||
'password': REDIS_SENTINEL_PASSWORD,
|
'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
|
CELERY_BROKER_TRANSPORT_OPTIONS = CELERY_RESULT_BACKEND_TRANSPORT_OPTIONS = SENTINEL_OPTIONS
|
||||||
else:
|
else:
|
||||||
CELERY_BROKER_URL = CELERY_BROKER_URL_FORMAT % {
|
CELERY_BROKER_URL = CELERY_BROKER_URL_FORMAT % {
|
||||||
'protocol': 'rediss' if REDIS_USE_SSL else 'redis',
|
'protocol': REDIS_PROTOCOL,
|
||||||
'password': CONFIG.REDIS_PASSWORD,
|
'password': CONFIG.REDIS_PASSWORD,
|
||||||
'host': CONFIG.REDIS_HOST,
|
'host': CONFIG.REDIS_HOST,
|
||||||
'port': CONFIG.REDIS_PORT,
|
'port': CONFIG.REDIS_PORT,
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:7db985efdf818137dafe489f339955f4d71a245ffd6becc8f6efac539a625682
|
oid sha256:0cc37a87259f2cd3794ad628ee40497bd97beaee7219be3d9773e1e544c3e352
|
||||||
size 133463
|
size 134193
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2022-11-17 17:34+0800\n"
|
"POT-Creation-Date: 2023-05-08 18:10+0800\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\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
|
#: acls/models/base.py:25 acls/serializers/login_asset_acl.py:47
|
||||||
#: applications/models/application.py:219 assets/models/asset.py:138
|
#: applications/models/application.py:219 assets/models/asset.py:138
|
||||||
#: assets/models/base.py:175 assets/models/cluster.py:18
|
#: assets/models/base.py:177 assets/models/cluster.py:18
|
||||||
#: assets/models/cmd_filter.py:27 assets/models/domain.py:23
|
#: 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
|
#: 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
|
#: orgs/models.py:70 perms/models/base.py:83 rbac/models/role.py:29
|
||||||
#: settings/models.py:33 settings/serializers/sms.py:6
|
#: settings/models.py:33 settings/serializers/sms.py:6
|
||||||
#: terminal/models/endpoint.py:14 terminal/models/endpoint.py:87
|
#: 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
|
#: terminal/models/terminal.py:101 users/forms/profile.py:33
|
||||||
#: users/models/group.py:15 users/models/user.py:673
|
#: users/models/group.py:15 users/models/user.py:673
|
||||||
#: xpack/plugins/cloud/models.py:27
|
#: xpack/plugins/cloud/models.py:27
|
||||||
@@ -55,14 +55,14 @@ msgstr "アクティブ"
|
|||||||
|
|
||||||
#: acls/models/base.py:32 applications/models/application.py:232
|
#: acls/models/base.py:32 applications/models/application.py:232
|
||||||
#: assets/models/asset.py:143 assets/models/asset.py:231
|
#: 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:182
|
||||||
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:52
|
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:52
|
||||||
#: assets/models/cmd_filter.py:100 assets/models/domain.py:24
|
#: assets/models/cmd_filter.py:100 assets/models/domain.py:24
|
||||||
#: assets/models/domain.py:65 assets/models/group.py:23
|
#: assets/models/domain.py:65 assets/models/group.py:23
|
||||||
#: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:73
|
#: 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
|
#: 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/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
|
#: tickets/models/comment.py:32 tickets/models/ticket/general.py:288
|
||||||
#: users/models/group.py:16 users/models/user.py:712
|
#: users/models/group.py:16 users/models/user.py:712
|
||||||
#: xpack/plugins/change_auth_plan/models/base.py:44
|
#: xpack/plugins/change_auth_plan/models/base.py:44
|
||||||
@@ -131,7 +131,7 @@ msgstr "システムユーザー"
|
|||||||
#: assets/models/asset.py:386 assets/models/authbook.py:19
|
#: assets/models/asset.py:386 assets/models/authbook.py:19
|
||||||
#: assets/models/backup.py:31 assets/models/cmd_filter.py:42
|
#: assets/models/backup.py:31 assets/models/cmd_filter.py:42
|
||||||
#: assets/models/gathered_user.py:14 assets/serializers/label.py:30
|
#: 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
|
#: authentication/models.py:66 authentication/models.py:90
|
||||||
#: perms/models/asset_permission.py:23 terminal/backends/command/models.py:21
|
#: perms/models/asset_permission.py:23 terminal/backends/command/models.py:21
|
||||||
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:47
|
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:47
|
||||||
@@ -155,7 +155,7 @@ msgid "Format for comma-delimited string, with * indicating a match all. "
|
|||||||
msgstr "コンマ区切り文字列の形式。* はすべて一致することを示します。"
|
msgstr "コンマ区切り文字列の形式。* はすべて一致することを示します。"
|
||||||
|
|
||||||
#: acls/serializers/login_acl.py:15 acls/serializers/login_asset_acl.py:17
|
#: 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:178
|
||||||
#: assets/models/gathered_user.py:15 audits/models.py:139
|
#: assets/models/gathered_user.py:15 audits/models.py:139
|
||||||
#: authentication/forms.py:25 authentication/forms.py:27
|
#: authentication/forms.py:25 authentication/forms.py:27
|
||||||
#: authentication/models.py:260
|
#: authentication/models.py:260
|
||||||
@@ -186,7 +186,7 @@ msgstr ""
|
|||||||
#: authentication/templates/authentication/_msg_oauth_bind.html:12
|
#: authentication/templates/authentication/_msg_oauth_bind.html:12
|
||||||
#: authentication/templates/authentication/_msg_rest_password_success.html:8
|
#: authentication/templates/authentication/_msg_rest_password_success.html:8
|
||||||
#: authentication/templates/authentication/_msg_rest_public_key_success.html:8
|
#: authentication/templates/authentication/_msg_rest_public_key_success.html:8
|
||||||
#: settings/serializers/terminal.py:10 terminal/serializers/endpoint.py:54
|
#: settings/serializers/terminal.py:10 terminal/serializers/endpoint.py:58
|
||||||
msgid "IP"
|
msgid "IP"
|
||||||
msgstr "IP"
|
msgstr "IP"
|
||||||
|
|
||||||
@@ -269,7 +269,7 @@ msgid "Application"
|
|||||||
msgstr "アプリケーション"
|
msgstr "アプリケーション"
|
||||||
|
|
||||||
#: applications/models/account.py:15 assets/models/authbook.py:20
|
#: 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
|
#: authentication/models.py:83 perms/models/application_permission.py:33
|
||||||
#: perms/models/asset_permission.py:25 terminal/backends/command/models.py:22
|
#: perms/models/asset_permission.py:25 terminal/backends/command/models.py:22
|
||||||
#: terminal/backends/command/serializers.py:36 terminal/models/session.py:49
|
#: terminal/backends/command/serializers.py:36 terminal/models/session.py:49
|
||||||
@@ -280,7 +280,7 @@ msgid "System user"
|
|||||||
msgstr "システムユーザー"
|
msgstr "システムユーザー"
|
||||||
|
|
||||||
#: applications/models/account.py:17 assets/models/authbook.py:21
|
#: 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"
|
msgid "Version"
|
||||||
msgstr "バージョン"
|
msgstr "バージョン"
|
||||||
|
|
||||||
@@ -310,7 +310,7 @@ msgstr "カテゴリ"
|
|||||||
#: assets/models/cmd_filter.py:86 assets/models/user.py:251
|
#: assets/models/cmd_filter.py:86 assets/models/user.py:251
|
||||||
#: authentication/models.py:70 perms/models/application_permission.py:24
|
#: authentication/models.py:70 perms/models/application_permission.py:24
|
||||||
#: perms/serializers/application/user_permission.py:34
|
#: 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/comment.py:26 tickets/models/flow.py:57
|
||||||
#: tickets/models/ticket/apply_application.py:18
|
#: tickets/models/ticket/apply_application.py:18
|
||||||
#: tickets/models/ticket/general.py:273
|
#: tickets/models/ticket/general.py:273
|
||||||
@@ -353,7 +353,7 @@ msgid "Type display"
|
|||||||
msgstr "タイプ表示"
|
msgstr "タイプ表示"
|
||||||
|
|
||||||
#: applications/serializers/application.py:105 assets/models/asset.py:230
|
#: applications/serializers/application.py:105 assets/models/asset.py:230
|
||||||
#: assets/models/base.py:181 assets/models/cluster.py:26
|
#: assets/models/base.py:183 assets/models/cluster.py:26
|
||||||
#: assets/models/cmd_filter.py:53 assets/models/domain.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/gathered_user.py:19 assets/models/group.py:22
|
||||||
#: assets/models/label.py:25 assets/serializers/account.py:18
|
#: assets/models/label.py:25 assets/serializers/account.py:18
|
||||||
@@ -367,7 +367,7 @@ msgstr "タイプ表示"
|
|||||||
msgid "Date created"
|
msgid "Date created"
|
||||||
msgstr "作成された日付"
|
msgstr "作成された日付"
|
||||||
|
|
||||||
#: applications/serializers/application.py:106 assets/models/base.py:182
|
#: applications/serializers/application.py:106 assets/models/base.py:184
|
||||||
#: assets/models/cmd_filter.py:54 assets/models/gathered_user.py:20
|
#: 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/account.py:21 assets/serializers/cmd_filter.py:29
|
||||||
#: assets/serializers/cmd_filter.py:49 common/db/models.py:117
|
#: assets/serializers/cmd_filter.py:49 common/db/models.py:117
|
||||||
@@ -445,7 +445,7 @@ msgid "Application path"
|
|||||||
msgstr "アプリケーションパス"
|
msgstr "アプリケーションパス"
|
||||||
|
|
||||||
#: applications/serializers/attrs/application_category/remote_app.py:44
|
#: 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/apply_application.py:38
|
||||||
#: tickets/serializers/ticket/common.py:59
|
#: tickets/serializers/ticket/common.py:59
|
||||||
#: xpack/plugins/change_auth_plan/serializers/asset.py:67
|
#: xpack/plugins/change_auth_plan/serializers/asset.py:67
|
||||||
@@ -634,7 +634,7 @@ msgid "Is active"
|
|||||||
msgstr "アクティブです。"
|
msgstr "アクティブです。"
|
||||||
|
|
||||||
#: assets/models/asset.py:222 assets/models/cluster.py:19
|
#: 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"
|
msgid "Admin user"
|
||||||
msgstr "管理ユーザー"
|
msgstr "管理ユーザー"
|
||||||
|
|
||||||
@@ -650,7 +650,7 @@ msgstr "資産番号"
|
|||||||
msgid "Labels"
|
msgid "Labels"
|
||||||
msgstr "ラベル"
|
msgstr "ラベル"
|
||||||
|
|
||||||
#: assets/models/asset.py:229 assets/models/base.py:183
|
#: assets/models/asset.py:229 assets/models/base.py:185
|
||||||
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:56
|
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:56
|
||||||
#: assets/models/cmd_filter.py:103 assets/models/group.py:21
|
#: 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
|
#: common/db/models.py:114 common/mixins/models.py:49 orgs/models.py:71
|
||||||
@@ -786,32 +786,32 @@ msgstr "成功は"
|
|||||||
msgid "Account backup execution"
|
msgid "Account backup execution"
|
||||||
msgstr "アカウントバックアップの実行"
|
msgstr "アカウントバックアップの実行"
|
||||||
|
|
||||||
#: assets/models/base.py:30 assets/tasks/const.py:51 audits/const.py:5
|
#: assets/models/base.py:29 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/geoip/utils.py:31 common/utils/ip/geoip/utils.py:37
|
||||||
#: common/utils/ip/utils.py:84
|
#: common/utils/ip/utils.py:84
|
||||||
msgid "Unknown"
|
msgid "Unknown"
|
||||||
msgstr "不明"
|
msgstr "不明"
|
||||||
|
|
||||||
#: assets/models/base.py:31
|
#: assets/models/base.py:30
|
||||||
msgid "Ok"
|
msgid "Ok"
|
||||||
msgstr "OK"
|
msgstr "OK"
|
||||||
|
|
||||||
#: assets/models/base.py:32 audits/models.py:136
|
#: assets/models/base.py:31 audits/models.py:136
|
||||||
#: xpack/plugins/change_auth_plan/serializers/app.py:88
|
#: xpack/plugins/change_auth_plan/serializers/app.py:88
|
||||||
#: xpack/plugins/change_auth_plan/serializers/asset.py:199
|
#: xpack/plugins/change_auth_plan/serializers/asset.py:199
|
||||||
#: xpack/plugins/cloud/const.py:41
|
#: xpack/plugins/cloud/const.py:41
|
||||||
msgid "Failed"
|
msgid "Failed"
|
||||||
msgstr "失敗しました"
|
msgstr "失敗しました"
|
||||||
|
|
||||||
#: assets/models/base.py:38 assets/serializers/domain.py:47
|
#: assets/models/base.py:37 assets/serializers/domain.py:47
|
||||||
msgid "Connectivity"
|
msgid "Connectivity"
|
||||||
msgstr "接続性"
|
msgstr "接続性"
|
||||||
|
|
||||||
#: assets/models/base.py:40 authentication/models.py:263
|
#: assets/models/base.py:39 authentication/models.py:263
|
||||||
msgid "Date verified"
|
msgid "Date verified"
|
||||||
msgstr "確認済みの日付"
|
msgstr "確認済みの日付"
|
||||||
|
|
||||||
#: assets/models/base.py:177 assets/serializers/base.py:15
|
#: assets/models/base.py:179 assets/serializers/base.py:14
|
||||||
#: assets/serializers/base.py:37 assets/serializers/system_user.py:29
|
#: assets/serializers/base.py:37 assets/serializers/system_user.py:29
|
||||||
#: audits/signal_handlers.py:58 authentication/confirm/password.py:9
|
#: audits/signal_handlers.py:58 authentication/confirm/password.py:9
|
||||||
#: authentication/forms.py:32
|
#: authentication/forms.py:32
|
||||||
@@ -829,14 +829,14 @@ msgstr "確認済みの日付"
|
|||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "パスワード"
|
msgstr "パスワード"
|
||||||
|
|
||||||
#: assets/models/base.py:178 assets/serializers/base.py:41
|
#: assets/models/base.py:180 assets/serializers/base.py:41
|
||||||
#: xpack/plugins/change_auth_plan/models/asset.py:53
|
#: 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:130
|
||||||
#: xpack/plugins/change_auth_plan/models/asset.py:206
|
#: xpack/plugins/change_auth_plan/models/asset.py:206
|
||||||
msgid "SSH private key"
|
msgid "SSH private key"
|
||||||
msgstr "SSH秘密鍵"
|
msgstr "SSH秘密鍵"
|
||||||
|
|
||||||
#: assets/models/base.py:179 xpack/plugins/change_auth_plan/models/asset.py:56
|
#: assets/models/base.py:181 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:126
|
||||||
#: xpack/plugins/change_auth_plan/models/asset.py:202
|
#: xpack/plugins/change_auth_plan/models/asset.py:202
|
||||||
msgid "SSH public key"
|
msgid "SSH public key"
|
||||||
@@ -889,7 +889,7 @@ msgstr "デフォルトクラスター"
|
|||||||
msgid "User group"
|
msgid "User group"
|
||||||
msgstr "ユーザーグループ"
|
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"
|
msgid "Command filter"
|
||||||
msgstr "コマンドフィルター"
|
msgstr "コマンドフィルター"
|
||||||
|
|
||||||
@@ -954,7 +954,7 @@ msgstr "テストゲートウェイ"
|
|||||||
msgid "Unable to connect to port {port} on {ip}"
|
msgid "Unable to connect to port {port} on {ip}"
|
||||||
msgstr "{ip} でポート {port} に接続できません"
|
msgstr "{ip} でポート {port} に接続できません"
|
||||||
|
|
||||||
#: assets/models/domain.py:134 authentication/middleware.py:75
|
#: assets/models/domain.py:134 authentication/middleware.py:87
|
||||||
#: xpack/plugins/cloud/providers/fc.py:48
|
#: xpack/plugins/cloud/providers/fc.py:48
|
||||||
msgid "Authentication failed"
|
msgid "Authentication failed"
|
||||||
msgstr "認証に失敗しました"
|
msgstr "認証に失敗しました"
|
||||||
@@ -1015,7 +1015,7 @@ msgstr "フルバリュー"
|
|||||||
msgid "Parent key"
|
msgid "Parent key"
|
||||||
msgstr "親キー"
|
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
|
#: xpack/plugins/cloud/models.py:95 xpack/plugins/cloud/serializers/task.py:75
|
||||||
msgid "Node"
|
msgid "Node"
|
||||||
msgstr "ノード"
|
msgstr "ノード"
|
||||||
@@ -1095,7 +1095,7 @@ msgstr "ユーザースイッチ"
|
|||||||
msgid "Switch from"
|
msgid "Switch from"
|
||||||
msgstr "から切り替え"
|
msgstr "から切り替え"
|
||||||
|
|
||||||
#: assets/models/user.py:345
|
#: assets/models/user.py:356
|
||||||
msgid "Can match system user"
|
msgid "Can match system user"
|
||||||
msgstr "システムユーザーに一致できます"
|
msgstr "システムユーザーに一致できます"
|
||||||
|
|
||||||
@@ -1192,7 +1192,7 @@ msgstr "ssh秘密鍵"
|
|||||||
msgid "Key password"
|
msgid "Key password"
|
||||||
msgstr "キーパスワード"
|
msgstr "キーパスワード"
|
||||||
|
|
||||||
#: assets/serializers/base.py:58
|
#: assets/serializers/base.py:58 assets/serializers/utils.py:24
|
||||||
msgid "private key invalid or passphrase error"
|
msgid "private key invalid or passphrase error"
|
||||||
msgstr "秘密鍵が無効またはpassphraseエラー"
|
msgstr "秘密鍵が無効またはpassphraseエラー"
|
||||||
|
|
||||||
@@ -1205,7 +1205,7 @@ msgid "Pattern"
|
|||||||
msgstr "パターン"
|
msgstr "パターン"
|
||||||
|
|
||||||
#: assets/serializers/domain.py:14 assets/serializers/label.py:12
|
#: 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
|
#: perms/serializers/asset/permission.py:49
|
||||||
msgid "Assets amount"
|
msgid "Assets amount"
|
||||||
msgstr "資産額"
|
msgstr "資産額"
|
||||||
@@ -1234,86 +1234,81 @@ msgstr "同じレベルのノード名を同じにすることはできません
|
|||||||
msgid "SSH key fingerprint"
|
msgid "SSH key fingerprint"
|
||||||
msgstr "SSHキー指紋"
|
msgstr "SSHキー指紋"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:40
|
#: assets/serializers/system_user.py:59
|
||||||
#: perms/serializers/application/permission.py:46
|
|
||||||
msgid "Apps amount"
|
|
||||||
msgstr "アプリの量"
|
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:62
|
|
||||||
#: perms/serializers/asset/permission.py:50
|
#: perms/serializers/asset/permission.py:50
|
||||||
msgid "Nodes amount"
|
msgid "Nodes amount"
|
||||||
msgstr "ノード量"
|
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"
|
msgid "Login mode display"
|
||||||
msgstr "ログインモード表示"
|
msgstr "ログインモード表示"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:66
|
#: assets/serializers/system_user.py:63
|
||||||
msgid "Ad domain"
|
msgid "Ad domain"
|
||||||
msgstr "広告ドメイン"
|
msgstr "広告ドメイン"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:67
|
#: assets/serializers/system_user.py:64
|
||||||
msgid "Is asset protocol"
|
msgid "Is asset protocol"
|
||||||
msgstr "資産プロトコルです"
|
msgstr "資産プロトコルです"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:68
|
#: assets/serializers/system_user.py:65
|
||||||
msgid "Only ssh and automatic login system users are supported"
|
msgid "Only ssh and automatic login system users are supported"
|
||||||
msgstr "sshと自動ログインシステムのユーザーのみがサポートされています"
|
msgstr "sshと自動ログインシステムのユーザーのみがサポートされています"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:108
|
#: assets/serializers/system_user.py:105
|
||||||
msgid "Username same with user with protocol {} only allow 1"
|
msgid "Username same with user with protocol {} only allow 1"
|
||||||
msgstr "プロトコル {} のユーザーと同じユーザー名は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"
|
msgid "Special char not allowed"
|
||||||
msgstr "特別なcharは許可されていません"
|
msgstr "特別なcharは許可されていません"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:131
|
#: assets/serializers/system_user.py:128
|
||||||
msgid "* Automatic login mode must fill in the username."
|
msgid "* Automatic login mode must fill in the username."
|
||||||
msgstr "* 自動ログインモードはユーザー名を入力する必要があります。"
|
msgstr "* 自動ログインモードはユーザー名を入力する必要があります。"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:146
|
#: assets/serializers/system_user.py:143
|
||||||
msgid "Path should starts with /"
|
msgid "Path should starts with /"
|
||||||
msgstr "パスは/で始まる必要があります"
|
msgstr "パスは/で始まる必要があります"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:158
|
#: assets/serializers/system_user.py:155
|
||||||
msgid "Password or private key required"
|
msgid "Password or private key required"
|
||||||
msgstr "パスワードまたは秘密鍵が必要"
|
msgstr "パスワードまたは秘密鍵が必要"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:172
|
#: assets/serializers/system_user.py:169
|
||||||
msgid "Only ssh protocol system users are allowed"
|
msgid "Only ssh protocol system users are allowed"
|
||||||
msgstr "Sshプロトコルシステムユーザーのみが許可されています"
|
msgstr "Sshプロトコルシステムユーザーのみが許可されています"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:176
|
#: assets/serializers/system_user.py:173
|
||||||
msgid "The protocol must be consistent with the current user: {}"
|
msgid "The protocol must be consistent with the current user: {}"
|
||||||
msgstr "プロトコルは現在のユーザーと一致している必要があります: {}"
|
msgstr "プロトコルは現在のユーザーと一致している必要があります: {}"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:180
|
#: assets/serializers/system_user.py:177
|
||||||
msgid "Only system users with automatic login are allowed"
|
msgid "Only system users with automatic login are allowed"
|
||||||
msgstr "自動ログインを持つシステムユーザーのみが許可されます"
|
msgstr "自動ログインを持つシステムユーザーのみが許可されます"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:288
|
#: assets/serializers/system_user.py:284
|
||||||
msgid "System user name"
|
msgid "System user name"
|
||||||
msgstr "システムユーザー名"
|
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
|
#: rbac/serializers/rolebinding.py:23
|
||||||
msgid "Org name"
|
msgid "Org name"
|
||||||
msgstr "組織名"
|
msgstr "組織名"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:298
|
#: assets/serializers/system_user.py:294
|
||||||
msgid "Asset hostname"
|
msgid "Asset hostname"
|
||||||
msgstr "資産ホスト名"
|
msgstr "資産ホスト名"
|
||||||
|
|
||||||
#: assets/serializers/utils.py:11
|
#: assets/serializers/utils.py:13
|
||||||
msgid "Password can not contains `{{` "
|
msgid "Password can not contains `{{` "
|
||||||
msgstr "パスワードには '{{' を含まない"
|
msgstr "パスワードには '{{' を含まない"
|
||||||
|
|
||||||
#: assets/serializers/utils.py:14
|
#: assets/serializers/utils.py:16
|
||||||
msgid "Password can not contains `'` "
|
msgid "Password can not contains `'` "
|
||||||
msgstr "パスワードには `'` を含まない"
|
msgstr "パスワードには `'` を含まない"
|
||||||
|
|
||||||
#: assets/serializers/utils.py:16
|
#: assets/serializers/utils.py:18
|
||||||
msgid "Password can not contains `\"` "
|
msgid "Password can not contains `\"` "
|
||||||
msgstr "パスワードには `\"` を含まない"
|
msgstr "パスワードには `\"` を含まない"
|
||||||
|
|
||||||
@@ -1429,6 +1424,10 @@ msgid "For security, do not push user {}"
|
|||||||
msgstr "セキュリティのために、ユーザー {} をプッシュしないでください"
|
msgstr "セキュリティのために、ユーザー {} をプッシュしないでください"
|
||||||
|
|
||||||
#: assets/tasks/utils.py:55
|
#: 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"
|
msgid "No assets matched, stop task"
|
||||||
msgstr "一致する資産がない、タスクを停止"
|
msgstr "一致する資産がない、タスクを停止"
|
||||||
|
|
||||||
@@ -1664,7 +1663,8 @@ msgstr "企業微信"
|
|||||||
|
|
||||||
#: audits/signal_handlers.py:62 authentication/views/feishu.py:144
|
#: audits/signal_handlers.py:62 authentication/views/feishu.py:144
|
||||||
#: authentication/views/login.py:85 notifications/backends/__init__.py:14
|
#: 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"
|
msgid "FeiShu"
|
||||||
msgstr "本を飛ばす"
|
msgstr "本を飛ばす"
|
||||||
|
|
||||||
@@ -1722,7 +1722,7 @@ msgid "Authentication"
|
|||||||
msgstr "認証"
|
msgstr "認証"
|
||||||
|
|
||||||
#: authentication/backends/custom.py:58
|
#: authentication/backends/custom.py:58
|
||||||
#: authentication/backends/oauth2/backends.py:158 authentication/models.py:158
|
#: authentication/backends/oauth2/backends.py:167 authentication/models.py:158
|
||||||
msgid "User invalid, disabled or expired"
|
msgid "User invalid, disabled or expired"
|
||||||
msgstr "ユーザーが無効、無効、または期限切れです"
|
msgstr "ユーザーが無効、無効、または期限切れです"
|
||||||
|
|
||||||
@@ -1937,15 +1937,15 @@ msgstr "本を飛ばすは拘束されていません"
|
|||||||
msgid "Your password is invalid"
|
msgid "Your password is invalid"
|
||||||
msgstr "パスワードが無効です"
|
msgstr "パスワードが無効です"
|
||||||
|
|
||||||
#: authentication/errors/redirect.py:85 authentication/mixins.py:306
|
#: authentication/errors/redirect.py:85 authentication/mixins.py:307
|
||||||
msgid "Your password is too simple, please change it for security"
|
msgid "Your password is too simple, please change it for security"
|
||||||
msgstr "パスワードがシンプルすぎるので、セキュリティのために変更してください"
|
msgstr "パスワードがシンプルすぎるので、セキュリティのために変更してください"
|
||||||
|
|
||||||
#: authentication/errors/redirect.py:93 authentication/mixins.py:313
|
#: authentication/errors/redirect.py:93 authentication/mixins.py:314
|
||||||
msgid "You should to change your password before login"
|
msgid "You should to change your password before login"
|
||||||
msgstr "ログインする前にパスワードを変更する必要があります"
|
msgstr "ログインする前にパスワードを変更する必要があります"
|
||||||
|
|
||||||
#: authentication/errors/redirect.py:101 authentication/mixins.py:320
|
#: authentication/errors/redirect.py:101 authentication/mixins.py:321
|
||||||
msgid "Your password has expired, please reset before logging in"
|
msgid "Your password has expired, please reset before logging in"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"パスワードの有効期限が切れました。ログインする前にリセットしてください。"
|
"パスワードの有効期限が切れました。ログインする前にリセットしてください。"
|
||||||
@@ -2044,15 +2044,15 @@ msgstr "電話番号を設定して有効にする"
|
|||||||
msgid "Clear phone number to disable"
|
msgid "Clear phone number to disable"
|
||||||
msgstr "無効にする電話番号をクリアする"
|
msgstr "無効にする電話番号をクリアする"
|
||||||
|
|
||||||
#: authentication/middleware.py:76 settings/utils/ldap.py:652
|
#: authentication/middleware.py:88 settings/utils/ldap.py:652
|
||||||
msgid "Authentication failed (before login check failed): {}"
|
msgid "Authentication failed (before login check failed): {}"
|
||||||
msgstr "認証に失敗しました (ログインチェックが失敗する前): {}"
|
msgstr "認証に失敗しました (ログインチェックが失敗する前): {}"
|
||||||
|
|
||||||
#: authentication/mixins.py:256
|
#: authentication/mixins.py:257
|
||||||
msgid "The MFA type ({}) is not enabled"
|
msgid "The MFA type ({}) is not enabled"
|
||||||
msgstr "MFAタイプ ({}) が有効になっていない"
|
msgstr "MFAタイプ ({}) が有効になっていない"
|
||||||
|
|
||||||
#: authentication/mixins.py:296
|
#: authentication/mixins.py:297
|
||||||
msgid "Please change your password"
|
msgid "Please change your password"
|
||||||
msgstr "パスワードを変更してください"
|
msgstr "パスワードを変更してください"
|
||||||
|
|
||||||
@@ -2263,7 +2263,7 @@ msgstr "コードエラー"
|
|||||||
#: authentication/templates/authentication/_msg_reset_password_code.html:9
|
#: authentication/templates/authentication/_msg_reset_password_code.html:9
|
||||||
#: authentication/templates/authentication/_msg_rest_password_success.html:2
|
#: authentication/templates/authentication/_msg_rest_password_success.html:2
|
||||||
#: authentication/templates/authentication/_msg_rest_public_key_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:417 ops/tasks.py:145 ops/tasks.py:148
|
||||||
#: perms/templates/perms/_msg_item_permissions_expire.html:3
|
#: perms/templates/perms/_msg_item_permissions_expire.html:3
|
||||||
#: perms/templates/perms/_msg_permed_items_expire.html:3
|
#: perms/templates/perms/_msg_permed_items_expire.html:3
|
||||||
#: tickets/templates/tickets/approve_check_password.html:33
|
#: tickets/templates/tickets/approve_check_password.html:33
|
||||||
@@ -2747,11 +2747,11 @@ msgstr "特殊文字を含むべきではない"
|
|||||||
msgid "The mobile phone number format is incorrect"
|
msgid "The mobile phone number format is incorrect"
|
||||||
msgstr "携帯電話番号の形式が正しくありません"
|
msgstr "携帯電話番号の形式が正しくありません"
|
||||||
|
|
||||||
#: jumpserver/conf.py:412
|
#: jumpserver/conf.py:416
|
||||||
msgid "Create account successfully"
|
msgid "Create account successfully"
|
||||||
msgstr "アカウントを正常に作成"
|
msgstr "アカウントを正常に作成"
|
||||||
|
|
||||||
#: jumpserver/conf.py:414
|
#: jumpserver/conf.py:418
|
||||||
msgid "Your account has been created successfully"
|
msgid "Your account has been created successfully"
|
||||||
msgstr "アカウントが正常に作成されました"
|
msgstr "アカウントが正常に作成されました"
|
||||||
|
|
||||||
@@ -3198,6 +3198,10 @@ msgstr "ユーザーグループの量"
|
|||||||
msgid "System users amount"
|
msgid "System users amount"
|
||||||
msgstr "システムユーザー数"
|
msgstr "システムユーザー数"
|
||||||
|
|
||||||
|
#: perms/serializers/application/permission.py:46
|
||||||
|
msgid "Apps amount"
|
||||||
|
msgstr "アプリの量"
|
||||||
|
|
||||||
#: perms/serializers/application/permission.py:79
|
#: perms/serializers/application/permission.py:79
|
||||||
msgid ""
|
msgid ""
|
||||||
"The application list contains applications that are different from the "
|
"The application list contains applications that are different from the "
|
||||||
@@ -3603,7 +3607,7 @@ msgstr "CAS"
|
|||||||
msgid "Enable CAS Auth"
|
msgid "Enable CAS Auth"
|
||||||
msgstr "CAS 認証の有効化"
|
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"
|
msgid "Server url"
|
||||||
msgstr "サービス側アドレス"
|
msgstr "サービス側アドレス"
|
||||||
|
|
||||||
@@ -3636,7 +3640,7 @@ msgstr "そうでない場合はユーザーを作成"
|
|||||||
msgid "Enable DingTalk Auth"
|
msgid "Enable DingTalk Auth"
|
||||||
msgstr "ピン認証の有効化"
|
msgstr "ピン認証の有効化"
|
||||||
|
|
||||||
#: settings/serializers/auth/feishu.py:14
|
#: settings/serializers/auth/feishu.py:16
|
||||||
msgid "Enable FeiShu Auth"
|
msgid "Enable FeiShu Auth"
|
||||||
msgstr "飛本認証の有効化"
|
msgstr "飛本認証の有効化"
|
||||||
|
|
||||||
@@ -3723,11 +3727,11 @@ msgstr "クライアントID"
|
|||||||
msgid "Client Secret"
|
msgid "Client Secret"
|
||||||
msgstr "クライアント秘密"
|
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"
|
msgid "Provider auth endpoint"
|
||||||
msgstr "認証エンドポイントアドレス"
|
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"
|
msgid "Provider token endpoint"
|
||||||
msgstr "プロバイダートークンエンドポイント"
|
msgstr "プロバイダートークンエンドポイント"
|
||||||
|
|
||||||
@@ -3735,15 +3739,15 @@ msgstr "プロバイダートークンエンドポイント"
|
|||||||
msgid "Client authentication method"
|
msgid "Client authentication method"
|
||||||
msgstr "クライアント認証方式"
|
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"
|
msgid "Provider userinfo endpoint"
|
||||||
msgstr "プロバイダーuserinfoエンドポイント"
|
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"
|
msgid "Provider end session endpoint"
|
||||||
msgstr "プロバイダーのセッション終了エンドポイント"
|
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
|
#: settings/serializers/auth/saml2.py:35
|
||||||
msgid "Always update user"
|
msgid "Always update user"
|
||||||
msgstr "常にユーザーを更新"
|
msgstr "常にユーザーを更新"
|
||||||
@@ -3772,51 +3776,59 @@ msgstr ""
|
|||||||
"ユーザー属性マッピングは、OpenIDのユーザー属性をjumpserverユーザーにマッピン"
|
"ユーザー属性マッピングは、OpenIDのユーザー属性をjumpserverユーザーにマッピン"
|
||||||
"グする方法、username, name,emailは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"
|
msgid "Use Keycloak"
|
||||||
msgstr "Keycloakを使用する"
|
msgstr "Keycloakを使用する"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:52
|
#: settings/serializers/auth/oidc.py:57
|
||||||
msgid "Realm name"
|
msgid "Realm name"
|
||||||
msgstr "レルム名"
|
msgstr "レルム名"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:58
|
#: settings/serializers/auth/oidc.py:63
|
||||||
msgid "Enable OPENID Auth"
|
msgid "Enable OPENID Auth"
|
||||||
msgstr "OIDC認証の有効化"
|
msgstr "OIDC認証の有効化"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:60
|
#: settings/serializers/auth/oidc.py:65
|
||||||
msgid "Provider endpoint"
|
msgid "Provider endpoint"
|
||||||
msgstr "プロバイダーエンドポイント"
|
msgstr "プロバイダーエンドポイント"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:69
|
#: settings/serializers/auth/oidc.py:74
|
||||||
msgid "Provider jwks endpoint"
|
msgid "Provider jwks endpoint"
|
||||||
msgstr "プロバイダーjwksエンドポイント"
|
msgstr "プロバイダーjwksエンドポイント"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:78
|
#: settings/serializers/auth/oidc.py:83
|
||||||
msgid "Provider sign alg"
|
msgid "Provider sign alg"
|
||||||
msgstr "プロビダーサインalg"
|
msgstr "プロビダーサインalg"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:81
|
#: settings/serializers/auth/oidc.py:86
|
||||||
msgid "Provider sign key"
|
msgid "Provider sign key"
|
||||||
msgstr "プロバイダ署名キー"
|
msgstr "プロバイダ署名キー"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:83
|
#: settings/serializers/auth/oidc.py:88
|
||||||
msgid "Scopes"
|
msgid "Scopes"
|
||||||
msgstr "スコープ"
|
msgstr "スコープ"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:85
|
#: settings/serializers/auth/oidc.py:90
|
||||||
msgid "Id token max age"
|
msgid "Id token max age"
|
||||||
msgstr "IDトークンの最大年齢"
|
msgstr "IDトークンの最大年齢"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:88
|
#: settings/serializers/auth/oidc.py:93
|
||||||
msgid "Id token include claims"
|
msgid "Id token include claims"
|
||||||
msgstr "IDトークンにはクレームが含まれます"
|
msgstr "IDトークンにはクレームが含まれます"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:90
|
#: settings/serializers/auth/oidc.py:95
|
||||||
msgid "Use state"
|
msgid "Use state"
|
||||||
msgstr "使用状態"
|
msgstr "使用状態"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:91
|
#: settings/serializers/auth/oidc.py:96
|
||||||
msgid "Use nonce"
|
msgid "Use nonce"
|
||||||
msgstr "Nonceを使用"
|
msgstr "Nonceを使用"
|
||||||
|
|
||||||
@@ -4731,8 +4743,8 @@ msgstr "期限切れです。"
|
|||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" Your password has expired, please click <a href="
|
" Your password has expired, please click <a "
|
||||||
"\"%(user_password_update_url)s\"> this link </a> update password.\n"
|
"href=\"%(user_password_update_url)s\"> this link </a> update password.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
@@ -4753,34 +4765,34 @@ msgid ""
|
|||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
" クリックしてください <a href=\"%(user_password_update_url)s"
|
" クリックしてください <a "
|
||||||
"\"> リンク </a> パスワードの更新\n"
|
"href=\"%(user_password_update_url)s\"> リンク </a> パスワードの更新\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: templates/_message.html:43
|
#: templates/_message.html:43
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" Your information was incomplete. Please click <a href="
|
" Your information was incomplete. Please click <a "
|
||||||
"\"%(first_login_url)s\"> this link </a>to complete your information.\n"
|
"href=\"%(first_login_url)s\"> this link </a>to complete your information.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
" あなたの情報が不完全なので、クリックしてください。 <a href="
|
" あなたの情報が不完全なので、クリックしてください。 <a "
|
||||||
"\"%(first_login_url)s\"> リンク </a> 補完\n"
|
"href=\"%(first_login_url)s\"> リンク </a> 補完\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: templates/_message.html:56
|
#: templates/_message.html:56
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" Your ssh public key not set or expired. Please click <a href="
|
" Your ssh public key not set or expired. Please click <a "
|
||||||
"\"%(user_pubkey_update)s\"> this link </a>to update\n"
|
"href=\"%(user_pubkey_update)s\"> this link </a>to update\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
" SSHキーが設定されていないか無効になっている場合は、 <a href="
|
" SSHキーが設定されていないか無効になっている場合は、 <a "
|
||||||
"\"%(user_pubkey_update)s\"> リンク </a> 更新\n"
|
"href=\"%(user_pubkey_update)s\"> リンク </a> 更新\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: templates/_mfa_login_field.html:28
|
#: templates/_mfa_login_field.html:28
|
||||||
@@ -5000,7 +5012,7 @@ msgid "RDP Port"
|
|||||||
msgstr "RDP ポート"
|
msgstr "RDP ポート"
|
||||||
|
|
||||||
#: terminal/models/endpoint.py:27 terminal/models/endpoint.py:95
|
#: terminal/models/endpoint.py:27 terminal/models/endpoint.py:95
|
||||||
#: terminal/serializers/endpoint.py:57 terminal/serializers/storage.py:38
|
#: terminal/serializers/endpoint.py:61 terminal/serializers/storage.py:38
|
||||||
#: terminal/serializers/storage.py:50 terminal/serializers/storage.py:80
|
#: terminal/serializers/storage.py:50 terminal/serializers/storage.py:80
|
||||||
#: terminal/serializers/storage.py:90 terminal/serializers/storage.py:98
|
#: terminal/serializers/storage.py:90 terminal/serializers/storage.py:98
|
||||||
msgid "Endpoint"
|
msgid "Endpoint"
|
||||||
@@ -5038,23 +5050,23 @@ msgstr "リプレイ"
|
|||||||
msgid "Date end"
|
msgid "Date end"
|
||||||
msgstr "終了日"
|
msgstr "終了日"
|
||||||
|
|
||||||
#: terminal/models/session.py:261
|
#: terminal/models/session.py:265
|
||||||
msgid "Session record"
|
msgid "Session record"
|
||||||
msgstr "セッション記録"
|
msgstr "セッション記録"
|
||||||
|
|
||||||
#: terminal/models/session.py:263
|
#: terminal/models/session.py:267
|
||||||
msgid "Can monitor session"
|
msgid "Can monitor session"
|
||||||
msgstr "セッションを監視できます"
|
msgstr "セッションを監視できます"
|
||||||
|
|
||||||
#: terminal/models/session.py:264
|
#: terminal/models/session.py:268
|
||||||
msgid "Can share session"
|
msgid "Can share session"
|
||||||
msgstr "セッションを共有できます"
|
msgstr "セッションを共有できます"
|
||||||
|
|
||||||
#: terminal/models/session.py:265
|
#: terminal/models/session.py:269
|
||||||
msgid "Can terminate session"
|
msgid "Can terminate session"
|
||||||
msgstr "セッションを終了できます"
|
msgstr "セッションを終了できます"
|
||||||
|
|
||||||
#: terminal/models/session.py:266
|
#: terminal/models/session.py:270
|
||||||
msgid "Can validate session action perm"
|
msgid "Can validate session action perm"
|
||||||
msgstr "セッションアクションのパーマを検証できます"
|
msgstr "セッションアクションのパーマを検証できます"
|
||||||
|
|
||||||
@@ -5139,15 +5151,15 @@ msgstr "スレッド"
|
|||||||
msgid "Boot Time"
|
msgid "Boot Time"
|
||||||
msgstr "ブート時間"
|
msgstr "ブート時間"
|
||||||
|
|
||||||
#: terminal/models/storage.py:29
|
#: terminal/models/storage.py:28
|
||||||
msgid "Default storage"
|
msgid "Default storage"
|
||||||
msgstr "デフォルトのストレージ"
|
msgstr "デフォルトのストレージ"
|
||||||
|
|
||||||
#: terminal/models/storage.py:139 terminal/models/terminal.py:109
|
#: terminal/models/storage.py:141 terminal/models/terminal.py:109
|
||||||
msgid "Command storage"
|
msgid "Command storage"
|
||||||
msgstr "コマンドストレージ"
|
msgstr "コマンドストレージ"
|
||||||
|
|
||||||
#: terminal/models/storage.py:199 terminal/models/terminal.py:110
|
#: terminal/models/storage.py:201 terminal/models/terminal.py:110
|
||||||
msgid "Replay storage"
|
msgid "Replay storage"
|
||||||
msgstr "再生ストレージ"
|
msgstr "再生ストレージ"
|
||||||
|
|
||||||
@@ -5191,15 +5203,15 @@ msgstr "レベル"
|
|||||||
msgid "Batch danger command alert"
|
msgid "Batch danger command alert"
|
||||||
msgstr "一括危険コマンド警告"
|
msgstr "一括危険コマンド警告"
|
||||||
|
|
||||||
#: terminal/serializers/endpoint.py:14
|
#: terminal/serializers/endpoint.py:15
|
||||||
msgid "Magnus listen db port"
|
msgid "Magnus listen db port"
|
||||||
msgstr "Magnus がリッスンするデータベース ポート"
|
msgstr "Magnus がリッスンするデータベース ポート"
|
||||||
|
|
||||||
#: terminal/serializers/endpoint.py:17
|
#: terminal/serializers/endpoint.py:18
|
||||||
msgid "Magnus Listen port range"
|
msgid "Magnus Listen port range"
|
||||||
msgstr "Magnus がリッスンするポート範囲"
|
msgstr "Magnus がリッスンするポート範囲"
|
||||||
|
|
||||||
#: terminal/serializers/endpoint.py:19
|
#: terminal/serializers/endpoint.py:20
|
||||||
msgid ""
|
msgid ""
|
||||||
"The range of ports that Magnus listens on is modified in the configuration "
|
"The range of ports that Magnus listens on is modified in the configuration "
|
||||||
"file"
|
"file"
|
||||||
@@ -5207,10 +5219,15 @@ msgstr "Magnus がリッスンするポート範囲を構成ファイルで変
|
|||||||
|
|
||||||
#: terminal/serializers/endpoint.py:51
|
#: terminal/serializers/endpoint.py:51
|
||||||
msgid ""
|
msgid ""
|
||||||
"If asset IP addresses under different endpoints conflict, use asset labels"
|
"Format for comma-delimited string, with * indicating a match all. Such as: "
|
||||||
|
"192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:"
|
||||||
|
"db8:1a:1110::/64<br>If asset IP addresses under different endpoints "
|
||||||
|
"conflict, use asset labels"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"異なるエンドポイントの下に競合するアセットIPがある場合は、アセットタグを使用"
|
"コンマ区切り文字列の形式。* はすべて一致することを示します。例: "
|
||||||
"して実装します"
|
"192.168.10.1、192.168.1.0/24、10.1.1.1-10.1.1.20、2001:db8:2de::e13、2001:"
|
||||||
|
"db8:1a:1110:::/64<br>異なるエンドポイントの下に競合するアセットIPがある場合"
|
||||||
|
"は、アセットタグを使用して実装します"
|
||||||
|
|
||||||
#: terminal/serializers/session.py:15 terminal/serializers/session.py:42
|
#: terminal/serializers/session.py:15 terminal/serializers/session.py:42
|
||||||
msgid "Terminal display"
|
msgid "Terminal display"
|
||||||
@@ -5334,7 +5351,7 @@ msgstr "見つかりません"
|
|||||||
msgid "view"
|
msgid "view"
|
||||||
msgstr "表示"
|
msgstr "表示"
|
||||||
|
|
||||||
#: terminal/utils/db_port_mapper.py:65
|
#: terminal/utils/db_port_mapper.py:78
|
||||||
msgid ""
|
msgid ""
|
||||||
"No available port is matched. The number of databases may have exceeded the "
|
"No available port is matched. The number of databases may have exceeded the "
|
||||||
"number of ports open to the database agent service, Contact the "
|
"number of ports open to the database agent service, Contact the "
|
||||||
@@ -5344,7 +5361,7 @@ msgstr ""
|
|||||||
"サービスによって開かれたポートの数を超えた可能性があります。さらにポートを開"
|
"サービスによって開かれたポートの数を超えた可能性があります。さらにポートを開"
|
||||||
"くには、管理者に連絡してください。"
|
"くには、管理者に連絡してください。"
|
||||||
|
|
||||||
#: terminal/utils/db_port_mapper.py:91
|
#: terminal/utils/db_port_mapper.py:104
|
||||||
msgid ""
|
msgid ""
|
||||||
"No ports can be used, check and modify the limit on the number of ports that "
|
"No ports can be used, check and modify the limit on the number of ports that "
|
||||||
"Magnus listens on in the configuration file."
|
"Magnus listens on in the configuration file."
|
||||||
@@ -5352,7 +5369,7 @@ msgstr ""
|
|||||||
"使用できるポートがありません。設定ファイルで Magnus がリッスンするポート数の"
|
"使用できるポートがありません。設定ファイルで Magnus がリッスンするポート数の"
|
||||||
"制限を確認して変更してください. "
|
"制限を確認して変更してください. "
|
||||||
|
|
||||||
#: terminal/utils/db_port_mapper.py:93
|
#: terminal/utils/db_port_mapper.py:106
|
||||||
msgid "All available port count: {}, Already use port count: {}"
|
msgid "All available port count: {}, Already use port count: {}"
|
||||||
msgstr "使用可能なすべてのポート数: {}、すでに使用しているポート数: {}"
|
msgstr "使用可能なすべてのポート数: {}、すでに使用しているポート数: {}"
|
||||||
|
|
||||||
@@ -5444,19 +5461,19 @@ msgstr ""
|
|||||||
"チケットのタイトル: {} チケット申請者: {} チケットプロセッサ: {} チケットID: "
|
"チケットのタイトル: {} チケット申請者: {} チケットプロセッサ: {} チケットID: "
|
||||||
"{}"
|
"{}"
|
||||||
|
|
||||||
#: tickets/handlers/base.py:84
|
#: tickets/handlers/base.py:86
|
||||||
msgid "Change field"
|
msgid "Change field"
|
||||||
msgstr "フィールドを変更"
|
msgstr "フィールドを変更"
|
||||||
|
|
||||||
#: tickets/handlers/base.py:84
|
#: tickets/handlers/base.py:86
|
||||||
msgid "Before change"
|
msgid "Before change"
|
||||||
msgstr "変更前"
|
msgstr "変更前"
|
||||||
|
|
||||||
#: tickets/handlers/base.py:84
|
#: tickets/handlers/base.py:86
|
||||||
msgid "After change"
|
msgid "After change"
|
||||||
msgstr "変更後"
|
msgstr "変更後"
|
||||||
|
|
||||||
#: tickets/handlers/base.py:96
|
#: tickets/handlers/base.py:98
|
||||||
msgid "{} {} the ticket"
|
msgid "{} {} the ticket"
|
||||||
msgstr "{} {} チケット"
|
msgstr "{} {} チケット"
|
||||||
|
|
||||||
@@ -7071,3 +7088,9 @@ msgstr "究極のエディション"
|
|||||||
#: xpack/plugins/license/models.py:77
|
#: xpack/plugins/license/models.py:77
|
||||||
msgid "Community edition"
|
msgid "Community edition"
|
||||||
msgstr "コミュニティ版"
|
msgstr "コミュニティ版"
|
||||||
|
|
||||||
|
#~ msgid ""
|
||||||
|
#~ "If asset IP addresses under different endpoints conflict, use asset labels"
|
||||||
|
#~ msgstr ""
|
||||||
|
#~ "異なるエンドポイントの下に競合するアセットIPがある場合は、アセットタグを使"
|
||||||
|
#~ "用して実装します"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b143c62843946c3e18b623d05065f12e9d3c578efe5cd0d2016056d2b8448ae8
|
oid sha256:62e47d2577f103b524a1ae75e3357cdeb5f90d89f89c58a8c8fa8f63d67749f8
|
||||||
size 109495
|
size 110097
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2022-11-17 17:34+0800\n"
|
"POT-Creation-Date: 2023-05-08 18:10+0800\n"
|
||||||
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
|
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
|
||||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||||
"Language-Team: JumpServer team<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
|
#: acls/models/base.py:25 acls/serializers/login_asset_acl.py:47
|
||||||
#: applications/models/application.py:219 assets/models/asset.py:138
|
#: applications/models/application.py:219 assets/models/asset.py:138
|
||||||
#: assets/models/base.py:175 assets/models/cluster.py:18
|
#: assets/models/base.py:177 assets/models/cluster.py:18
|
||||||
#: assets/models/cmd_filter.py:27 assets/models/domain.py:23
|
#: 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
|
#: 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
|
#: orgs/models.py:70 perms/models/base.py:83 rbac/models/role.py:29
|
||||||
#: settings/models.py:33 settings/serializers/sms.py:6
|
#: settings/models.py:33 settings/serializers/sms.py:6
|
||||||
#: terminal/models/endpoint.py:14 terminal/models/endpoint.py:87
|
#: 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
|
#: terminal/models/terminal.py:101 users/forms/profile.py:33
|
||||||
#: users/models/group.py:15 users/models/user.py:673
|
#: users/models/group.py:15 users/models/user.py:673
|
||||||
#: xpack/plugins/cloud/models.py:27
|
#: xpack/plugins/cloud/models.py:27
|
||||||
@@ -54,14 +54,14 @@ msgstr "激活中"
|
|||||||
|
|
||||||
#: acls/models/base.py:32 applications/models/application.py:232
|
#: acls/models/base.py:32 applications/models/application.py:232
|
||||||
#: assets/models/asset.py:143 assets/models/asset.py:231
|
#: 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:182
|
||||||
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:52
|
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:52
|
||||||
#: assets/models/cmd_filter.py:100 assets/models/domain.py:24
|
#: assets/models/cmd_filter.py:100 assets/models/domain.py:24
|
||||||
#: assets/models/domain.py:65 assets/models/group.py:23
|
#: assets/models/domain.py:65 assets/models/group.py:23
|
||||||
#: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:73
|
#: 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
|
#: 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/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
|
#: tickets/models/comment.py:32 tickets/models/ticket/general.py:288
|
||||||
#: users/models/group.py:16 users/models/user.py:712
|
#: users/models/group.py:16 users/models/user.py:712
|
||||||
#: xpack/plugins/change_auth_plan/models/base.py:44
|
#: xpack/plugins/change_auth_plan/models/base.py:44
|
||||||
@@ -130,7 +130,7 @@ msgstr "系统用户"
|
|||||||
#: assets/models/asset.py:386 assets/models/authbook.py:19
|
#: assets/models/asset.py:386 assets/models/authbook.py:19
|
||||||
#: assets/models/backup.py:31 assets/models/cmd_filter.py:42
|
#: assets/models/backup.py:31 assets/models/cmd_filter.py:42
|
||||||
#: assets/models/gathered_user.py:14 assets/serializers/label.py:30
|
#: 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
|
#: authentication/models.py:66 authentication/models.py:90
|
||||||
#: perms/models/asset_permission.py:23 terminal/backends/command/models.py:21
|
#: perms/models/asset_permission.py:23 terminal/backends/command/models.py:21
|
||||||
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:47
|
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:47
|
||||||
@@ -154,7 +154,7 @@ msgid "Format for comma-delimited string, with * indicating a match all. "
|
|||||||
msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
|
msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
|
||||||
|
|
||||||
#: acls/serializers/login_acl.py:15 acls/serializers/login_asset_acl.py:17
|
#: 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:178
|
||||||
#: assets/models/gathered_user.py:15 audits/models.py:139
|
#: assets/models/gathered_user.py:15 audits/models.py:139
|
||||||
#: authentication/forms.py:25 authentication/forms.py:27
|
#: authentication/forms.py:25 authentication/forms.py:27
|
||||||
#: authentication/models.py:260
|
#: authentication/models.py:260
|
||||||
@@ -184,7 +184,7 @@ msgstr ""
|
|||||||
#: authentication/templates/authentication/_msg_oauth_bind.html:12
|
#: authentication/templates/authentication/_msg_oauth_bind.html:12
|
||||||
#: authentication/templates/authentication/_msg_rest_password_success.html:8
|
#: authentication/templates/authentication/_msg_rest_password_success.html:8
|
||||||
#: authentication/templates/authentication/_msg_rest_public_key_success.html:8
|
#: authentication/templates/authentication/_msg_rest_public_key_success.html:8
|
||||||
#: settings/serializers/terminal.py:10 terminal/serializers/endpoint.py:54
|
#: settings/serializers/terminal.py:10 terminal/serializers/endpoint.py:58
|
||||||
msgid "IP"
|
msgid "IP"
|
||||||
msgstr "IP"
|
msgstr "IP"
|
||||||
|
|
||||||
@@ -264,7 +264,7 @@ msgid "Application"
|
|||||||
msgstr "应用程序"
|
msgstr "应用程序"
|
||||||
|
|
||||||
#: applications/models/account.py:15 assets/models/authbook.py:20
|
#: 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
|
#: authentication/models.py:83 perms/models/application_permission.py:33
|
||||||
#: perms/models/asset_permission.py:25 terminal/backends/command/models.py:22
|
#: perms/models/asset_permission.py:25 terminal/backends/command/models.py:22
|
||||||
#: terminal/backends/command/serializers.py:36 terminal/models/session.py:49
|
#: terminal/backends/command/serializers.py:36 terminal/models/session.py:49
|
||||||
@@ -275,7 +275,7 @@ msgid "System user"
|
|||||||
msgstr "系统用户"
|
msgstr "系统用户"
|
||||||
|
|
||||||
#: applications/models/account.py:17 assets/models/authbook.py:21
|
#: 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"
|
msgid "Version"
|
||||||
msgstr "版本"
|
msgstr "版本"
|
||||||
|
|
||||||
@@ -305,7 +305,7 @@ msgstr "类别"
|
|||||||
#: assets/models/cmd_filter.py:86 assets/models/user.py:251
|
#: assets/models/cmd_filter.py:86 assets/models/user.py:251
|
||||||
#: authentication/models.py:70 perms/models/application_permission.py:24
|
#: authentication/models.py:70 perms/models/application_permission.py:24
|
||||||
#: perms/serializers/application/user_permission.py:34
|
#: 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/comment.py:26 tickets/models/flow.py:57
|
||||||
#: tickets/models/ticket/apply_application.py:18
|
#: tickets/models/ticket/apply_application.py:18
|
||||||
#: tickets/models/ticket/general.py:273
|
#: tickets/models/ticket/general.py:273
|
||||||
@@ -348,7 +348,7 @@ msgid "Type display"
|
|||||||
msgstr "类型名称"
|
msgstr "类型名称"
|
||||||
|
|
||||||
#: applications/serializers/application.py:105 assets/models/asset.py:230
|
#: applications/serializers/application.py:105 assets/models/asset.py:230
|
||||||
#: assets/models/base.py:181 assets/models/cluster.py:26
|
#: assets/models/base.py:183 assets/models/cluster.py:26
|
||||||
#: assets/models/cmd_filter.py:53 assets/models/domain.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/gathered_user.py:19 assets/models/group.py:22
|
||||||
#: assets/models/label.py:25 assets/serializers/account.py:18
|
#: assets/models/label.py:25 assets/serializers/account.py:18
|
||||||
@@ -362,7 +362,7 @@ msgstr "类型名称"
|
|||||||
msgid "Date created"
|
msgid "Date created"
|
||||||
msgstr "创建日期"
|
msgstr "创建日期"
|
||||||
|
|
||||||
#: applications/serializers/application.py:106 assets/models/base.py:182
|
#: applications/serializers/application.py:106 assets/models/base.py:184
|
||||||
#: assets/models/cmd_filter.py:54 assets/models/gathered_user.py:20
|
#: 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/account.py:21 assets/serializers/cmd_filter.py:29
|
||||||
#: assets/serializers/cmd_filter.py:49 common/db/models.py:117
|
#: assets/serializers/cmd_filter.py:49 common/db/models.py:117
|
||||||
@@ -440,7 +440,7 @@ msgid "Application path"
|
|||||||
msgstr "应用路径"
|
msgstr "应用路径"
|
||||||
|
|
||||||
#: applications/serializers/attrs/application_category/remote_app.py:44
|
#: 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/apply_application.py:38
|
||||||
#: tickets/serializers/ticket/common.py:59
|
#: tickets/serializers/ticket/common.py:59
|
||||||
#: xpack/plugins/change_auth_plan/serializers/asset.py:67
|
#: xpack/plugins/change_auth_plan/serializers/asset.py:67
|
||||||
@@ -627,7 +627,7 @@ msgid "Is active"
|
|||||||
msgstr "激活"
|
msgstr "激活"
|
||||||
|
|
||||||
#: assets/models/asset.py:222 assets/models/cluster.py:19
|
#: 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"
|
msgid "Admin user"
|
||||||
msgstr "特权用户"
|
msgstr "特权用户"
|
||||||
|
|
||||||
@@ -643,7 +643,7 @@ msgstr "资产编号"
|
|||||||
msgid "Labels"
|
msgid "Labels"
|
||||||
msgstr "标签管理"
|
msgstr "标签管理"
|
||||||
|
|
||||||
#: assets/models/asset.py:229 assets/models/base.py:183
|
#: assets/models/asset.py:229 assets/models/base.py:185
|
||||||
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:56
|
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:56
|
||||||
#: assets/models/cmd_filter.py:103 assets/models/group.py:21
|
#: 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
|
#: common/db/models.py:114 common/mixins/models.py:49 orgs/models.py:71
|
||||||
@@ -779,32 +779,32 @@ msgstr "是否成功"
|
|||||||
msgid "Account backup execution"
|
msgid "Account backup execution"
|
||||||
msgstr "账号备份执行"
|
msgstr "账号备份执行"
|
||||||
|
|
||||||
#: assets/models/base.py:30 assets/tasks/const.py:51 audits/const.py:5
|
#: assets/models/base.py:29 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/geoip/utils.py:31 common/utils/ip/geoip/utils.py:37
|
||||||
#: common/utils/ip/utils.py:84
|
#: common/utils/ip/utils.py:84
|
||||||
msgid "Unknown"
|
msgid "Unknown"
|
||||||
msgstr "未知"
|
msgstr "未知"
|
||||||
|
|
||||||
#: assets/models/base.py:31
|
#: assets/models/base.py:30
|
||||||
msgid "Ok"
|
msgid "Ok"
|
||||||
msgstr "成功"
|
msgstr "成功"
|
||||||
|
|
||||||
#: assets/models/base.py:32 audits/models.py:136
|
#: assets/models/base.py:31 audits/models.py:136
|
||||||
#: xpack/plugins/change_auth_plan/serializers/app.py:88
|
#: xpack/plugins/change_auth_plan/serializers/app.py:88
|
||||||
#: xpack/plugins/change_auth_plan/serializers/asset.py:199
|
#: xpack/plugins/change_auth_plan/serializers/asset.py:199
|
||||||
#: xpack/plugins/cloud/const.py:41
|
#: xpack/plugins/cloud/const.py:41
|
||||||
msgid "Failed"
|
msgid "Failed"
|
||||||
msgstr "失败"
|
msgstr "失败"
|
||||||
|
|
||||||
#: assets/models/base.py:38 assets/serializers/domain.py:47
|
#: assets/models/base.py:37 assets/serializers/domain.py:47
|
||||||
msgid "Connectivity"
|
msgid "Connectivity"
|
||||||
msgstr "可连接性"
|
msgstr "可连接性"
|
||||||
|
|
||||||
#: assets/models/base.py:40 authentication/models.py:263
|
#: assets/models/base.py:39 authentication/models.py:263
|
||||||
msgid "Date verified"
|
msgid "Date verified"
|
||||||
msgstr "校验日期"
|
msgstr "校验日期"
|
||||||
|
|
||||||
#: assets/models/base.py:177 assets/serializers/base.py:15
|
#: assets/models/base.py:179 assets/serializers/base.py:14
|
||||||
#: assets/serializers/base.py:37 assets/serializers/system_user.py:29
|
#: assets/serializers/base.py:37 assets/serializers/system_user.py:29
|
||||||
#: audits/signal_handlers.py:58 authentication/confirm/password.py:9
|
#: audits/signal_handlers.py:58 authentication/confirm/password.py:9
|
||||||
#: authentication/forms.py:32
|
#: authentication/forms.py:32
|
||||||
@@ -822,14 +822,14 @@ msgstr "校验日期"
|
|||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "密码"
|
msgstr "密码"
|
||||||
|
|
||||||
#: assets/models/base.py:178 assets/serializers/base.py:41
|
#: assets/models/base.py:180 assets/serializers/base.py:41
|
||||||
#: xpack/plugins/change_auth_plan/models/asset.py:53
|
#: 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:130
|
||||||
#: xpack/plugins/change_auth_plan/models/asset.py:206
|
#: xpack/plugins/change_auth_plan/models/asset.py:206
|
||||||
msgid "SSH private key"
|
msgid "SSH private key"
|
||||||
msgstr "SSH密钥"
|
msgstr "SSH密钥"
|
||||||
|
|
||||||
#: assets/models/base.py:179 xpack/plugins/change_auth_plan/models/asset.py:56
|
#: assets/models/base.py:181 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:126
|
||||||
#: xpack/plugins/change_auth_plan/models/asset.py:202
|
#: xpack/plugins/change_auth_plan/models/asset.py:202
|
||||||
msgid "SSH public key"
|
msgid "SSH public key"
|
||||||
@@ -882,7 +882,7 @@ msgstr "默认Cluster"
|
|||||||
msgid "User group"
|
msgid "User group"
|
||||||
msgstr "用户组"
|
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"
|
msgid "Command filter"
|
||||||
msgstr "命令过滤器"
|
msgstr "命令过滤器"
|
||||||
|
|
||||||
@@ -947,7 +947,7 @@ msgstr "测试网关"
|
|||||||
msgid "Unable to connect to port {port} on {ip}"
|
msgid "Unable to connect to port {port} on {ip}"
|
||||||
msgstr "无法连接到 {ip} 上的端口 {port}"
|
msgstr "无法连接到 {ip} 上的端口 {port}"
|
||||||
|
|
||||||
#: assets/models/domain.py:134 authentication/middleware.py:75
|
#: assets/models/domain.py:134 authentication/middleware.py:87
|
||||||
#: xpack/plugins/cloud/providers/fc.py:48
|
#: xpack/plugins/cloud/providers/fc.py:48
|
||||||
msgid "Authentication failed"
|
msgid "Authentication failed"
|
||||||
msgstr "认证失败"
|
msgstr "认证失败"
|
||||||
@@ -1008,7 +1008,7 @@ msgstr "全称"
|
|||||||
msgid "Parent key"
|
msgid "Parent key"
|
||||||
msgstr "ssh私钥"
|
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
|
#: xpack/plugins/cloud/models.py:95 xpack/plugins/cloud/serializers/task.py:75
|
||||||
msgid "Node"
|
msgid "Node"
|
||||||
msgstr "节点"
|
msgstr "节点"
|
||||||
@@ -1088,7 +1088,7 @@ msgstr "用户切换"
|
|||||||
msgid "Switch from"
|
msgid "Switch from"
|
||||||
msgstr "切换自"
|
msgstr "切换自"
|
||||||
|
|
||||||
#: assets/models/user.py:345
|
#: assets/models/user.py:356
|
||||||
msgid "Can match system user"
|
msgid "Can match system user"
|
||||||
msgstr "可以匹配系统用户"
|
msgstr "可以匹配系统用户"
|
||||||
|
|
||||||
@@ -1182,7 +1182,7 @@ msgstr "ssh私钥"
|
|||||||
msgid "Key password"
|
msgid "Key password"
|
||||||
msgstr "密钥密码"
|
msgstr "密钥密码"
|
||||||
|
|
||||||
#: assets/serializers/base.py:58
|
#: assets/serializers/base.py:58 assets/serializers/utils.py:24
|
||||||
msgid "private key invalid or passphrase error"
|
msgid "private key invalid or passphrase error"
|
||||||
msgstr "密钥不合法或密钥密码错误"
|
msgstr "密钥不合法或密钥密码错误"
|
||||||
|
|
||||||
@@ -1195,7 +1195,7 @@ msgid "Pattern"
|
|||||||
msgstr "模式"
|
msgstr "模式"
|
||||||
|
|
||||||
#: assets/serializers/domain.py:14 assets/serializers/label.py:12
|
#: 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
|
#: perms/serializers/asset/permission.py:49
|
||||||
msgid "Assets amount"
|
msgid "Assets amount"
|
||||||
msgstr "资产数量"
|
msgstr "资产数量"
|
||||||
@@ -1224,86 +1224,81 @@ msgstr "同级别节点名字不能重复"
|
|||||||
msgid "SSH key fingerprint"
|
msgid "SSH key fingerprint"
|
||||||
msgstr "密钥指纹"
|
msgstr "密钥指纹"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:40
|
#: assets/serializers/system_user.py:59
|
||||||
#: perms/serializers/application/permission.py:46
|
|
||||||
msgid "Apps amount"
|
|
||||||
msgstr "应用数量"
|
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:62
|
|
||||||
#: perms/serializers/asset/permission.py:50
|
#: perms/serializers/asset/permission.py:50
|
||||||
msgid "Nodes amount"
|
msgid "Nodes amount"
|
||||||
msgstr "节点数量"
|
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"
|
msgid "Login mode display"
|
||||||
msgstr "认证方式名称"
|
msgstr "认证方式名称"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:66
|
#: assets/serializers/system_user.py:63
|
||||||
msgid "Ad domain"
|
msgid "Ad domain"
|
||||||
msgstr "Ad 网域"
|
msgstr "Ad 网域"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:67
|
#: assets/serializers/system_user.py:64
|
||||||
msgid "Is asset protocol"
|
msgid "Is asset protocol"
|
||||||
msgstr "资产协议"
|
msgstr "资产协议"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:68
|
#: assets/serializers/system_user.py:65
|
||||||
msgid "Only ssh and automatic login system users are supported"
|
msgid "Only ssh and automatic login system users are supported"
|
||||||
msgstr "仅支持ssh协议和自动登录的系统用户"
|
msgstr "仅支持ssh协议和自动登录的系统用户"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:108
|
#: assets/serializers/system_user.py:105
|
||||||
msgid "Username same with user with protocol {} only allow 1"
|
msgid "Username same with user with protocol {} only allow 1"
|
||||||
msgstr "用户名和用户相同的一种协议只允许存在一个"
|
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"
|
msgid "Special char not allowed"
|
||||||
msgstr "不能包含特殊字符"
|
msgstr "不能包含特殊字符"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:131
|
#: assets/serializers/system_user.py:128
|
||||||
msgid "* Automatic login mode must fill in the username."
|
msgid "* Automatic login mode must fill in the username."
|
||||||
msgstr "自动登录模式,必须填写用户名"
|
msgstr "自动登录模式,必须填写用户名"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:146
|
#: assets/serializers/system_user.py:143
|
||||||
msgid "Path should starts with /"
|
msgid "Path should starts with /"
|
||||||
msgstr "路径应该以 / 开头"
|
msgstr "路径应该以 / 开头"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:158
|
#: assets/serializers/system_user.py:155
|
||||||
msgid "Password or private key required"
|
msgid "Password or private key required"
|
||||||
msgstr "密码或密钥密码需要一个"
|
msgstr "密码或密钥密码需要一个"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:172
|
#: assets/serializers/system_user.py:169
|
||||||
msgid "Only ssh protocol system users are allowed"
|
msgid "Only ssh protocol system users are allowed"
|
||||||
msgstr "仅允许ssh协议的系统用户"
|
msgstr "仅允许ssh协议的系统用户"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:176
|
#: assets/serializers/system_user.py:173
|
||||||
msgid "The protocol must be consistent with the current user: {}"
|
msgid "The protocol must be consistent with the current user: {}"
|
||||||
msgstr "协议必须和当前用户保持一致: {}"
|
msgstr "协议必须和当前用户保持一致: {}"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:180
|
#: assets/serializers/system_user.py:177
|
||||||
msgid "Only system users with automatic login are allowed"
|
msgid "Only system users with automatic login are allowed"
|
||||||
msgstr "仅允许自动登录的系统用户"
|
msgstr "仅允许自动登录的系统用户"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:288
|
#: assets/serializers/system_user.py:284
|
||||||
msgid "System user name"
|
msgid "System user name"
|
||||||
msgstr "系统用户名称"
|
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
|
#: rbac/serializers/rolebinding.py:23
|
||||||
msgid "Org name"
|
msgid "Org name"
|
||||||
msgstr "组织名称"
|
msgstr "组织名称"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:298
|
#: assets/serializers/system_user.py:294
|
||||||
msgid "Asset hostname"
|
msgid "Asset hostname"
|
||||||
msgstr "资产主机名"
|
msgstr "资产主机名"
|
||||||
|
|
||||||
#: assets/serializers/utils.py:11
|
#: assets/serializers/utils.py:13
|
||||||
msgid "Password can not contains `{{` "
|
msgid "Password can not contains `{{` "
|
||||||
msgstr "密码不能包含 `{{` 字符"
|
msgstr "密码不能包含 `{{` 字符"
|
||||||
|
|
||||||
#: assets/serializers/utils.py:14
|
#: assets/serializers/utils.py:16
|
||||||
msgid "Password can not contains `'` "
|
msgid "Password can not contains `'` "
|
||||||
msgstr "密码不能包含 `'` 字符"
|
msgstr "密码不能包含 `'` 字符"
|
||||||
|
|
||||||
#: assets/serializers/utils.py:16
|
#: assets/serializers/utils.py:18
|
||||||
msgid "Password can not contains `\"` "
|
msgid "Password can not contains `\"` "
|
||||||
msgstr "密码不能包含 `\"` 字符"
|
msgstr "密码不能包含 `\"` 字符"
|
||||||
|
|
||||||
@@ -1415,6 +1410,10 @@ msgid "For security, do not push user {}"
|
|||||||
msgstr "为了安全,禁止推送用户 {}"
|
msgstr "为了安全,禁止推送用户 {}"
|
||||||
|
|
||||||
#: assets/tasks/utils.py:55
|
#: 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"
|
msgid "No assets matched, stop task"
|
||||||
msgstr "没有匹配到资产,结束任务"
|
msgstr "没有匹配到资产,结束任务"
|
||||||
|
|
||||||
@@ -1650,7 +1649,8 @@ msgstr "企业微信"
|
|||||||
|
|
||||||
#: audits/signal_handlers.py:62 authentication/views/feishu.py:144
|
#: audits/signal_handlers.py:62 authentication/views/feishu.py:144
|
||||||
#: authentication/views/login.py:85 notifications/backends/__init__.py:14
|
#: 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"
|
msgid "FeiShu"
|
||||||
msgstr "飞书"
|
msgstr "飞书"
|
||||||
|
|
||||||
@@ -1706,7 +1706,7 @@ msgid "Authentication"
|
|||||||
msgstr "认证"
|
msgstr "认证"
|
||||||
|
|
||||||
#: authentication/backends/custom.py:58
|
#: authentication/backends/custom.py:58
|
||||||
#: authentication/backends/oauth2/backends.py:158 authentication/models.py:158
|
#: authentication/backends/oauth2/backends.py:167 authentication/models.py:158
|
||||||
msgid "User invalid, disabled or expired"
|
msgid "User invalid, disabled or expired"
|
||||||
msgstr "用户无效,已禁用或已过期"
|
msgstr "用户无效,已禁用或已过期"
|
||||||
|
|
||||||
@@ -1913,15 +1913,15 @@ msgstr "没有绑定飞书"
|
|||||||
msgid "Your password is invalid"
|
msgid "Your password is invalid"
|
||||||
msgstr "您的密码无效"
|
msgstr "您的密码无效"
|
||||||
|
|
||||||
#: authentication/errors/redirect.py:85 authentication/mixins.py:306
|
#: authentication/errors/redirect.py:85 authentication/mixins.py:307
|
||||||
msgid "Your password is too simple, please change it for security"
|
msgid "Your password is too simple, please change it for security"
|
||||||
msgstr "你的密码过于简单,为了安全,请修改"
|
msgstr "你的密码过于简单,为了安全,请修改"
|
||||||
|
|
||||||
#: authentication/errors/redirect.py:93 authentication/mixins.py:313
|
#: authentication/errors/redirect.py:93 authentication/mixins.py:314
|
||||||
msgid "You should to change your password before login"
|
msgid "You should to change your password before login"
|
||||||
msgstr "登录完成前,请先修改密码"
|
msgstr "登录完成前,请先修改密码"
|
||||||
|
|
||||||
#: authentication/errors/redirect.py:101 authentication/mixins.py:320
|
#: authentication/errors/redirect.py:101 authentication/mixins.py:321
|
||||||
msgid "Your password has expired, please reset before logging in"
|
msgid "Your password has expired, please reset before logging in"
|
||||||
msgstr "您的密码已过期,先修改再登录"
|
msgstr "您的密码已过期,先修改再登录"
|
||||||
|
|
||||||
@@ -2018,15 +2018,15 @@ msgstr "设置手机号码启用"
|
|||||||
msgid "Clear phone number to disable"
|
msgid "Clear phone number to disable"
|
||||||
msgstr "清空手机号码禁用"
|
msgstr "清空手机号码禁用"
|
||||||
|
|
||||||
#: authentication/middleware.py:76 settings/utils/ldap.py:652
|
#: authentication/middleware.py:88 settings/utils/ldap.py:652
|
||||||
msgid "Authentication failed (before login check failed): {}"
|
msgid "Authentication failed (before login check failed): {}"
|
||||||
msgstr "认证失败(登录前检查失败): {}"
|
msgstr "认证失败(登录前检查失败): {}"
|
||||||
|
|
||||||
#: authentication/mixins.py:256
|
#: authentication/mixins.py:257
|
||||||
msgid "The MFA type ({}) is not enabled"
|
msgid "The MFA type ({}) is not enabled"
|
||||||
msgstr "该 MFA ({}) 方式没有启用"
|
msgstr "该 MFA ({}) 方式没有启用"
|
||||||
|
|
||||||
#: authentication/mixins.py:296
|
#: authentication/mixins.py:297
|
||||||
msgid "Please change your password"
|
msgid "Please change your password"
|
||||||
msgstr "请修改密码"
|
msgstr "请修改密码"
|
||||||
|
|
||||||
@@ -2233,7 +2233,7 @@ msgstr "代码错误"
|
|||||||
#: authentication/templates/authentication/_msg_reset_password_code.html:9
|
#: authentication/templates/authentication/_msg_reset_password_code.html:9
|
||||||
#: authentication/templates/authentication/_msg_rest_password_success.html:2
|
#: authentication/templates/authentication/_msg_rest_password_success.html:2
|
||||||
#: authentication/templates/authentication/_msg_rest_public_key_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:417 ops/tasks.py:145 ops/tasks.py:148
|
||||||
#: perms/templates/perms/_msg_item_permissions_expire.html:3
|
#: perms/templates/perms/_msg_item_permissions_expire.html:3
|
||||||
#: perms/templates/perms/_msg_permed_items_expire.html:3
|
#: perms/templates/perms/_msg_permed_items_expire.html:3
|
||||||
#: tickets/templates/tickets/approve_check_password.html:33
|
#: tickets/templates/tickets/approve_check_password.html:33
|
||||||
@@ -2708,11 +2708,11 @@ msgstr "不能包含特殊字符"
|
|||||||
msgid "The mobile phone number format is incorrect"
|
msgid "The mobile phone number format is incorrect"
|
||||||
msgstr "手机号格式不正确"
|
msgstr "手机号格式不正确"
|
||||||
|
|
||||||
#: jumpserver/conf.py:412
|
#: jumpserver/conf.py:416
|
||||||
msgid "Create account successfully"
|
msgid "Create account successfully"
|
||||||
msgstr "创建账号成功"
|
msgstr "创建账号成功"
|
||||||
|
|
||||||
#: jumpserver/conf.py:414
|
#: jumpserver/conf.py:418
|
||||||
msgid "Your account has been created successfully"
|
msgid "Your account has been created successfully"
|
||||||
msgstr "你的账号已创建成功"
|
msgstr "你的账号已创建成功"
|
||||||
|
|
||||||
@@ -3153,6 +3153,10 @@ msgstr "用户组数量"
|
|||||||
msgid "System users amount"
|
msgid "System users amount"
|
||||||
msgstr "系统用户数量"
|
msgstr "系统用户数量"
|
||||||
|
|
||||||
|
#: perms/serializers/application/permission.py:46
|
||||||
|
msgid "Apps amount"
|
||||||
|
msgstr "应用数量"
|
||||||
|
|
||||||
#: perms/serializers/application/permission.py:79
|
#: perms/serializers/application/permission.py:79
|
||||||
msgid ""
|
msgid ""
|
||||||
"The application list contains applications that are different from the "
|
"The application list contains applications that are different from the "
|
||||||
@@ -3555,7 +3559,7 @@ msgstr "CAS"
|
|||||||
msgid "Enable CAS Auth"
|
msgid "Enable CAS Auth"
|
||||||
msgstr "启用 CAS 认证"
|
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"
|
msgid "Server url"
|
||||||
msgstr "服务端地址"
|
msgstr "服务端地址"
|
||||||
|
|
||||||
@@ -3588,7 +3592,7 @@ msgstr "创建用户(如果不存在)"
|
|||||||
msgid "Enable DingTalk Auth"
|
msgid "Enable DingTalk Auth"
|
||||||
msgstr "启用钉钉认证"
|
msgstr "启用钉钉认证"
|
||||||
|
|
||||||
#: settings/serializers/auth/feishu.py:14
|
#: settings/serializers/auth/feishu.py:16
|
||||||
msgid "Enable FeiShu Auth"
|
msgid "Enable FeiShu Auth"
|
||||||
msgstr "启用飞书认证"
|
msgstr "启用飞书认证"
|
||||||
|
|
||||||
@@ -3675,11 +3679,11 @@ msgstr "客户端 ID"
|
|||||||
msgid "Client Secret"
|
msgid "Client Secret"
|
||||||
msgstr "客户端密钥"
|
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"
|
msgid "Provider auth endpoint"
|
||||||
msgstr "授权端点地址"
|
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"
|
msgid "Provider token endpoint"
|
||||||
msgstr "token 端点地址"
|
msgstr "token 端点地址"
|
||||||
|
|
||||||
@@ -3687,15 +3691,15 @@ msgstr "token 端点地址"
|
|||||||
msgid "Client authentication method"
|
msgid "Client authentication method"
|
||||||
msgstr "客户端认证方式"
|
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"
|
msgid "Provider userinfo endpoint"
|
||||||
msgstr "用户信息端点地址"
|
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"
|
msgid "Provider end session endpoint"
|
||||||
msgstr "注销会话端点地址"
|
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
|
#: settings/serializers/auth/saml2.py:35
|
||||||
msgid "Always update user"
|
msgid "Always update user"
|
||||||
msgstr "总是更新用户信息"
|
msgstr "总是更新用户信息"
|
||||||
@@ -3724,51 +3728,59 @@ msgstr ""
|
|||||||
"用户属性映射代表怎样将OpenID中用户属性映射到jumpserver用户上,username, name,"
|
"用户属性映射代表怎样将OpenID中用户属性映射到jumpserver用户上,username, name,"
|
||||||
"email 是jumpserver的用户需要属性"
|
"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"
|
msgid "Use Keycloak"
|
||||||
msgstr "使用 Keycloak"
|
msgstr "使用 Keycloak"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:52
|
#: settings/serializers/auth/oidc.py:57
|
||||||
msgid "Realm name"
|
msgid "Realm name"
|
||||||
msgstr "域"
|
msgstr "域"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:58
|
#: settings/serializers/auth/oidc.py:63
|
||||||
msgid "Enable OPENID Auth"
|
msgid "Enable OPENID Auth"
|
||||||
msgstr "启用 OIDC 认证"
|
msgstr "启用 OIDC 认证"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:60
|
#: settings/serializers/auth/oidc.py:65
|
||||||
msgid "Provider endpoint"
|
msgid "Provider endpoint"
|
||||||
msgstr "端点地址"
|
msgstr "端点地址"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:69
|
#: settings/serializers/auth/oidc.py:74
|
||||||
msgid "Provider jwks endpoint"
|
msgid "Provider jwks endpoint"
|
||||||
msgstr "jwks 端点地址"
|
msgstr "jwks 端点地址"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:78
|
#: settings/serializers/auth/oidc.py:83
|
||||||
msgid "Provider sign alg"
|
msgid "Provider sign alg"
|
||||||
msgstr "签名算法"
|
msgstr "签名算法"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:81
|
#: settings/serializers/auth/oidc.py:86
|
||||||
msgid "Provider sign key"
|
msgid "Provider sign key"
|
||||||
msgstr "签名 Key"
|
msgstr "签名 Key"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:83
|
#: settings/serializers/auth/oidc.py:88
|
||||||
msgid "Scopes"
|
msgid "Scopes"
|
||||||
msgstr "连接范围"
|
msgstr "连接范围"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:85
|
#: settings/serializers/auth/oidc.py:90
|
||||||
msgid "Id token max age"
|
msgid "Id token max age"
|
||||||
msgstr "令牌有效时间"
|
msgstr "令牌有效时间"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:88
|
#: settings/serializers/auth/oidc.py:93
|
||||||
msgid "Id token include claims"
|
msgid "Id token include claims"
|
||||||
msgstr "声明"
|
msgstr "声明"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:90
|
#: settings/serializers/auth/oidc.py:95
|
||||||
msgid "Use state"
|
msgid "Use state"
|
||||||
msgstr "使用状态"
|
msgstr "使用状态"
|
||||||
|
|
||||||
#: settings/serializers/auth/oidc.py:91
|
#: settings/serializers/auth/oidc.py:96
|
||||||
msgid "Use nonce"
|
msgid "Use nonce"
|
||||||
msgstr "临时使用"
|
msgstr "临时使用"
|
||||||
|
|
||||||
@@ -4652,13 +4664,13 @@ msgstr "过期。"
|
|||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" Your password has expired, please click <a href="
|
" Your password has expired, please click <a "
|
||||||
"\"%(user_password_update_url)s\"> this link </a> update password.\n"
|
"href=\"%(user_password_update_url)s\"> this link </a> update password.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
" 您的密码已经过期,请点击 <a href="
|
" 您的密码已经过期,请点击 <a "
|
||||||
"\"%(user_password_update_url)s\"> 链接 </a> 更新密码\n"
|
"href=\"%(user_password_update_url)s\"> 链接 </a> 更新密码\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: templates/_message.html:30
|
#: templates/_message.html:30
|
||||||
@@ -4682,8 +4694,8 @@ msgstr ""
|
|||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" Your information was incomplete. Please click <a href="
|
" Your information was incomplete. Please click <a "
|
||||||
"\"%(first_login_url)s\"> this link </a>to complete your information.\n"
|
"href=\"%(first_login_url)s\"> this link </a>to complete your information.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
@@ -4695,13 +4707,13 @@ msgstr ""
|
|||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" Your ssh public key not set or expired. Please click <a href="
|
" Your ssh public key not set or expired. Please click <a "
|
||||||
"\"%(user_pubkey_update)s\"> this link </a>to update\n"
|
"href=\"%(user_pubkey_update)s\"> this link </a>to update\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
" 您的SSH密钥没有设置或已失效,请点击 <a href="
|
" 您的SSH密钥没有设置或已失效,请点击 <a "
|
||||||
"\"%(user_pubkey_update)s\"> 链接 </a> 更新\n"
|
"href=\"%(user_pubkey_update)s\"> 链接 </a> 更新\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: templates/_mfa_login_field.html:28
|
#: templates/_mfa_login_field.html:28
|
||||||
@@ -4914,7 +4926,7 @@ msgid "RDP Port"
|
|||||||
msgstr "RDP 端口"
|
msgstr "RDP 端口"
|
||||||
|
|
||||||
#: terminal/models/endpoint.py:27 terminal/models/endpoint.py:95
|
#: terminal/models/endpoint.py:27 terminal/models/endpoint.py:95
|
||||||
#: terminal/serializers/endpoint.py:57 terminal/serializers/storage.py:38
|
#: terminal/serializers/endpoint.py:61 terminal/serializers/storage.py:38
|
||||||
#: terminal/serializers/storage.py:50 terminal/serializers/storage.py:80
|
#: terminal/serializers/storage.py:50 terminal/serializers/storage.py:80
|
||||||
#: terminal/serializers/storage.py:90 terminal/serializers/storage.py:98
|
#: terminal/serializers/storage.py:90 terminal/serializers/storage.py:98
|
||||||
msgid "Endpoint"
|
msgid "Endpoint"
|
||||||
@@ -4952,23 +4964,23 @@ msgstr "回放"
|
|||||||
msgid "Date end"
|
msgid "Date end"
|
||||||
msgstr "结束日期"
|
msgstr "结束日期"
|
||||||
|
|
||||||
#: terminal/models/session.py:261
|
#: terminal/models/session.py:265
|
||||||
msgid "Session record"
|
msgid "Session record"
|
||||||
msgstr "会话记录"
|
msgstr "会话记录"
|
||||||
|
|
||||||
#: terminal/models/session.py:263
|
#: terminal/models/session.py:267
|
||||||
msgid "Can monitor session"
|
msgid "Can monitor session"
|
||||||
msgstr "可以监控会话"
|
msgstr "可以监控会话"
|
||||||
|
|
||||||
#: terminal/models/session.py:264
|
#: terminal/models/session.py:268
|
||||||
msgid "Can share session"
|
msgid "Can share session"
|
||||||
msgstr "可以分享会话"
|
msgstr "可以分享会话"
|
||||||
|
|
||||||
#: terminal/models/session.py:265
|
#: terminal/models/session.py:269
|
||||||
msgid "Can terminate session"
|
msgid "Can terminate session"
|
||||||
msgstr "可以终断会话"
|
msgstr "可以终断会话"
|
||||||
|
|
||||||
#: terminal/models/session.py:266
|
#: terminal/models/session.py:270
|
||||||
msgid "Can validate session action perm"
|
msgid "Can validate session action perm"
|
||||||
msgstr "可以验证会话动作权限"
|
msgstr "可以验证会话动作权限"
|
||||||
|
|
||||||
@@ -5053,15 +5065,15 @@ msgstr "线程数"
|
|||||||
msgid "Boot Time"
|
msgid "Boot Time"
|
||||||
msgstr "运行时间"
|
msgstr "运行时间"
|
||||||
|
|
||||||
#: terminal/models/storage.py:29
|
#: terminal/models/storage.py:28
|
||||||
msgid "Default storage"
|
msgid "Default storage"
|
||||||
msgstr "默认存储"
|
msgstr "默认存储"
|
||||||
|
|
||||||
#: terminal/models/storage.py:139 terminal/models/terminal.py:109
|
#: terminal/models/storage.py:141 terminal/models/terminal.py:109
|
||||||
msgid "Command storage"
|
msgid "Command storage"
|
||||||
msgstr "命令存储"
|
msgstr "命令存储"
|
||||||
|
|
||||||
#: terminal/models/storage.py:199 terminal/models/terminal.py:110
|
#: terminal/models/storage.py:201 terminal/models/terminal.py:110
|
||||||
msgid "Replay storage"
|
msgid "Replay storage"
|
||||||
msgstr "录像存储"
|
msgstr "录像存储"
|
||||||
|
|
||||||
@@ -5105,15 +5117,15 @@ msgstr "级别"
|
|||||||
msgid "Batch danger command alert"
|
msgid "Batch danger command alert"
|
||||||
msgstr "批量危险命令告警"
|
msgstr "批量危险命令告警"
|
||||||
|
|
||||||
#: terminal/serializers/endpoint.py:14
|
#: terminal/serializers/endpoint.py:15
|
||||||
msgid "Magnus listen db port"
|
msgid "Magnus listen db port"
|
||||||
msgstr "Magnus 监听的数据库端口"
|
msgstr "Magnus 监听的数据库端口"
|
||||||
|
|
||||||
#: terminal/serializers/endpoint.py:17
|
#: terminal/serializers/endpoint.py:18
|
||||||
msgid "Magnus Listen port range"
|
msgid "Magnus Listen port range"
|
||||||
msgstr "Magnus 监听的端口范围"
|
msgstr "Magnus 监听的端口范围"
|
||||||
|
|
||||||
#: terminal/serializers/endpoint.py:19
|
#: terminal/serializers/endpoint.py:20
|
||||||
msgid ""
|
msgid ""
|
||||||
"The range of ports that Magnus listens on is modified in the configuration "
|
"The range of ports that Magnus listens on is modified in the configuration "
|
||||||
"file"
|
"file"
|
||||||
@@ -5121,8 +5133,14 @@ msgstr "请在配置文件中修改 Magnus 监听的端口范围"
|
|||||||
|
|
||||||
#: terminal/serializers/endpoint.py:51
|
#: terminal/serializers/endpoint.py:51
|
||||||
msgid ""
|
msgid ""
|
||||||
"If asset IP addresses under different endpoints conflict, use asset labels"
|
"Format for comma-delimited string, with * indicating a match all. Such as: "
|
||||||
msgstr "如果不同端点下的资产 IP 有冲突,使用资产标签实现"
|
"192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:"
|
||||||
|
"db8:1a:1110::/64<br>If asset IP addresses under different endpoints "
|
||||||
|
"conflict, use asset labels"
|
||||||
|
msgstr ""
|
||||||
|
"格式为逗号分隔的字符串, * 表示匹配所有。例如: 192.168.10.1, 192.168.1.0/24, "
|
||||||
|
"10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64<br>如果不同端点"
|
||||||
|
"下的资产 IP 有冲突,使用资产标签实现"
|
||||||
|
|
||||||
#: terminal/serializers/session.py:15 terminal/serializers/session.py:42
|
#: terminal/serializers/session.py:15 terminal/serializers/session.py:42
|
||||||
msgid "Terminal display"
|
msgid "Terminal display"
|
||||||
@@ -5246,7 +5264,7 @@ msgstr "没有发现"
|
|||||||
msgid "view"
|
msgid "view"
|
||||||
msgstr "查看"
|
msgstr "查看"
|
||||||
|
|
||||||
#: terminal/utils/db_port_mapper.py:65
|
#: terminal/utils/db_port_mapper.py:78
|
||||||
msgid ""
|
msgid ""
|
||||||
"No available port is matched. The number of databases may have exceeded the "
|
"No available port is matched. The number of databases may have exceeded the "
|
||||||
"number of ports open to the database agent service, Contact the "
|
"number of ports open to the database agent service, Contact the "
|
||||||
@@ -5255,13 +5273,13 @@ msgstr ""
|
|||||||
"未匹配到可用端口,数据库的数量可能已经超过数据库代理服务开放的端口数量,请联"
|
"未匹配到可用端口,数据库的数量可能已经超过数据库代理服务开放的端口数量,请联"
|
||||||
"系管理员开放更多端口。"
|
"系管理员开放更多端口。"
|
||||||
|
|
||||||
#: terminal/utils/db_port_mapper.py:91
|
#: terminal/utils/db_port_mapper.py:104
|
||||||
msgid ""
|
msgid ""
|
||||||
"No ports can be used, check and modify the limit on the number of ports that "
|
"No ports can be used, check and modify the limit on the number of ports that "
|
||||||
"Magnus listens on in the configuration file."
|
"Magnus listens on in the configuration file."
|
||||||
msgstr "没有端口可以使用,检查并修改配置文件中 Magnus 监听的端口数量限制。"
|
msgstr "没有端口可以使用,检查并修改配置文件中 Magnus 监听的端口数量限制。"
|
||||||
|
|
||||||
#: terminal/utils/db_port_mapper.py:93
|
#: terminal/utils/db_port_mapper.py:106
|
||||||
msgid "All available port count: {}, Already use port count: {}"
|
msgid "All available port count: {}, Already use port count: {}"
|
||||||
msgstr "所有可用端口数量:{},已使用端口数量:{}"
|
msgstr "所有可用端口数量:{},已使用端口数量:{}"
|
||||||
|
|
||||||
@@ -5351,19 +5369,19 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"通过工单创建, 工单标题: {}, 工单申请人: {}, 工单处理人: {}, 工单 ID: {}"
|
"通过工单创建, 工单标题: {}, 工单申请人: {}, 工单处理人: {}, 工单 ID: {}"
|
||||||
|
|
||||||
#: tickets/handlers/base.py:84
|
#: tickets/handlers/base.py:86
|
||||||
msgid "Change field"
|
msgid "Change field"
|
||||||
msgstr "变更字段"
|
msgstr "变更字段"
|
||||||
|
|
||||||
#: tickets/handlers/base.py:84
|
#: tickets/handlers/base.py:86
|
||||||
msgid "Before change"
|
msgid "Before change"
|
||||||
msgstr "变更前"
|
msgstr "变更前"
|
||||||
|
|
||||||
#: tickets/handlers/base.py:84
|
#: tickets/handlers/base.py:86
|
||||||
msgid "After change"
|
msgid "After change"
|
||||||
msgstr "变更后"
|
msgstr "变更后"
|
||||||
|
|
||||||
#: tickets/handlers/base.py:96
|
#: tickets/handlers/base.py:98
|
||||||
msgid "{} {} the ticket"
|
msgid "{} {} the ticket"
|
||||||
msgstr "{} {} 工单"
|
msgstr "{} {} 工单"
|
||||||
|
|
||||||
@@ -6961,3 +6979,7 @@ msgstr "旗舰版"
|
|||||||
#: xpack/plugins/license/models.py:77
|
#: xpack/plugins/license/models.py:77
|
||||||
msgid "Community edition"
|
msgid "Community edition"
|
||||||
msgstr "社区版"
|
msgstr "社区版"
|
||||||
|
|
||||||
|
#~ msgid ""
|
||||||
|
#~ "If asset IP addresses under different endpoints conflict, use asset labels"
|
||||||
|
#~ msgstr "如果不同端点下的资产 IP 有冲突,使用资产标签实现"
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ class CeleryResultApi(generics.RetrieveAPIView):
|
|||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
pk = self.kwargs.get('pk')
|
pk = self.kwargs.get('pk')
|
||||||
return AsyncResult(pk)
|
return AsyncResult(str(pk))
|
||||||
|
|
||||||
|
|
||||||
class CeleryPeriodTaskViewSet(CommonApiMixin, viewsets.ModelViewSet):
|
class CeleryPeriodTaskViewSet(CommonApiMixin, viewsets.ModelViewSet):
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from .ansible.inventory import BaseInventory
|
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
from .ansible.inventory import BaseInventory
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'JMSInventory', 'JMSCustomInventory',
|
'JMSInventory', 'JMSCustomInventory',
|
||||||
@@ -110,7 +110,10 @@ class JMSInventory(JMSBaseInventory):
|
|||||||
|
|
||||||
if self.system_user:
|
if self.system_user:
|
||||||
self.system_user.load_asset_special_auth(asset=asset, username=self.run_as)
|
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:
|
else:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,13 @@ __all__ = ['FeiShuSettingSerializer']
|
|||||||
class FeiShuSettingSerializer(serializers.Serializer):
|
class FeiShuSettingSerializer(serializers.Serializer):
|
||||||
PREFIX_TITLE = '%s-%s' % (_('Authentication'), _('FeiShu'))
|
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_ID = serializers.CharField(max_length=256, required=True, label='App ID')
|
||||||
FEISHU_APP_SECRET = EncryptedField(max_length=256, required=False, label='App Secret')
|
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')
|
||||||
|
)
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ class CommonSettingSerializer(serializers.Serializer):
|
|||||||
help_text=_('User attr map present how to map OpenID user attr to '
|
help_text=_('User attr map present how to map OpenID user attr to '
|
||||||
'jumpserver, username,name,email is jumpserver attr')
|
'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):
|
class KeycloakSettingSerializer(CommonSettingSerializer):
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class Endpoint(JMSModel):
|
|||||||
return None
|
return None
|
||||||
endpoints = cls.objects.filter(name__in=values).order_by('-date_updated')
|
endpoints = cls.objects.filter(name__in=values).order_by('-date_updated')
|
||||||
for endpoint in endpoints:
|
for endpoint in endpoints:
|
||||||
if endpoint.is_valid_for(protocol):
|
if endpoint.is_valid_for(instance, protocol):
|
||||||
return endpoint
|
return endpoint
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -118,9 +118,13 @@ class Session(OrgModelMixin):
|
|||||||
def find_ok_relative_path_in_storage(self, storage):
|
def find_ok_relative_path_in_storage(self, storage):
|
||||||
session_paths = self.get_all_possible_relative_path()
|
session_paths = self.get_all_possible_relative_path()
|
||||||
for rel_path in session_paths:
|
for rel_path in session_paths:
|
||||||
if storage.exists(rel_path):
|
# storage 为多个外部存储时, 可能会因部分不可用,
|
||||||
return rel_path
|
# 抛出异常, 影响录像的获取
|
||||||
|
try:
|
||||||
|
if storage.exists(rel_path):
|
||||||
|
return rel_path
|
||||||
|
except:
|
||||||
|
pass
|
||||||
@property
|
@property
|
||||||
def asset_obj(self):
|
def asset_obj(self):
|
||||||
return Asset.objects.get(id=self.asset_id)
|
return Asset.objects.get(id=self.asset_id)
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ from .terminal import Terminal
|
|||||||
from .command import Command
|
from .command import Command
|
||||||
from .. import const
|
from .. import const
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
@@ -37,10 +36,10 @@ class CommonStorageModelMixin(models.Model):
|
|||||||
|
|
||||||
def set_to_default(self):
|
def set_to_default(self):
|
||||||
self.is_default = True
|
self.is_default = True
|
||||||
self.save()
|
self.save(update_fields=['is_default'])
|
||||||
self.__class__.objects.select_for_update()\
|
self.__class__.objects.select_for_update() \
|
||||||
.filter(is_default=True)\
|
.filter(is_default=True) \
|
||||||
.exclude(id=self.id)\
|
.exclude(id=self.id) \
|
||||||
.update(is_default=False)
|
.update(is_default=False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -128,7 +127,10 @@ class CommandStorage(CommonStorageModelMixin, CommonModelMixin):
|
|||||||
|
|
||||||
def save(self, force_insert=False, force_update=False, using=None,
|
def save(self, force_insert=False, force_update=False, using=None,
|
||||||
update_fields=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:
|
if self.type in TYPE_ENGINE_MAPPING:
|
||||||
engine_mod = import_module(TYPE_ENGINE_MAPPING[self.type])
|
engine_mod = import_module(TYPE_ENGINE_MAPPING[self.type])
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from common.drf.serializers import BulkModelSerializer
|
from common.drf.serializers import BulkModelSerializer
|
||||||
|
from common.utils import lazyproperty
|
||||||
from acls.serializers.rules import ip_group_child_validator, ip_group_help_text
|
from acls.serializers.rules import ip_group_child_validator, ip_group_help_text
|
||||||
from ..utils import db_port_manager
|
from ..utils import db_port_manager
|
||||||
from ..models import Endpoint, EndpointRule
|
from ..models import Endpoint, EndpointRule
|
||||||
@@ -46,9 +47,12 @@ class EndpointSerializer(BulkModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class EndpointRuleSerializer(BulkModelSerializer):
|
class EndpointRuleSerializer(BulkModelSerializer):
|
||||||
_ip_group_help_text = '{} <br> {}'.format(
|
_ip_group_help_text = _(
|
||||||
ip_group_help_text,
|
'Format for comma-delimited string, with * indicating a match all. '
|
||||||
_('If asset IP addresses under different endpoints conflict, use asset labels')
|
'Such as: '
|
||||||
|
'192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64'
|
||||||
|
'<br>'
|
||||||
|
'If asset IP addresses under different endpoints conflict, use asset labels'
|
||||||
)
|
)
|
||||||
ip_group = serializers.ListField(
|
ip_group = serializers.ListField(
|
||||||
default=['*'], label=_('IP'), help_text=_ip_group_help_text,
|
default=['*'], label=_('IP'), help_text=_ip_group_help_text,
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ logger = get_logger(__file__)
|
|||||||
|
|
||||||
|
|
||||||
@receiver(django_ready)
|
@receiver(django_ready)
|
||||||
def init_db_port_mapper(sender, **kwargs):
|
def check_db_port_mapper(sender, **kwargs):
|
||||||
logger.info('Init db port mapper')
|
logger.info('Init db port mapper')
|
||||||
try:
|
try:
|
||||||
db_port_manager.init()
|
db_port_manager.check()
|
||||||
except (ProgrammingError,) as e:
|
except (ProgrammingError,) as e:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -35,9 +35,22 @@ class DBPortManager(object):
|
|||||||
def magnus_listen_port_range(self):
|
def magnus_listen_port_range(self):
|
||||||
return settings.MAGNUS_PORTS
|
return settings.MAGNUS_PORTS
|
||||||
|
|
||||||
def init(self):
|
@staticmethod
|
||||||
|
def fetch_dbs():
|
||||||
with tmp_to_root_org():
|
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]
|
db_ids = [str(i) for i in db_ids]
|
||||||
mapper = dict(zip(self.all_available_ports, list(db_ids)))
|
mapper = dict(zip(self.all_available_ports, list(db_ids)))
|
||||||
self.set_mapper(mapper)
|
self.set_mapper(mapper)
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
from django.utils.translation import ugettext as _
|
import html2text
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
from tickets.const import TicketState, TicketType
|
||||||
from tickets.utils import (
|
from tickets.utils import (
|
||||||
send_ticket_processed_mail_to_applicant,
|
send_ticket_processed_mail_to_applicant,
|
||||||
send_ticket_applied_mail_to_assignees
|
send_ticket_applied_mail_to_assignees
|
||||||
)
|
)
|
||||||
from tickets.const import TicketState, TicketType
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
@@ -96,11 +97,20 @@ class BaseHandler:
|
|||||||
approve_info = _('{} {} the ticket').format(user_display, state_display)
|
approve_info = _('{} {} the ticket').format(user_display, state_display)
|
||||||
context = self._diff_prev_approve_context(state)
|
context = self._diff_prev_approve_context(state)
|
||||||
context.update({'approve_info': approve_info})
|
context.update({'approve_info': approve_info})
|
||||||
|
body = self.safe_html_script(
|
||||||
|
render_to_string('tickets/ticket_approve_diff.html', context)
|
||||||
|
)
|
||||||
data = {
|
data = {
|
||||||
'body': render_to_string('tickets/ticket_approve_diff.html', context),
|
'body': body,
|
||||||
'user': user,
|
'user': user,
|
||||||
'user_display': str(user),
|
'user_display': str(user),
|
||||||
'type': 'state',
|
'type': 'state',
|
||||||
'state': state
|
'state': state
|
||||||
}
|
}
|
||||||
return self.ticket.comments.create(**data)
|
return self.ticket.comments.create(**data)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def safe_html_script(unsafe_html):
|
||||||
|
unsafe_html = unsafe_html.replace('\n', '')
|
||||||
|
html_str = html2text.html2text(unsafe_html)
|
||||||
|
return html_str
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
from users.notifications import PasswordExpirationReminderMsg
|
from users.notifications import PasswordExpirationReminderMsg
|
||||||
from ops.celery.utils import (
|
from ops.celery.utils import (
|
||||||
@@ -80,6 +81,7 @@ def check_user_expired_periodic():
|
|||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
|
@transaction.atomic
|
||||||
def import_ldap_user():
|
def import_ldap_user():
|
||||||
logger.info("Start import ldap user task")
|
logger.info("Start import ldap user task")
|
||||||
util_server = LDAPServerUtil()
|
util_server = LDAPServerUtil()
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ jsonfield2==4.0.0.post0
|
|||||||
geoip2==4.5.0
|
geoip2==4.5.0
|
||||||
ipip-ipdb==1.6.1
|
ipip-ipdb==1.6.1
|
||||||
# Django environment
|
# Django environment
|
||||||
Django==3.2.16
|
Django==3.2.15
|
||||||
django-bootstrap3==14.2.0
|
django-bootstrap3==14.2.0
|
||||||
django-filter==2.4.0
|
django-filter==2.4.0
|
||||||
django-formtools==2.2
|
django-formtools==2.2
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ connection_params = {
|
|||||||
|
|
||||||
if settings.REDIS_USE_SSL:
|
if settings.REDIS_USE_SSL:
|
||||||
connection_params['ssl'] = settings.REDIS_USE_SSL
|
connection_params['ssl'] = settings.REDIS_USE_SSL
|
||||||
connection_params['ssl_cert_reqs'] = settings.REDIS_SSL_REQUIRED
|
connection_params['ssl_cert_reqs'] = settings.REDIS_SSL_REQUIRED
|
||||||
connection_params['ssl_keyfile'] = settings.REDIS_SSL_KEY
|
connection_params['ssl_keyfile'] = settings.REDIS_SSL_KEY
|
||||||
connection_params['ssl_certfile'] = settings.REDIS_SSL_CERT
|
connection_params['ssl_certfile'] = settings.REDIS_SSL_CERT
|
||||||
connection_params['ssl_ca_certs'] = settings.REDIS_SSL_CA
|
connection_params['ssl_ca_certs'] = settings.REDIS_SSL_CA
|
||||||
@@ -39,6 +39,11 @@ if REDIS_SENTINEL_SERVICE_NAME and REDIS_SENTINELS:
|
|||||||
connection_params['sentinels'] = REDIS_SENTINELS
|
connection_params['sentinels'] = REDIS_SENTINELS
|
||||||
sentinel_client = Sentinel(
|
sentinel_client = Sentinel(
|
||||||
**connection_params, sentinel_kwargs={
|
**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,
|
'password': REDIS_SENTINEL_PASSWORD,
|
||||||
'socket_timeout': REDIS_SENTINEL_SOCKET_TIMEOUT
|
'socket_timeout': REDIS_SENTINEL_SOCKET_TIMEOUT
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user