mirror of https://github.com/jumpserver/jumpserver
commit
eee093742c
|
@ -144,10 +144,17 @@ class Gateway(BaseUser):
|
||||||
key_filename=self.private_key_file,
|
key_filename=self.private_key_file,
|
||||||
sock=sock,
|
sock=sock,
|
||||||
timeout=5)
|
timeout=5)
|
||||||
except (paramiko.SSHException, paramiko.ssh_exception.SSHException,
|
except (paramiko.SSHException,
|
||||||
paramiko.AuthenticationException, TimeoutError) as e:
|
paramiko.ssh_exception.SSHException,
|
||||||
|
paramiko.ChannelException,
|
||||||
|
paramiko.AuthenticationException,
|
||||||
|
TimeoutError) as e:
|
||||||
|
|
||||||
|
err = getattr(e, 'text', str(e))
|
||||||
|
if err == 'Connect failed':
|
||||||
|
err = _('Connect failed')
|
||||||
self.is_connective = False
|
self.is_connective = False
|
||||||
return False, str(e)
|
return False, err
|
||||||
finally:
|
finally:
|
||||||
client.close()
|
client.close()
|
||||||
self.is_connective = True
|
self.is_connective = True
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from simple_history.signals import pre_create_historical_record
|
from simple_history.signals import pre_create_historical_record
|
||||||
from django.db.models.signals import post_save, pre_save, post_delete
|
from django.db.models.signals import post_save, pre_save, pre_delete
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from ..models import AuthBook, SystemUser
|
from ..models import AuthBook, SystemUser
|
||||||
|
@ -28,7 +28,7 @@ def pre_create_historical_record_callback(sender, history_instance=None, **kwarg
|
||||||
setattr(history_instance, attr, system_user_attr_value)
|
setattr(history_instance, attr, system_user_attr_value)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=AuthBook)
|
@receiver(pre_delete, sender=AuthBook)
|
||||||
def on_authbook_post_delete(sender, instance, **kwargs):
|
def on_authbook_post_delete(sender, instance, **kwargs):
|
||||||
instance.remove_asset_admin_user_if_need()
|
instance.remove_asset_admin_user_if_need()
|
||||||
|
|
||||||
|
|
|
@ -89,9 +89,6 @@ class ClientProtocolMixin:
|
||||||
drives_redirect = is_true(self.request.query_params.get('drives_redirect'))
|
drives_redirect = is_true(self.request.query_params.get('drives_redirect'))
|
||||||
token = self.create_token(user, asset, application, system_user)
|
token = self.create_token(user, asset, application, system_user)
|
||||||
|
|
||||||
if system_user.login_mode == SystemUser.LOGIN_MANUAL:
|
|
||||||
options['prompt for credentials on client:i'] = '1'
|
|
||||||
|
|
||||||
if drives_redirect:
|
if drives_redirect:
|
||||||
options['drivestoredirect:s'] = '*'
|
options['drivestoredirect:s'] = '*'
|
||||||
options['screen mode id:i'] = '2' if full_screen else '1'
|
options['screen mode id:i'] = '2' if full_screen else '1'
|
||||||
|
|
|
@ -67,9 +67,9 @@ class ChallengeMixin(forms.Form):
|
||||||
|
|
||||||
def get_user_login_form_cls(*, captcha=False):
|
def get_user_login_form_cls(*, captcha=False):
|
||||||
bases = []
|
bases = []
|
||||||
if settings.SECURITY_LOGIN_CAPTCHA_ENABLED and captcha:
|
|
||||||
bases.append(CaptchaMixin)
|
|
||||||
if settings.SECURITY_LOGIN_CHALLENGE_ENABLED:
|
if settings.SECURITY_LOGIN_CHALLENGE_ENABLED:
|
||||||
bases.append(ChallengeMixin)
|
bases.append(ChallengeMixin)
|
||||||
|
elif settings.SECURITY_LOGIN_CAPTCHA_ENABLED and captcha:
|
||||||
|
bases.append(CaptchaMixin)
|
||||||
bases.append(UserLoginForm)
|
bases.append(UserLoginForm)
|
||||||
return type('UserLoginForm', tuple(bases), {})
|
return type('UserLoginForm', tuple(bases), {})
|
||||||
|
|
|
@ -191,7 +191,10 @@ class AuthMixin(PasswordEncryptionViewMixin):
|
||||||
raise self.partial_credential_error(error=error)
|
raise self.partial_credential_error(error=error)
|
||||||
|
|
||||||
def _set_partial_credential_error(self, username, ip, request):
|
def _set_partial_credential_error(self, username, ip, request):
|
||||||
self.partial_credential_error = partial(errors.CredentialError, username=username, ip=ip, request=request)
|
self.partial_credential_error = partial(
|
||||||
|
errors.CredentialError, username=username,
|
||||||
|
ip=ip, request=request
|
||||||
|
)
|
||||||
|
|
||||||
def get_auth_data(self, decrypt_passwd=False):
|
def get_auth_data(self, decrypt_passwd=False):
|
||||||
request = self.request
|
request = self.request
|
||||||
|
|
|
@ -147,7 +147,7 @@
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div style="line-height: 17px;margin-bottom: 20px;color: #999999;">
|
<div style="line-height: 17px;margin-bottom: 20px;color: #999999;">
|
||||||
{% if form.errors %}
|
{% if form.errors %}
|
||||||
<p class="red-fonts" style="color: red">
|
<p class="help-block">
|
||||||
{% if form.non_field_errors %}
|
{% if form.non_field_errors %}
|
||||||
{{ form.non_field_errors.as_text }}
|
{{ form.non_field_errors.as_text }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -160,9 +160,15 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% bootstrap_field form.username show_label=False %}
|
{% bootstrap_field form.username show_label=False %}
|
||||||
<div class="form-group">
|
|
||||||
<input type="password" class="form-control" id="password" placeholder="{% trans 'Password' %}" required="">
|
<div class="form-group {% if form.password.errors %} has-error {% endif %}">
|
||||||
|
<input type="password" class="form-control" id="password" placeholder="{% trans 'Password' %}" required>
|
||||||
<input id="password-hidden" type="text" style="display:none" name="{{ form.password.html_name }}">
|
<input id="password-hidden" type="text" style="display:none" name="{{ form.password.html_name }}">
|
||||||
|
{% if form.password.errors %}
|
||||||
|
<p class="help-block" style="text-align: left">
|
||||||
|
{{ form.password.errors.as_text }}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if form.challenge %}
|
{% if form.challenge %}
|
||||||
{% bootstrap_field form.challenge show_label=False %}
|
{% bootstrap_field form.challenge show_label=False %}
|
||||||
|
@ -220,7 +226,6 @@
|
||||||
<i class="fa"><img src="{{ LOGIN_FEISHU_LOGO_URL }}" height="13" width="13"></i> {% trans 'FeiShu' %}
|
<i class="fa"><img src="{{ LOGIN_FEISHU_LOGO_URL }}" height="13" width="13"></i> {% trans 'FeiShu' %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="text-center" style="display: inline-block;">
|
<div class="text-center" style="display: inline-block;">
|
||||||
|
@ -236,6 +241,9 @@
|
||||||
<script type="text/javascript" src="/static/js/plugins/jsencrypt/jsencrypt.min.js"></script>
|
<script type="text/javascript" src="/static/js/plugins/jsencrypt/jsencrypt.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
function encryptLoginPassword(password, rsaPublicKey) {
|
function encryptLoginPassword(password, rsaPublicKey) {
|
||||||
|
if (!password) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
var jsencrypt = new JSEncrypt(); //加密对象
|
var jsencrypt = new JSEncrypt(); //加密对象
|
||||||
jsencrypt.setPublicKey(rsaPublicKey); // 设置密钥
|
jsencrypt.setPublicKey(rsaPublicKey); // 设置密钥
|
||||||
return jsencrypt.encrypt(password); //加密
|
return jsencrypt.encrypt(password); //加密
|
||||||
|
|
|
@ -134,7 +134,6 @@ function cancelTicket() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelCloseConfirm() {
|
function cancelCloseConfirm() {
|
||||||
cancelTicket();
|
|
||||||
window.onbeforeunload = function() {};
|
window.onbeforeunload = function() {};
|
||||||
window.onunload = function(){};
|
window.onunload = function(){};
|
||||||
}
|
}
|
||||||
|
@ -158,6 +157,7 @@ $(document).ready(function () {
|
||||||
cancelCloseConfirm();
|
cancelCloseConfirm();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}).on('click', '.btn-return', function () {
|
}).on('click', '.btn-return', function () {
|
||||||
|
cancelTicket();
|
||||||
cancelCloseConfirm();
|
cancelCloseConfirm();
|
||||||
window.location = "{% url 'authentication:login' %}"
|
window.location = "{% url 'authentication:login' %}"
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,6 +9,7 @@ from django.contrib.auth import login as auth_login, logout as auth_logout
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import reverse, redirect
|
from django.shortcuts import reverse, redirect
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.db import transaction
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.views.decorators.cache import never_cache
|
from django.views.decorators.cache import never_cache
|
||||||
from django.views.decorators.csrf import csrf_protect
|
from django.views.decorators.csrf import csrf_protect
|
||||||
|
@ -18,14 +19,13 @@ from django.views.generic.edit import FormView
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.contrib.auth import BACKEND_SESSION_KEY
|
from django.contrib.auth import BACKEND_SESSION_KEY
|
||||||
from django.db.transaction import atomic
|
|
||||||
|
|
||||||
from common.utils import get_request_ip, FlashMessageUtil
|
from common.utils import FlashMessageUtil
|
||||||
from users.utils import (
|
from users.utils import (
|
||||||
redirect_user_first_login_or_index
|
redirect_user_first_login_or_index
|
||||||
)
|
)
|
||||||
from ..const import RSA_PRIVATE_KEY, RSA_PUBLIC_KEY
|
from ..const import RSA_PRIVATE_KEY, RSA_PUBLIC_KEY
|
||||||
from .. import mixins, errors, utils
|
from .. import mixins, errors
|
||||||
from ..forms import get_user_login_form_cls
|
from ..forms import get_user_login_form_cls
|
||||||
|
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ class UserLoginView(mixins.AuthMixin, FormView):
|
||||||
self.request.session.delete_test_cookie()
|
self.request.session.delete_test_cookie()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with atomic():
|
with transaction.atomic():
|
||||||
self.check_user_auth(decrypt_passwd=True)
|
self.check_user_auth(decrypt_passwd=True)
|
||||||
except errors.AuthFailedError as e:
|
except errors.AuthFailedError as e:
|
||||||
form.add_error(None, e.msg)
|
form.add_error(None, e.msg)
|
||||||
|
|
|
@ -14,6 +14,7 @@ import types
|
||||||
import errno
|
import errno
|
||||||
import json
|
import json
|
||||||
import yaml
|
import yaml
|
||||||
|
import copy
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from urllib.parse import urljoin, urlparse
|
from urllib.parse import urljoin, urlparse
|
||||||
|
@ -348,7 +349,8 @@ class Config(dict):
|
||||||
'HEALTH_CHECK_TOKEN': '',
|
'HEALTH_CHECK_TOKEN': '',
|
||||||
}
|
}
|
||||||
|
|
||||||
def compatible_auth_openid_of_key(self):
|
@staticmethod
|
||||||
|
def convert_keycloak_to_openid(keycloak_config):
|
||||||
"""
|
"""
|
||||||
兼容OpenID旧配置 (即 version <= 1.5.8)
|
兼容OpenID旧配置 (即 version <= 1.5.8)
|
||||||
因为旧配置只支持OpenID协议的Keycloak实现,
|
因为旧配置只支持OpenID协议的Keycloak实现,
|
||||||
|
@ -356,65 +358,79 @@ class Config(dict):
|
||||||
构造出新配置中标准OpenID协议中所需的Endpoint即可
|
构造出新配置中标准OpenID协议中所需的Endpoint即可
|
||||||
(Keycloak说明文档参考: https://www.keycloak.org/docs/latest/securing_apps/)
|
(Keycloak说明文档参考: https://www.keycloak.org/docs/latest/securing_apps/)
|
||||||
"""
|
"""
|
||||||
if self.AUTH_OPENID and not self.AUTH_OPENID_REALM_NAME:
|
|
||||||
self['AUTH_OPENID_KEYCLOAK'] = False
|
|
||||||
|
|
||||||
if not self.AUTH_OPENID:
|
openid_config = copy.deepcopy(keycloak_config)
|
||||||
|
|
||||||
|
auth_openid = openid_config.get('AUTH_OPENID')
|
||||||
|
auth_openid_realm_name = openid_config.get('AUTH_OPENID_REALM_NAME')
|
||||||
|
auth_openid_server_url = openid_config.get('AUTH_OPENID_SERVER_URL')
|
||||||
|
|
||||||
|
if not auth_openid:
|
||||||
return
|
return
|
||||||
|
|
||||||
realm_name = self.AUTH_OPENID_REALM_NAME
|
if auth_openid and not auth_openid_realm_name:
|
||||||
if realm_name is None:
|
# 开启的是标准 OpenID 配置,关掉 Keycloak 配置
|
||||||
|
openid_config.update({
|
||||||
|
'AUTH_OPENID_KEYCLOAK': False
|
||||||
|
})
|
||||||
|
|
||||||
|
if auth_openid_realm_name is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
compatible_keycloak_config = [
|
# # convert key # #
|
||||||
(
|
compatible_config = {
|
||||||
'AUTH_OPENID_PROVIDER_ENDPOINT',
|
'AUTH_OPENID_PROVIDER_ENDPOINT': auth_openid_server_url,
|
||||||
self.AUTH_OPENID_SERVER_URL
|
|
||||||
),
|
|
||||||
(
|
|
||||||
'AUTH_OPENID_PROVIDER_AUTHORIZATION_ENDPOINT',
|
|
||||||
'/realms/{}/protocol/openid-connect/auth'.format(realm_name)
|
|
||||||
),
|
|
||||||
(
|
|
||||||
'AUTH_OPENID_PROVIDER_TOKEN_ENDPOINT',
|
|
||||||
'/realms/{}/protocol/openid-connect/token'.format(realm_name)
|
|
||||||
),
|
|
||||||
(
|
|
||||||
'AUTH_OPENID_PROVIDER_JWKS_ENDPOINT',
|
|
||||||
'/realms/{}/protocol/openid-connect/certs'.format(realm_name)
|
|
||||||
),
|
|
||||||
(
|
|
||||||
'AUTH_OPENID_PROVIDER_USERINFO_ENDPOINT',
|
|
||||||
'/realms/{}/protocol/openid-connect/userinfo'.format(realm_name)
|
|
||||||
),
|
|
||||||
(
|
|
||||||
'AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT',
|
|
||||||
'/realms/{}/protocol/openid-connect/logout'.format(realm_name)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
for key, value in compatible_keycloak_config:
|
|
||||||
self[key] = value
|
|
||||||
|
|
||||||
def compatible_auth_openid_of_value(self):
|
'AUTH_OPENID_PROVIDER_AUTHORIZATION_ENDPOINT': '/realms/{}/protocol/openid-connect/auth'
|
||||||
"""
|
''.format(auth_openid_realm_name),
|
||||||
兼容值的绝对路径、相对路径
|
'AUTH_OPENID_PROVIDER_TOKEN_ENDPOINT': '/realms/{}/protocol/openid-connect/token'
|
||||||
(key 为 AUTH_OPENID_PROVIDER_*_ENDPOINT 的配置)
|
''.format(auth_openid_realm_name),
|
||||||
"""
|
'AUTH_OPENID_PROVIDER_JWKS_ENDPOINT': '/realms/{}/protocol/openid-connect/certs'
|
||||||
if not self.AUTH_OPENID:
|
''.format(auth_openid_realm_name),
|
||||||
return
|
'AUTH_OPENID_PROVIDER_USERINFO_ENDPOINT': '/realms/{}/protocol/openid-connect/userinfo'
|
||||||
|
''.format(auth_openid_realm_name),
|
||||||
|
'AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT': '/realms/{}/protocol/openid-connect/logout'
|
||||||
|
''.format(auth_openid_realm_name)
|
||||||
|
}
|
||||||
|
for key, value in compatible_config.items():
|
||||||
|
openid_config[key] = value
|
||||||
|
|
||||||
base = self.AUTH_OPENID_PROVIDER_ENDPOINT
|
# # convert value # #
|
||||||
config = list(self.items())
|
""" 兼容值的绝对路径、相对路径 (key 为 AUTH_OPENID_PROVIDER_*_ENDPOINT 的配置) """
|
||||||
for key, value in config:
|
base = openid_config.get('AUTH_OPENID_PROVIDER_ENDPOINT')
|
||||||
|
for key, value in openid_config.items():
|
||||||
result = re.match(r'^AUTH_OPENID_PROVIDER_.*_ENDPOINT$', key)
|
result = re.match(r'^AUTH_OPENID_PROVIDER_.*_ENDPOINT$', key)
|
||||||
if result is None:
|
if result is None:
|
||||||
continue
|
continue
|
||||||
if value is None:
|
if value is None:
|
||||||
# None 在 url 中有特殊含义 (比如对于: end_session_endpoint)
|
# None 在 url 中有特殊含义 (比如对于: end_session_endpoint)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
value = build_absolute_uri(base, value)
|
value = build_absolute_uri(base, value)
|
||||||
|
openid_config[key] = value
|
||||||
|
|
||||||
|
return openid_config
|
||||||
|
|
||||||
|
def get_keycloak_config(self):
|
||||||
|
keycloak_config = {
|
||||||
|
'AUTH_OPENID': self.AUTH_OPENID,
|
||||||
|
'AUTH_OPENID_REALM_NAME': self.AUTH_OPENID_REALM_NAME,
|
||||||
|
'AUTH_OPENID_SERVER_URL': self.AUTH_OPENID_SERVER_URL,
|
||||||
|
'AUTH_OPENID_PROVIDER_ENDPOINT': self.AUTH_OPENID_PROVIDER_ENDPOINT
|
||||||
|
}
|
||||||
|
return keycloak_config
|
||||||
|
|
||||||
|
def set_openid_config(self, openid_config):
|
||||||
|
for key, value in openid_config.items():
|
||||||
self[key] = value
|
self[key] = value
|
||||||
|
|
||||||
|
def compatible_auth_openid(self, keycloak_config=None):
|
||||||
|
if keycloak_config is None:
|
||||||
|
keycloak_config = self.get_keycloak_config()
|
||||||
|
openid_config = self.convert_keycloak_to_openid(keycloak_config)
|
||||||
|
if openid_config:
|
||||||
|
self.set_openid_config(openid_config)
|
||||||
|
|
||||||
def compatible(self):
|
def compatible(self):
|
||||||
"""
|
"""
|
||||||
对配置做兼容处理
|
对配置做兼容处理
|
||||||
|
@ -424,14 +440,8 @@ class Config(dict):
|
||||||
处理顺序要保持先对key做处理, 再对value做处理,
|
处理顺序要保持先对key做处理, 再对value做处理,
|
||||||
因为处理value的时候,只根据最新版本支持的key进行
|
因为处理value的时候,只根据最新版本支持的key进行
|
||||||
"""
|
"""
|
||||||
parts = ['key', 'value']
|
# 兼容 OpenID 配置
|
||||||
targets = ['auth_openid']
|
self.compatible_auth_openid()
|
||||||
for part in parts:
|
|
||||||
for target in targets:
|
|
||||||
method_name = 'compatible_{}_of_{}'.format(target, part)
|
|
||||||
method = getattr(self, method_name, None)
|
|
||||||
if method is not None:
|
|
||||||
method()
|
|
||||||
|
|
||||||
def convert_type(self, k, v):
|
def convert_type(self, k, v):
|
||||||
default_value = self.defaults.get(k)
|
default_value = self.defaults.get(k)
|
||||||
|
|
Binary file not shown.
|
@ -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: 2021-09-15 20:51+0800\n"
|
"POT-Creation-Date: 2021-09-16 19:25+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"
|
||||||
|
@ -25,7 +25,7 @@ msgstr ""
|
||||||
#: orgs/models.py:24 perms/models/base.py:44 settings/models.py:29
|
#: orgs/models.py:24 perms/models/base.py:44 settings/models.py:29
|
||||||
#: settings/serializers/sms.py:6 terminal/models/storage.py:23
|
#: settings/serializers/sms.py:6 terminal/models/storage.py:23
|
||||||
#: terminal/models/task.py:16 terminal/models/terminal.py:100
|
#: terminal/models/task.py:16 terminal/models/terminal.py:100
|
||||||
#: users/forms/profile.py:32 users/models/group.py:15 users/models/user.py:604
|
#: users/forms/profile.py:32 users/models/group.py:15 users/models/user.py:605
|
||||||
#: users/templates/users/_select_user_modal.html:13
|
#: users/templates/users/_select_user_modal.html:13
|
||||||
#: users/templates/users/user_asset_permission.html:37
|
#: users/templates/users/user_asset_permission.html:37
|
||||||
#: users/templates/users/user_asset_permission.html:154
|
#: users/templates/users/user_asset_permission.html:154
|
||||||
|
@ -60,7 +60,7 @@ msgstr "激活中"
|
||||||
#: orgs/models.py:27 perms/models/base.py:53 settings/models.py:34
|
#: orgs/models.py:27 perms/models/base.py:53 settings/models.py:34
|
||||||
#: terminal/models/storage.py:26 terminal/models/terminal.py:114
|
#: terminal/models/storage.py:26 terminal/models/terminal.py:114
|
||||||
#: tickets/models/ticket.py:71 users/models/group.py:16
|
#: tickets/models/ticket.py:71 users/models/group.py:16
|
||||||
#: users/models/user.py:637 xpack/plugins/change_auth_plan/models/base.py:41
|
#: users/models/user.py:638 xpack/plugins/change_auth_plan/models/base.py:41
|
||||||
#: xpack/plugins/cloud/models.py:35 xpack/plugins/cloud/models.py:113
|
#: xpack/plugins/cloud/models.py:35 xpack/plugins/cloud/models.py:113
|
||||||
#: xpack/plugins/gathered_user/models.py:26
|
#: xpack/plugins/gathered_user/models.py:26
|
||||||
msgid "Comment"
|
msgid "Comment"
|
||||||
|
@ -98,7 +98,7 @@ msgstr "动作"
|
||||||
#: terminal/backends/command/models.py:18
|
#: terminal/backends/command/models.py:18
|
||||||
#: terminal/backends/command/serializers.py:12 terminal/models/session.py:38
|
#: terminal/backends/command/serializers.py:12 terminal/models/session.py:38
|
||||||
#: tickets/models/comment.py:17 users/const.py:14 users/models/user.py:181
|
#: tickets/models/comment.py:17 users/const.py:14 users/models/user.py:181
|
||||||
#: users/models/user.py:809 users/models/user.py:835
|
#: users/models/user.py:814 users/models/user.py:840
|
||||||
#: users/serializers/group.py:19
|
#: users/serializers/group.py:19
|
||||||
#: users/templates/users/user_asset_permission.html:38
|
#: users/templates/users/user_asset_permission.html:38
|
||||||
#: users/templates/users/user_asset_permission.html:64
|
#: users/templates/users/user_asset_permission.html:64
|
||||||
|
@ -177,7 +177,7 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
|
||||||
#: applications/serializers/attrs/application_type/vmware_client.py:26
|
#: applications/serializers/attrs/application_type/vmware_client.py:26
|
||||||
#: assets/models/base.py:176 assets/models/gathered_user.py:15
|
#: assets/models/base.py:176 assets/models/gathered_user.py:15
|
||||||
#: audits/models.py:105 authentication/forms.py:15 authentication/forms.py:17
|
#: audits/models.py:105 authentication/forms.py:15 authentication/forms.py:17
|
||||||
#: ops/models/adhoc.py:148 users/forms/profile.py:31 users/models/user.py:602
|
#: ops/models/adhoc.py:148 users/forms/profile.py:31 users/models/user.py:603
|
||||||
#: users/templates/users/_select_user_modal.html:14
|
#: users/templates/users/_select_user_modal.html:14
|
||||||
#: xpack/plugins/change_auth_plan/models/asset.py:35
|
#: xpack/plugins/change_auth_plan/models/asset.py:35
|
||||||
#: xpack/plugins/change_auth_plan/models/asset.py:191
|
#: xpack/plugins/change_auth_plan/models/asset.py:191
|
||||||
|
@ -281,7 +281,7 @@ msgstr "应用管理"
|
||||||
#: applications/serializers/application.py:88 assets/models/label.py:21
|
#: applications/serializers/application.py:88 assets/models/label.py:21
|
||||||
#: perms/models/application_permission.py:20
|
#: perms/models/application_permission.py:20
|
||||||
#: perms/serializers/application/user_permission.py:33
|
#: perms/serializers/application/user_permission.py:33
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:20
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:22
|
||||||
#: xpack/plugins/change_auth_plan/models/app.py:25
|
#: xpack/plugins/change_auth_plan/models/app.py:25
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "类别"
|
msgstr "类别"
|
||||||
|
@ -292,7 +292,7 @@ msgstr "类别"
|
||||||
#: perms/serializers/application/user_permission.py:34
|
#: perms/serializers/application/user_permission.py:34
|
||||||
#: terminal/models/storage.py:55 terminal/models/storage.py:116
|
#: terminal/models/storage.py:55 terminal/models/storage.py:116
|
||||||
#: tickets/models/flow.py:51 tickets/models/ticket.py:48
|
#: tickets/models/flow.py:51 tickets/models/ticket.py:48
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:27
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:29
|
||||||
#: xpack/plugins/change_auth_plan/models/app.py:28
|
#: xpack/plugins/change_auth_plan/models/app.py:28
|
||||||
#: xpack/plugins/change_auth_plan/models/app.py:148
|
#: xpack/plugins/change_auth_plan/models/app.py:148
|
||||||
msgid "Type"
|
msgid "Type"
|
||||||
|
@ -310,7 +310,7 @@ msgstr ""
|
||||||
#: applications/serializers/application.py:59
|
#: applications/serializers/application.py:59
|
||||||
#: applications/serializers/application.py:89 assets/serializers/label.py:13
|
#: applications/serializers/application.py:89 assets/serializers/label.py:13
|
||||||
#: perms/serializers/application/permission.py:16
|
#: perms/serializers/application/permission.py:16
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:24
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:26
|
||||||
msgid "Category display"
|
msgid "Category display"
|
||||||
msgstr "类别名称"
|
msgstr "类别名称"
|
||||||
|
|
||||||
|
@ -318,7 +318,7 @@ msgstr "类别名称"
|
||||||
#: applications/serializers/application.py:91
|
#: applications/serializers/application.py:91
|
||||||
#: assets/serializers/system_user.py:26 audits/serializers.py:29
|
#: assets/serializers/system_user.py:26 audits/serializers.py:29
|
||||||
#: perms/serializers/application/permission.py:17
|
#: perms/serializers/application/permission.py:17
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:31
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:33
|
||||||
#: tickets/serializers/ticket/ticket.py:22
|
#: tickets/serializers/ticket/ticket.py:22
|
||||||
#: tickets/serializers/ticket/ticket.py:168
|
#: tickets/serializers/ticket/ticket.py:168
|
||||||
msgid "Type display"
|
msgid "Type display"
|
||||||
|
@ -377,7 +377,7 @@ msgstr "目标URL"
|
||||||
#: applications/serializers/attrs/application_type/vmware_client.py:30
|
#: applications/serializers/attrs/application_type/vmware_client.py:30
|
||||||
#: assets/models/base.py:177 audits/signals_handler.py:65
|
#: assets/models/base.py:177 audits/signals_handler.py:65
|
||||||
#: authentication/forms.py:22
|
#: authentication/forms.py:22
|
||||||
#: authentication/templates/authentication/login.html:164
|
#: authentication/templates/authentication/login.html:165
|
||||||
#: settings/serializers/auth/ldap.py:44 users/forms/profile.py:21
|
#: settings/serializers/auth/ldap.py:44 users/forms/profile.py:21
|
||||||
#: users/templates/users/user_otp_check_password.html:13
|
#: users/templates/users/user_otp_check_password.html:13
|
||||||
#: users/templates/users/user_password_update.html:43
|
#: users/templates/users/user_password_update.html:43
|
||||||
|
@ -531,7 +531,7 @@ msgstr "标签管理"
|
||||||
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:26
|
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:26
|
||||||
#: assets/models/cmd_filter.py:67 assets/models/group.py:21
|
#: assets/models/cmd_filter.py:67 assets/models/group.py:21
|
||||||
#: common/db/models.py:70 common/mixins/models.py:49 orgs/models.py:25
|
#: common/db/models.py:70 common/mixins/models.py:49 orgs/models.py:25
|
||||||
#: orgs/models.py:437 perms/models/base.py:51 users/models/user.py:645
|
#: orgs/models.py:437 perms/models/base.py:51 users/models/user.py:646
|
||||||
#: users/serializers/group.py:33
|
#: users/serializers/group.py:33
|
||||||
#: xpack/plugins/change_auth_plan/models/base.py:45
|
#: xpack/plugins/change_auth_plan/models/base.py:45
|
||||||
#: xpack/plugins/cloud/models.py:119 xpack/plugins/gathered_user/models.py:30
|
#: xpack/plugins/cloud/models.py:119 xpack/plugins/gathered_user/models.py:30
|
||||||
|
@ -544,7 +544,7 @@ msgstr "创建者"
|
||||||
#: assets/models/label.py:25 common/db/models.py:72 common/mixins/models.py:50
|
#: assets/models/label.py:25 common/db/models.py:72 common/mixins/models.py:50
|
||||||
#: ops/models/adhoc.py:38 ops/models/command.py:29 orgs/models.py:26
|
#: ops/models/adhoc.py:38 ops/models/command.py:29 orgs/models.py:26
|
||||||
#: orgs/models.py:435 perms/models/base.py:52 users/models/group.py:18
|
#: orgs/models.py:435 perms/models/base.py:52 users/models/group.py:18
|
||||||
#: users/models/user.py:836 xpack/plugins/cloud/models.py:122
|
#: users/models/user.py:841 xpack/plugins/cloud/models.py:122
|
||||||
msgid "Date created"
|
msgid "Date created"
|
||||||
msgstr "创建日期"
|
msgstr "创建日期"
|
||||||
|
|
||||||
|
@ -599,7 +599,7 @@ msgstr "带宽"
|
||||||
msgid "Contact"
|
msgid "Contact"
|
||||||
msgstr "联系人"
|
msgstr "联系人"
|
||||||
|
|
||||||
#: assets/models/cluster.py:22 users/models/user.py:623
|
#: assets/models/cluster.py:22 users/models/user.py:624
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "手机"
|
msgstr "手机"
|
||||||
|
|
||||||
|
@ -625,7 +625,7 @@ msgid "Default"
|
||||||
msgstr "默认"
|
msgstr "默认"
|
||||||
|
|
||||||
#: assets/models/cluster.py:36 assets/models/label.py:14
|
#: assets/models/cluster.py:36 assets/models/label.py:14
|
||||||
#: users/models/user.py:821
|
#: users/models/user.py:826
|
||||||
msgid "System"
|
msgid "System"
|
||||||
msgstr "系统"
|
msgstr "系统"
|
||||||
|
|
||||||
|
@ -687,7 +687,7 @@ msgstr "无法连接到 {ip} 上的端口 {port}"
|
||||||
msgid "Authentication failed"
|
msgid "Authentication failed"
|
||||||
msgstr "认证失败"
|
msgstr "认证失败"
|
||||||
|
|
||||||
#: assets/models/domain.py:133
|
#: assets/models/domain.py:133 assets/models/domain.py:155
|
||||||
msgid "Connect failed"
|
msgid "Connect failed"
|
||||||
msgstr "连接失败"
|
msgstr "连接失败"
|
||||||
|
|
||||||
|
@ -1091,8 +1091,8 @@ msgstr "成功"
|
||||||
|
|
||||||
#: audits/models.py:43 ops/models/command.py:30 perms/models/base.py:49
|
#: audits/models.py:43 ops/models/command.py:30 perms/models/base.py:49
|
||||||
#: terminal/models/session.py:52
|
#: terminal/models/session.py:52
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:53
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:55
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:45
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:47
|
||||||
#: xpack/plugins/change_auth_plan/models/base.py:105
|
#: xpack/plugins/change_auth_plan/models/base.py:105
|
||||||
#: xpack/plugins/change_auth_plan/models/base.py:189
|
#: xpack/plugins/change_auth_plan/models/base.py:189
|
||||||
#: xpack/plugins/gathered_user/models.py:76
|
#: xpack/plugins/gathered_user/models.py:76
|
||||||
|
@ -1160,7 +1160,7 @@ msgstr "用户代理"
|
||||||
#: audits/models.py:110
|
#: audits/models.py:110
|
||||||
#: authentication/templates/authentication/_mfa_confirm_modal.html:14
|
#: authentication/templates/authentication/_mfa_confirm_modal.html:14
|
||||||
#: authentication/templates/authentication/login_otp.html:6
|
#: authentication/templates/authentication/login_otp.html:6
|
||||||
#: users/forms/profile.py:64 users/models/user.py:626
|
#: users/forms/profile.py:64 users/models/user.py:627
|
||||||
#: users/serializers/profile.py:102
|
#: users/serializers/profile.py:102
|
||||||
msgid "MFA"
|
msgid "MFA"
|
||||||
msgstr "多因子认证"
|
msgstr "多因子认证"
|
||||||
|
@ -1239,14 +1239,14 @@ msgid "Auth Token"
|
||||||
msgstr "认证令牌"
|
msgstr "认证令牌"
|
||||||
|
|
||||||
#: audits/signals_handler.py:68
|
#: audits/signals_handler.py:68
|
||||||
#: authentication/templates/authentication/login.html:210
|
#: authentication/templates/authentication/login.html:216
|
||||||
#: notifications/backends/__init__.py:11 users/models/user.py:659
|
#: notifications/backends/__init__.py:11 users/models/user.py:660
|
||||||
msgid "WeCom"
|
msgid "WeCom"
|
||||||
msgstr "企业微信"
|
msgstr "企业微信"
|
||||||
|
|
||||||
#: audits/signals_handler.py:69
|
#: audits/signals_handler.py:69
|
||||||
#: authentication/templates/authentication/login.html:215
|
#: authentication/templates/authentication/login.html:221
|
||||||
#: notifications/backends/__init__.py:12 users/models/user.py:660
|
#: notifications/backends/__init__.py:12 users/models/user.py:661
|
||||||
msgid "DingTalk"
|
msgid "DingTalk"
|
||||||
msgstr "钉钉"
|
msgstr "钉钉"
|
||||||
|
|
||||||
|
@ -1433,7 +1433,7 @@ msgstr "{ApplicationPermission} 添加 {SystemUser}"
|
||||||
msgid "{ApplicationPermission} REMOVE {SystemUser}"
|
msgid "{ApplicationPermission} REMOVE {SystemUser}"
|
||||||
msgstr "{ApplicationPermission} 移除 {SystemUser}"
|
msgstr "{ApplicationPermission} 移除 {SystemUser}"
|
||||||
|
|
||||||
#: authentication/api/connection_token.py:226
|
#: authentication/api/connection_token.py:227
|
||||||
msgid "Invalid token"
|
msgid "Invalid token"
|
||||||
msgstr "无效的令牌"
|
msgstr "无效的令牌"
|
||||||
|
|
||||||
|
@ -1616,15 +1616,15 @@ msgstr "来源 IP 不被允许登录"
|
||||||
msgid "SSO auth closed"
|
msgid "SSO auth closed"
|
||||||
msgstr "SSO 认证关闭了"
|
msgstr "SSO 认证关闭了"
|
||||||
|
|
||||||
#: authentication/errors.py:298 authentication/mixins.py:318
|
#: authentication/errors.py:298 authentication/mixins.py:321
|
||||||
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.py:307 authentication/mixins.py:325
|
#: authentication/errors.py:307 authentication/mixins.py:328
|
||||||
msgid "You should to change your password before login"
|
msgid "You should to change your password before login"
|
||||||
msgstr "登录完成前,请先修改密码"
|
msgstr "登录完成前,请先修改密码"
|
||||||
|
|
||||||
#: authentication/errors.py:316 authentication/mixins.py:332
|
#: authentication/errors.py:316 authentication/mixins.py:335
|
||||||
msgid "Your password has expired, please reset before logging in"
|
msgid "Your password has expired, please reset before logging in"
|
||||||
msgstr "您的密码已过期,先修改再登录"
|
msgstr "您的密码已过期,先修改再登录"
|
||||||
|
|
||||||
|
@ -1649,7 +1649,7 @@ msgstr "MFA 类型"
|
||||||
msgid "MFA code"
|
msgid "MFA code"
|
||||||
msgstr "多因子认证验证码"
|
msgstr "多因子认证验证码"
|
||||||
|
|
||||||
#: authentication/mixins.py:308
|
#: authentication/mixins.py:311
|
||||||
msgid "Please change your password"
|
msgid "Please change your password"
|
||||||
msgstr "请修改密码"
|
msgstr "请修改密码"
|
||||||
|
|
||||||
|
@ -1764,31 +1764,31 @@ msgstr "代码错误"
|
||||||
msgid "Welcome back, please enter username and password to login"
|
msgid "Welcome back, please enter username and password to login"
|
||||||
msgstr "欢迎回来,请输入用户名和密码登录"
|
msgstr "欢迎回来,请输入用户名和密码登录"
|
||||||
|
|
||||||
#: authentication/templates/authentication/login.html:183
|
#: authentication/templates/authentication/login.html:189
|
||||||
#: users/templates/users/forgot_password.html:15
|
#: users/templates/users/forgot_password.html:15
|
||||||
#: users/templates/users/forgot_password.html:16
|
#: users/templates/users/forgot_password.html:16
|
||||||
msgid "Forgot password"
|
msgid "Forgot password"
|
||||||
msgstr "忘记密码"
|
msgstr "忘记密码"
|
||||||
|
|
||||||
#: authentication/templates/authentication/login.html:190
|
#: authentication/templates/authentication/login.html:196
|
||||||
#: templates/_header_bar.html:83
|
#: templates/_header_bar.html:83
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "登录"
|
msgstr "登录"
|
||||||
|
|
||||||
#: authentication/templates/authentication/login.html:197
|
#: authentication/templates/authentication/login.html:203
|
||||||
msgid "More login options"
|
msgid "More login options"
|
||||||
msgstr "更多登录方式"
|
msgstr "更多登录方式"
|
||||||
|
|
||||||
#: authentication/templates/authentication/login.html:200
|
#: authentication/templates/authentication/login.html:206
|
||||||
msgid "OpenID"
|
msgid "OpenID"
|
||||||
msgstr "OpenID"
|
msgstr "OpenID"
|
||||||
|
|
||||||
#: authentication/templates/authentication/login.html:205
|
#: authentication/templates/authentication/login.html:211
|
||||||
msgid "CAS"
|
msgid "CAS"
|
||||||
msgstr "CAS"
|
msgstr "CAS"
|
||||||
|
|
||||||
#: authentication/templates/authentication/login.html:220
|
#: authentication/templates/authentication/login.html:226
|
||||||
#: notifications/backends/__init__.py:14 users/models/user.py:661
|
#: notifications/backends/__init__.py:14 users/models/user.py:662
|
||||||
msgid "FeiShu"
|
msgid "FeiShu"
|
||||||
msgstr "飞书"
|
msgstr "飞书"
|
||||||
|
|
||||||
|
@ -2186,7 +2186,7 @@ msgstr ""
|
||||||
"div>"
|
"div>"
|
||||||
|
|
||||||
#: notifications/backends/__init__.py:10 users/forms/profile.py:101
|
#: notifications/backends/__init__.py:10 users/forms/profile.py:101
|
||||||
#: users/models/user.py:606
|
#: users/models/user.py:607
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "邮件"
|
msgstr "邮件"
|
||||||
|
|
||||||
|
@ -2342,22 +2342,22 @@ msgstr "任务结束"
|
||||||
msgid "Server performance"
|
msgid "Server performance"
|
||||||
msgstr "监控告警"
|
msgstr "监控告警"
|
||||||
|
|
||||||
#: ops/notifications.py:40
|
#: ops/notifications.py:47
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "The terminal is offline: {name}"
|
msgid "The terminal is offline: {name}"
|
||||||
msgstr "终端已离线: {name}"
|
msgstr "终端已离线: {name}"
|
||||||
|
|
||||||
#: ops/notifications.py:46
|
#: ops/notifications.py:53
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "[Disk] Disk used more than {max_threshold}%: => {value} ({name})"
|
msgid "[Disk] Disk used more than {max_threshold}%: => {value} ({name})"
|
||||||
msgstr "[Disk] 硬盘使用率超过 {max_threshold}%: => {value} ({name})"
|
msgstr "[Disk] 硬盘使用率超过 {max_threshold}%: => {value} ({name})"
|
||||||
|
|
||||||
#: ops/notifications.py:53
|
#: ops/notifications.py:60
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "[Memory] Memory used more than {max_threshold}%: => {value} ({name})"
|
msgid "[Memory] Memory used more than {max_threshold}%: => {value} ({name})"
|
||||||
msgstr "[Memory] 内存使用率超过 {max_threshold}%: => {value} ({name})"
|
msgstr "[Memory] 内存使用率超过 {max_threshold}%: => {value} ({name})"
|
||||||
|
|
||||||
#: ops/notifications.py:60
|
#: ops/notifications.py:67
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "[CPU] CPU load more than {max_threshold}: => {value} ({name})"
|
msgid "[CPU] CPU load more than {max_threshold}: => {value} ({name})"
|
||||||
msgstr "[CPU] CPU 使用率超过 {max_threshold}: => {value} ({name})"
|
msgstr "[CPU] CPU 使用率超过 {max_threshold}: => {value} ({name})"
|
||||||
|
@ -2404,7 +2404,7 @@ msgstr "组织审计员"
|
||||||
msgid "GLOBAL"
|
msgid "GLOBAL"
|
||||||
msgstr "全局组织"
|
msgstr "全局组织"
|
||||||
|
|
||||||
#: orgs/models.py:434 users/models/user.py:614 users/serializers/user.py:37
|
#: orgs/models.py:434 users/models/user.py:615 users/serializers/user.py:37
|
||||||
#: users/templates/users/_select_user_modal.html:15
|
#: users/templates/users/_select_user_modal.html:15
|
||||||
msgid "Role"
|
msgid "Role"
|
||||||
msgstr "角色"
|
msgstr "角色"
|
||||||
|
@ -2469,7 +2469,7 @@ msgid "Favorite"
|
||||||
msgstr "收藏夹"
|
msgstr "收藏夹"
|
||||||
|
|
||||||
#: perms/models/base.py:47 templates/_nav.html:21 users/models/group.py:31
|
#: perms/models/base.py:47 templates/_nav.html:21 users/models/group.py:31
|
||||||
#: users/models/user.py:610 users/templates/users/_select_user_modal.html:16
|
#: users/models/user.py:611 users/templates/users/_select_user_modal.html:16
|
||||||
#: users/templates/users/user_asset_permission.html:39
|
#: users/templates/users/user_asset_permission.html:39
|
||||||
#: users/templates/users/user_asset_permission.html:67
|
#: users/templates/users/user_asset_permission.html:67
|
||||||
#: users/templates/users/user_database_app_permission.html:38
|
#: users/templates/users/user_database_app_permission.html:38
|
||||||
|
@ -2478,9 +2478,9 @@ msgid "User group"
|
||||||
msgstr "用户组"
|
msgstr "用户组"
|
||||||
|
|
||||||
#: perms/models/base.py:50
|
#: perms/models/base.py:50
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:56
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:58
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:48
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:50
|
||||||
#: users/models/user.py:642
|
#: users/models/user.py:643
|
||||||
msgid "Date expired"
|
msgid "Date expired"
|
||||||
msgstr "失效日期"
|
msgstr "失效日期"
|
||||||
|
|
||||||
|
@ -2583,7 +2583,7 @@ msgstr "成功导入 {} 个用户 ( 组织: {} )"
|
||||||
msgid "Welcome to the JumpServer open source Bastion Host"
|
msgid "Welcome to the JumpServer open source Bastion Host"
|
||||||
msgstr "欢迎使用JumpServer开源堡垒机"
|
msgstr "欢迎使用JumpServer开源堡垒机"
|
||||||
|
|
||||||
#: settings/models.py:155 users/templates/users/reset_password.html:29
|
#: settings/models.py:191 users/templates/users/reset_password.html:29
|
||||||
msgid "Setting"
|
msgid "Setting"
|
||||||
msgstr "设置"
|
msgstr "设置"
|
||||||
|
|
||||||
|
@ -3132,7 +3132,7 @@ msgstr "下个设备登录,上次登录会被顶掉"
|
||||||
msgid "Only exist user login"
|
msgid "Only exist user login"
|
||||||
msgstr "仅已存在用户登录"
|
msgstr "仅已存在用户登录"
|
||||||
|
|
||||||
#: settings/serializers/security.py:66 settings/serializers/security.py:70
|
#: settings/serializers/security.py:66
|
||||||
msgid "If enable, CAS、OIDC auth will be failed, if user not exist yet"
|
msgid "If enable, CAS、OIDC auth will be failed, if user not exist yet"
|
||||||
msgstr "开启后,如果系统中不存在该用户,CAS、OIDC 登录将会失败"
|
msgstr "开启后,如果系统中不存在该用户,CAS、OIDC 登录将会失败"
|
||||||
|
|
||||||
|
@ -3140,6 +3140,10 @@ msgstr "开启后,如果系统中不存在该用户,CAS、OIDC 登录将会
|
||||||
msgid "Only from source login"
|
msgid "Only from source login"
|
||||||
msgstr "仅从用户来源登录"
|
msgstr "仅从用户来源登录"
|
||||||
|
|
||||||
|
#: settings/serializers/security.py:70
|
||||||
|
msgid "Only log in from the user source property"
|
||||||
|
msgstr "开启后,如果用户来源为本地,CAS、OIDC 登录将会失败"
|
||||||
|
|
||||||
#: settings/serializers/security.py:74
|
#: settings/serializers/security.py:74
|
||||||
msgid "MFA verify TTL"
|
msgid "MFA verify TTL"
|
||||||
msgstr "MFA 校验有效期"
|
msgstr "MFA 校验有效期"
|
||||||
|
@ -3980,7 +3984,7 @@ msgstr "链接失效"
|
||||||
|
|
||||||
#: terminal/models/sharing.py:50
|
#: terminal/models/sharing.py:50
|
||||||
msgid "Link expired"
|
msgid "Link expired"
|
||||||
msgstr "是否过期"
|
msgstr "链接过期"
|
||||||
|
|
||||||
#: terminal/models/sharing.py:63
|
#: terminal/models/sharing.py:63
|
||||||
msgid "Session sharing"
|
msgid "Session sharing"
|
||||||
|
@ -4539,52 +4543,52 @@ msgstr "你有一个新的工单, 申请人 - {}"
|
||||||
msgid "Your ticket has been processed, processor - {}"
|
msgid "Your ticket has been processed, processor - {}"
|
||||||
msgstr "你的工单已被处理, 处理人 - {}"
|
msgstr "你的工单已被处理, 处理人 - {}"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:16
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:18
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:16
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:18
|
||||||
msgid "Apply name"
|
msgid "Apply name"
|
||||||
msgstr "应用名称"
|
msgstr "应用名称"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:35
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:37
|
||||||
msgid "Apply applications"
|
msgid "Apply applications"
|
||||||
msgstr "申请应用"
|
msgstr "申请应用"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:40
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:42
|
||||||
msgid "Apply applications display"
|
msgid "Apply applications display"
|
||||||
msgstr "应用名称名称"
|
msgstr "应用名称名称"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:44
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:46
|
||||||
msgid "Apply system users"
|
msgid "Apply system users"
|
||||||
msgstr "系统用户"
|
msgstr "系统用户"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:49
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:51
|
||||||
msgid "Apply system user display"
|
msgid "Apply system user display"
|
||||||
msgstr "批准的系统用户名称"
|
msgstr "批准的系统用户名称"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:69
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:71
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:61
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:63
|
||||||
#: tickets/serializers/ticket/ticket.py:127
|
#: tickets/serializers/ticket/ticket.py:127
|
||||||
msgid "Permission named `{}` already exists"
|
msgid "Permission named `{}` already exists"
|
||||||
msgstr "授权名称 `{}` 已存在"
|
msgstr "授权名称 `{}` 已存在"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:75
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:80
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:67
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:72
|
||||||
msgid "The expiration date should be greater than the start date"
|
msgid "The expiration date should be greater than the start date"
|
||||||
msgstr "过期时间要大于开始时间"
|
msgstr "过期时间要大于开始时间"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:20
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:22
|
||||||
msgid "Apply assets"
|
msgid "Apply assets"
|
||||||
msgstr "申请资产"
|
msgstr "申请资产"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:24
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:26
|
||||||
msgid "Approve assets display"
|
msgid "Approve assets display"
|
||||||
msgstr "批准的资产名称"
|
msgstr "批准的资产名称"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:29
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:31
|
||||||
msgid "Approve system users"
|
msgid "Approve system users"
|
||||||
msgstr "批准的系统用户"
|
msgstr "批准的系统用户"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:33
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:35
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:41
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:43
|
||||||
msgid "Apply assets display"
|
msgid "Apply assets display"
|
||||||
msgstr "批准的资产名称"
|
msgstr "批准的资产名称"
|
||||||
|
|
||||||
|
@ -4758,7 +4762,7 @@ msgstr "不能和原来的密钥相同"
|
||||||
msgid "Not a valid ssh public key"
|
msgid "Not a valid ssh public key"
|
||||||
msgstr "SSH密钥不合法"
|
msgstr "SSH密钥不合法"
|
||||||
|
|
||||||
#: users/forms/profile.py:160 users/models/user.py:634
|
#: users/forms/profile.py:160 users/models/user.py:635
|
||||||
#: users/templates/users/user_password_update.html:48
|
#: users/templates/users/user_password_update.html:48
|
||||||
msgid "Public key"
|
msgid "Public key"
|
||||||
msgstr "SSH公钥"
|
msgstr "SSH公钥"
|
||||||
|
@ -4771,49 +4775,49 @@ msgstr "短信验证码"
|
||||||
msgid "Force enable"
|
msgid "Force enable"
|
||||||
msgstr "强制启用"
|
msgstr "强制启用"
|
||||||
|
|
||||||
#: users/models/user.py:583
|
#: users/models/user.py:584
|
||||||
msgid "Local"
|
msgid "Local"
|
||||||
msgstr "数据库"
|
msgstr "数据库"
|
||||||
|
|
||||||
#: users/models/user.py:617
|
#: users/models/user.py:618
|
||||||
msgid "Avatar"
|
msgid "Avatar"
|
||||||
msgstr "头像"
|
msgstr "头像"
|
||||||
|
|
||||||
#: users/models/user.py:620
|
#: users/models/user.py:621
|
||||||
msgid "Wechat"
|
msgid "Wechat"
|
||||||
msgstr "微信"
|
msgstr "微信"
|
||||||
|
|
||||||
#: users/models/user.py:631
|
#: users/models/user.py:632
|
||||||
msgid "Private key"
|
msgid "Private key"
|
||||||
msgstr "ssh私钥"
|
msgstr "ssh私钥"
|
||||||
|
|
||||||
#: users/models/user.py:650
|
#: users/models/user.py:651
|
||||||
msgid "Source"
|
msgid "Source"
|
||||||
msgstr "来源"
|
msgstr "来源"
|
||||||
|
|
||||||
#: users/models/user.py:654
|
#: users/models/user.py:655
|
||||||
msgid "Date password last updated"
|
msgid "Date password last updated"
|
||||||
msgstr "最后更新密码日期"
|
msgstr "最后更新密码日期"
|
||||||
|
|
||||||
#: users/models/user.py:657
|
#: users/models/user.py:658
|
||||||
msgid "Need update password"
|
msgid "Need update password"
|
||||||
msgstr "需要更新密码"
|
msgstr "需要更新密码"
|
||||||
|
|
||||||
#: users/models/user.py:817
|
#: users/models/user.py:822
|
||||||
msgid "Administrator"
|
msgid "Administrator"
|
||||||
msgstr "管理员"
|
msgstr "管理员"
|
||||||
|
|
||||||
#: users/models/user.py:820
|
#: users/models/user.py:825
|
||||||
msgid "Administrator is the super user of system"
|
msgid "Administrator is the super user of system"
|
||||||
msgstr "Administrator是初始的超级管理员"
|
msgstr "Administrator是初始的超级管理员"
|
||||||
|
|
||||||
#: users/notifications.py:17 users/notifications.py:50
|
#: users/notifications.py:19 users/notifications.py:52
|
||||||
#: users/templates/users/reset_password.html:5
|
#: users/templates/users/reset_password.html:5
|
||||||
#: users/templates/users/reset_password.html:6
|
#: users/templates/users/reset_password.html:6
|
||||||
msgid "Reset password"
|
msgid "Reset password"
|
||||||
msgstr "重置密码"
|
msgstr "重置密码"
|
||||||
|
|
||||||
#: users/notifications.py:18
|
#: users/notifications.py:20
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -4853,7 +4857,7 @@ msgstr ""
|
||||||
"%(login_url)s\n"
|
"%(login_url)s\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
||||||
#: users/notifications.py:51
|
#: users/notifications.py:53
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -4897,12 +4901,12 @@ msgstr ""
|
||||||
" <br>\n"
|
" <br>\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: users/notifications.py:90 users/notifications.py:122
|
#: users/notifications.py:92 users/notifications.py:124
|
||||||
#: users/views/profile/reset.py:127
|
#: users/views/profile/reset.py:127
|
||||||
msgid "Reset password success"
|
msgid "Reset password success"
|
||||||
msgstr "重置密码成功"
|
msgstr "重置密码成功"
|
||||||
|
|
||||||
#: users/notifications.py:91
|
#: users/notifications.py:93
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -4948,7 +4952,7 @@ msgstr ""
|
||||||
" \n"
|
" \n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: users/notifications.py:123
|
#: users/notifications.py:125
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -5011,11 +5015,11 @@ msgstr ""
|
||||||
" \n"
|
" \n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: users/notifications.py:166 users/notifications.py:202
|
#: users/notifications.py:170 users/notifications.py:206
|
||||||
msgid "Security notice"
|
msgid "Security notice"
|
||||||
msgstr "安全通知"
|
msgstr "安全通知"
|
||||||
|
|
||||||
#: users/notifications.py:167
|
#: users/notifications.py:171
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -5059,7 +5063,7 @@ msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: users/notifications.py:203
|
#: users/notifications.py:207
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -5108,11 +5112,11 @@ msgstr ""
|
||||||
" <br>\n"
|
" <br>\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: users/notifications.py:240 users/notifications.py:259
|
#: users/notifications.py:244 users/notifications.py:263
|
||||||
msgid "Expiration notice"
|
msgid "Expiration notice"
|
||||||
msgstr "过期通知"
|
msgstr "过期通知"
|
||||||
|
|
||||||
#: users/notifications.py:241
|
#: users/notifications.py:245
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -5133,7 +5137,7 @@ msgstr ""
|
||||||
"为了不影响您正常工作,请联系管理员确认。\n"
|
"为了不影响您正常工作,请联系管理员确认。\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: users/notifications.py:260
|
#: users/notifications.py:264
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -5155,11 +5159,11 @@ msgstr ""
|
||||||
" <br>\n"
|
" <br>\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: users/notifications.py:280 users/notifications.py:301
|
#: users/notifications.py:284 users/notifications.py:305
|
||||||
msgid "SSH Key Reset"
|
msgid "SSH Key Reset"
|
||||||
msgstr "重置SSH密钥"
|
msgstr "重置SSH密钥"
|
||||||
|
|
||||||
#: users/notifications.py:281
|
#: users/notifications.py:285
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -5184,7 +5188,7 @@ msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: users/notifications.py:302
|
#: users/notifications.py:306
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -5209,11 +5213,11 @@ msgstr ""
|
||||||
" <br>\n"
|
" <br>\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: users/notifications.py:324 users/notifications.py:344
|
#: users/notifications.py:328 users/notifications.py:348
|
||||||
msgid "MFA Reset"
|
msgid "MFA Reset"
|
||||||
msgstr "重置 MFA"
|
msgstr "重置 MFA"
|
||||||
|
|
||||||
#: users/notifications.py:325
|
#: users/notifications.py:329
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -5238,7 +5242,7 @@ msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: users/notifications.py:345
|
#: users/notifications.py:349
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -5267,7 +5271,7 @@ msgstr ""
|
||||||
msgid "The old password is incorrect"
|
msgid "The old password is incorrect"
|
||||||
msgstr "旧密码错误"
|
msgstr "旧密码错误"
|
||||||
|
|
||||||
#: users/serializers/profile.py:36 users/serializers/user.py:140
|
#: users/serializers/profile.py:36 users/serializers/user.py:141
|
||||||
msgid "Password does not match security rules"
|
msgid "Password does not match security rules"
|
||||||
msgstr "密码不满足安全规则"
|
msgstr "密码不满足安全规则"
|
||||||
|
|
||||||
|
@ -5353,11 +5357,15 @@ msgstr "是否绑定了钉钉"
|
||||||
msgid "Is feishu bound"
|
msgid "Is feishu bound"
|
||||||
msgstr "是否绑定了飞书"
|
msgstr "是否绑定了飞书"
|
||||||
|
|
||||||
#: users/serializers/user.py:114
|
#: users/serializers/user.py:91
|
||||||
|
msgid "Is OTP bound"
|
||||||
|
msgstr "是否绑定了虚拟MFA"
|
||||||
|
|
||||||
|
#: users/serializers/user.py:115
|
||||||
msgid "Role limit to {}"
|
msgid "Role limit to {}"
|
||||||
msgstr "角色只能为 {}"
|
msgstr "角色只能为 {}"
|
||||||
|
|
||||||
#: users/serializers/user.py:234
|
#: users/serializers/user.py:235
|
||||||
msgid "name not unique"
|
msgid "name not unique"
|
||||||
msgstr "名称重复"
|
msgstr "名称重复"
|
||||||
|
|
||||||
|
@ -6243,3 +6251,6 @@ msgstr "旗舰版"
|
||||||
#: xpack/plugins/license/models.py:77
|
#: xpack/plugins/license/models.py:77
|
||||||
msgid "Community edition"
|
msgid "Community edition"
|
||||||
msgstr "社区版"
|
msgstr "社区版"
|
||||||
|
|
||||||
|
#~ msgid "Only "
|
||||||
|
#~ msgstr "仅能从用户配置来源登录"
|
||||||
|
|
|
@ -17,13 +17,20 @@ class ServerPerformanceMessage(SystemMessage):
|
||||||
def __init__(self, msg):
|
def __init__(self, msg):
|
||||||
self._msg = msg
|
self._msg = msg
|
||||||
|
|
||||||
def get_common_msg(self):
|
def get_html_msg(self) -> dict:
|
||||||
subject = self._msg[:80]
|
subject = self._msg[:80]
|
||||||
return {
|
return {
|
||||||
'subject': subject.replace('<br>', '; '),
|
'subject': subject.replace('<br>', '; '),
|
||||||
'message': self._msg
|
'message': self._msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_text_msg(self) -> dict:
|
||||||
|
subject = self._msg[:80]
|
||||||
|
return {
|
||||||
|
'subject': subject.replace('<br>', '; '),
|
||||||
|
'message': self._msg.replace('<br>', '\n')
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def post_insert_to_db(cls, subscription: SystemMsgSubscription):
|
def post_insert_to_db(cls, subscription: SystemMsgSubscription):
|
||||||
admins = User.objects.filter(role=User.ROLE.ADMIN)
|
admins = User.objects.filter(role=User.ROLE.ADMIN)
|
||||||
|
@ -39,14 +46,14 @@ class ServerPerformanceCheckUtil(object):
|
||||||
'max_threshold': False,
|
'max_threshold': False,
|
||||||
'alarm_msg_format': _('The terminal is offline: {name}')
|
'alarm_msg_format': _('The terminal is offline: {name}')
|
||||||
},
|
},
|
||||||
'disk_usage': {
|
'disk_used': {
|
||||||
'default': 0,
|
'default': 0,
|
||||||
'max_threshold': 80,
|
'max_threshold': 80,
|
||||||
'alarm_msg_format': _(
|
'alarm_msg_format': _(
|
||||||
'[Disk] Disk used more than {max_threshold}%: => {value} ({name})'
|
'[Disk] Disk used more than {max_threshold}%: => {value} ({name})'
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
'memory_usage': {
|
'memory_used': {
|
||||||
'default': 0,
|
'default': 0,
|
||||||
'max_threshold': 85,
|
'max_threshold': 85,
|
||||||
'alarm_msg_format': _(
|
'alarm_msg_format': _(
|
||||||
|
@ -82,7 +89,6 @@ class ServerPerformanceCheckUtil(object):
|
||||||
default = data['default']
|
default = data['default']
|
||||||
max_threshold = data['max_threshold']
|
max_threshold = data['max_threshold']
|
||||||
value = getattr(self._terminal.stat, item, default)
|
value = getattr(self._terminal.stat, item, default)
|
||||||
print(value, max_threshold, self._terminal.name, self._terminal.id)
|
|
||||||
if isinstance(value, bool) and value != max_threshold:
|
if isinstance(value, bool) and value != max_threshold:
|
||||||
return
|
return
|
||||||
elif isinstance(value, (int, float)) and value < max_threshold:
|
elif isinstance(value, (int, float)) and value < max_threshold:
|
||||||
|
|
|
@ -84,6 +84,7 @@ class Setting(models.Model):
|
||||||
getattr(self.__class__, f'refresh_{self.name}')()
|
getattr(self.__class__, f'refresh_{self.name}')()
|
||||||
else:
|
else:
|
||||||
setattr(settings, self.name, self.cleaned_value)
|
setattr(settings, self.name, self.cleaned_value)
|
||||||
|
self.refresh_keycloak_to_openid_if_need()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def refresh_authentications(cls, name):
|
def refresh_authentications(cls, name):
|
||||||
|
@ -129,6 +130,41 @@ class Setting(models.Model):
|
||||||
def refresh_AUTH_OPENID(cls):
|
def refresh_AUTH_OPENID(cls):
|
||||||
cls.refresh_authentications('AUTH_OPENID')
|
cls.refresh_authentications('AUTH_OPENID')
|
||||||
|
|
||||||
|
def refresh_keycloak_to_openid_if_need(self):
|
||||||
|
watch_config_names = [
|
||||||
|
'AUTH_OPENID', 'AUTH_OPENID_REALM_NAME', 'AUTH_OPENID_SERVER_URL',
|
||||||
|
'AUTH_OPENID_PROVIDER_ENDPOINT', 'AUTH_OPENID_KEYCLOAK'
|
||||||
|
]
|
||||||
|
if self.name not in watch_config_names:
|
||||||
|
# 不在监听的配置中, 不需要刷新
|
||||||
|
return
|
||||||
|
auth_keycloak = self.__class__.objects.filter(name='AUTH_OPENID_KEYCLOAK').first()
|
||||||
|
if not auth_keycloak or not auth_keycloak.cleaned_value:
|
||||||
|
# 关闭 Keycloak 方式的配置, 不需要刷新
|
||||||
|
return
|
||||||
|
|
||||||
|
from jumpserver.conf import Config
|
||||||
|
config_names = [
|
||||||
|
'AUTH_OPENID', 'AUTH_OPENID_REALM_NAME',
|
||||||
|
'AUTH_OPENID_SERVER_URL', 'AUTH_OPENID_PROVIDER_ENDPOINT'
|
||||||
|
]
|
||||||
|
# 获取当前 keycloak 配置
|
||||||
|
keycloak_config = {}
|
||||||
|
for name in config_names:
|
||||||
|
setting = self.__class__.objects.filter(name=name).first()
|
||||||
|
if not setting:
|
||||||
|
continue
|
||||||
|
value = setting.cleaned_value
|
||||||
|
keycloak_config[name] = value
|
||||||
|
|
||||||
|
# 转化 keycloak 配置为 openid 配置
|
||||||
|
openid_config = Config.convert_keycloak_to_openid(keycloak_config)
|
||||||
|
if not openid_config:
|
||||||
|
return
|
||||||
|
# 刷新 settings
|
||||||
|
for key, value in openid_config.items():
|
||||||
|
setattr(settings, key, value)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def refresh_AUTH_RADIUS(cls):
|
def refresh_AUTH_RADIUS(cls):
|
||||||
cls.refresh_authentications('AUTH_RADIUS')
|
cls.refresh_authentications('AUTH_RADIUS')
|
||||||
|
|
|
@ -67,10 +67,10 @@ class SecurityAuthSerializer(serializers.Serializer):
|
||||||
)
|
)
|
||||||
ONLY_ALLOW_AUTH_FROM_SOURCE = serializers.BooleanField(
|
ONLY_ALLOW_AUTH_FROM_SOURCE = serializers.BooleanField(
|
||||||
required=False, default=False, label=_("Only from source login"),
|
required=False, default=False, label=_("Only from source login"),
|
||||||
help_text=_("If enable, CAS、OIDC auth will be failed, if user not exist yet")
|
help_text=_("Only log in from the user source property")
|
||||||
)
|
)
|
||||||
SECURITY_MFA_VERIFY_TTL = serializers.IntegerField(
|
SECURITY_MFA_VERIFY_TTL = serializers.IntegerField(
|
||||||
min_value=5, max_value=60*30,
|
min_value=5, max_value=60*60*10,
|
||||||
label=_("MFA verify TTL"), help_text=_("Unit: second"),
|
label=_("MFA verify TTL"), help_text=_("Unit: second"),
|
||||||
)
|
)
|
||||||
SECURITY_LOGIN_CAPTCHA_ENABLED = serializers.BooleanField(
|
SECURITY_LOGIN_CAPTCHA_ENABLED = serializers.BooleanField(
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
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 perms.models import ApplicationPermission
|
from perms.models import ApplicationPermission
|
||||||
from applications.const import AppCategory, AppType
|
from applications.const import AppCategory, AppType
|
||||||
from orgs.utils import tmp_to_org
|
from orgs.utils import tmp_to_org
|
||||||
from tickets.models import Ticket
|
from tickets.models import Ticket
|
||||||
|
from applications.models import Application
|
||||||
|
from assets.models import SystemUser
|
||||||
from .common import DefaultPermissionName
|
from .common import DefaultPermissionName
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -69,9 +73,19 @@ class ApplySerializer(serializers.Serializer):
|
||||||
'Permission named `{}` already exists'.format(permission_name)
|
'Permission named `{}` already exists'.format(permission_name)
|
||||||
))
|
))
|
||||||
|
|
||||||
def validate_apply_date_expired(self, apply_date_expired):
|
def validate_apply_applications(self, apply_applications):
|
||||||
apply_date_start = self.root.initial_data['meta'].get('apply_date_start')
|
type = self.root.initial_data['meta'].get('apply_type')
|
||||||
if str(apply_date_expired) <= apply_date_start:
|
org_id = self.root.initial_data.get('org_id')
|
||||||
|
with tmp_to_org(org_id):
|
||||||
|
applications = Application.objects.filter(id__in=apply_applications, type=type).values_list('id', flat=True)
|
||||||
|
return list(applications)
|
||||||
|
|
||||||
|
def validate_apply_date_expired(self, value):
|
||||||
|
date_start = self.root.initial_data['meta'].get('apply_date_start')
|
||||||
|
date_start = datetime.strptime(date_start, '%Y-%m-%dT%H:%M:%S.%fZ')
|
||||||
|
date_expired = self.root.initial_data['meta'].get('apply_date_expired')
|
||||||
|
date_expired = datetime.strptime(date_expired, '%Y-%m-%dT%H:%M:%S.%fZ')
|
||||||
|
if date_expired <= date_start:
|
||||||
error = _('The expiration date should be greater than the start date')
|
error = _('The expiration date should be greater than the start date')
|
||||||
raise serializers.ValidationError(error)
|
raise serializers.ValidationError(error)
|
||||||
return apply_date_expired
|
return value
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
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 perms.serializers import ActionsField
|
from perms.serializers import ActionsField
|
||||||
|
@ -61,9 +63,12 @@ class ApplySerializer(serializers.Serializer):
|
||||||
'Permission named `{}` already exists'.format(permission_name)
|
'Permission named `{}` already exists'.format(permission_name)
|
||||||
))
|
))
|
||||||
|
|
||||||
def validate_apply_date_expired(self, apply_date_expired):
|
def validate_apply_date_expired(self, value):
|
||||||
apply_date_start = self.root.initial_data['meta'].get('apply_date_start')
|
date_start = self.root.initial_data['meta'].get('apply_date_start')
|
||||||
if str(apply_date_expired) <= apply_date_start:
|
date_start = datetime.strptime(date_start, '%Y-%m-%dT%H:%M:%S.%fZ')
|
||||||
|
date_expired = self.root.initial_data['meta'].get('apply_date_expired')
|
||||||
|
date_expired = datetime.strptime(date_expired, '%Y-%m-%dT%H:%M:%S.%fZ')
|
||||||
|
if date_expired <= date_start:
|
||||||
error = _('The expiration date should be greater than the start date')
|
error = _('The expiration date should be greater than the start date')
|
||||||
raise serializers.ValidationError(error)
|
raise serializers.ValidationError(error)
|
||||||
return apply_date_expired
|
return value
|
||||||
|
|
|
@ -472,6 +472,7 @@ class MFAMixin:
|
||||||
(2, _("Force enable")),
|
(2, _("Force enable")),
|
||||||
)
|
)
|
||||||
is_org_admin: bool
|
is_org_admin: bool
|
||||||
|
username: str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mfa_enabled(self):
|
def mfa_enabled(self):
|
||||||
|
@ -685,6 +686,10 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
|
||||||
def is_feishu_bound(self):
|
def is_feishu_bound(self):
|
||||||
return bool(self.feishu_id)
|
return bool(self.feishu_id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_otp_secret_key_bound(self):
|
||||||
|
return bool(self.otp_secret_key)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('users:user-detail', args=(self.id,))
|
return reverse('users:user-detail', args=(self.id,))
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from common.utils import reverse, get_request_ip_or_data, get_request_user_agent, lazyproperty
|
from common.utils import reverse, get_request_ip_or_data, get_request_user_agent, lazyproperty
|
||||||
from notifications.notifications import UserMessage
|
from notifications.notifications import UserMessage
|
||||||
|
@ -160,6 +162,8 @@ Browser: %(browser)s
|
||||||
|
|
||||||
|
|
||||||
class PasswordExpirationReminderMsg(UserMessage):
|
class PasswordExpirationReminderMsg(UserMessage):
|
||||||
|
update_password_url = urljoin(settings.SITE_URL, '/ui/#/users/profile/?activeTab=PasswordUpdate')
|
||||||
|
|
||||||
def get_text_msg(self) -> dict:
|
def get_text_msg(self) -> dict:
|
||||||
user = self.user
|
user = self.user
|
||||||
|
|
||||||
|
@ -186,7 +190,7 @@ Login direct 👇
|
||||||
'name': user.name,
|
'name': user.name,
|
||||||
'date_password_expired': datetime.fromtimestamp(datetime.timestamp(
|
'date_password_expired': datetime.fromtimestamp(datetime.timestamp(
|
||||||
user.date_password_expired)).strftime('%Y-%m-%d %H:%M'),
|
user.date_password_expired)).strftime('%Y-%m-%d %H:%M'),
|
||||||
'update_password_url': reverse('users:user-password-update', external=True),
|
'update_password_url': self.update_password_url,
|
||||||
'forget_password_url': reverse('authentication:forgot-password', external=True),
|
'forget_password_url': reverse('authentication:forgot-password', external=True),
|
||||||
'email': user.email,
|
'email': user.email,
|
||||||
'login_url': reverse('authentication:login', external=True),
|
'login_url': reverse('authentication:login', external=True),
|
||||||
|
@ -224,7 +228,7 @@ Login direct 👇
|
||||||
'name': user.name,
|
'name': user.name,
|
||||||
'date_password_expired': datetime.fromtimestamp(datetime.timestamp(
|
'date_password_expired': datetime.fromtimestamp(datetime.timestamp(
|
||||||
user.date_password_expired)).strftime('%Y-%m-%d %H:%M'),
|
user.date_password_expired)).strftime('%Y-%m-%d %H:%M'),
|
||||||
'update_password_url': reverse('users:user-password-update', external=True),
|
'update_password_url': self.update_password_url,
|
||||||
'forget_password_url': reverse('authentication:forgot-password', external=True),
|
'forget_password_url': reverse('authentication:forgot-password', external=True),
|
||||||
'email': user.email,
|
'email': user.email,
|
||||||
'login_url': reverse('authentication:login', external=True),
|
'login_url': reverse('authentication:login', external=True),
|
||||||
|
|
|
@ -51,7 +51,7 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer):
|
||||||
'mfa_enabled', 'is_valid', 'is_expired', 'is_active', # 布尔字段
|
'mfa_enabled', 'is_valid', 'is_expired', 'is_active', # 布尔字段
|
||||||
'date_expired', 'date_joined', 'last_login', # 日期字段
|
'date_expired', 'date_joined', 'last_login', # 日期字段
|
||||||
'created_by', 'comment', # 通用字段
|
'created_by', 'comment', # 通用字段
|
||||||
'is_wecom_bound', 'is_dingtalk_bound', 'is_feishu_bound',
|
'is_wecom_bound', 'is_dingtalk_bound', 'is_feishu_bound', 'is_otp_secret_key_bound',
|
||||||
'wecom_id', 'dingtalk_id', 'feishu_id'
|
'wecom_id', 'dingtalk_id', 'feishu_id'
|
||||||
]
|
]
|
||||||
# 包含不太常用的字段,可以没有
|
# 包含不太常用的字段,可以没有
|
||||||
|
@ -88,6 +88,7 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer):
|
||||||
'is_wecom_bound': {'label': _('Is wecom bound')},
|
'is_wecom_bound': {'label': _('Is wecom bound')},
|
||||||
'is_dingtalk_bound': {'label': _('Is dingtalk bound')},
|
'is_dingtalk_bound': {'label': _('Is dingtalk bound')},
|
||||||
'is_feishu_bound': {'label': _('Is feishu bound')},
|
'is_feishu_bound': {'label': _('Is feishu bound')},
|
||||||
|
'is_otp_secret_key_bound': {'label': _('Is OTP bound')},
|
||||||
'phone': {'validators': [PhoneValidator()]},
|
'phone': {'validators': [PhoneValidator()]},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue