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