perf(settings): 优化settings配置 (#5515)

* stash

* perf: 优化 动态seting

* perf(settings): 优化settings配置

* perf: 完成终端和安全setting

* perf: 修改翻译

* perf: 去掉其他位置的DYNAMIC

* perf: 还原回来原来的一些代码

* perf: 优化ldap

* perf: 移除dynmic config

* perf: 去掉debug消息

* perf: 优化 refresh 命名

Co-authored-by: ibuler <ibuler@qq.com>
pull/5541/head
fit2bot 2021-01-26 17:54:12 +08:00 committed by GitHub
parent 351d4d8123
commit d363118911
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 858 additions and 490 deletions

View File

@ -1,40 +1,7 @@
# -*- coding: utf-8 -*-
#
from jumpserver.const import DYNAMIC
from werkzeug.local import Local, LocalProxy
from werkzeug.local import Local
thread_local = Local()
def _find(attr):
return getattr(thread_local, attr, None)
class _Settings:
pass
def get_dynamic_cfg_from_thread_local():
KEY = 'dynamic_config'
try:
cfg = getattr(thread_local, KEY)
except AttributeError:
cfg = _Settings()
setattr(thread_local, KEY, cfg)
return cfg
class DynamicDefaultLocalProxy(LocalProxy):
def __getattr__(self, item):
try:
value = super().__getattr__(item)
except AttributeError:
value = getattr(DYNAMIC, item)()
setattr(self, item, value)
return value
LOCAL_DYNAMIC_SETTINGS = DynamicDefaultLocalProxy(get_dynamic_cfg_from_thread_local)

View File

@ -5,16 +5,12 @@ import os
import logging
from collections import defaultdict
from django.conf import settings
from django.dispatch import receiver
from django.core.signals import request_finished
from django.db import connection
from django.conf import LazySettings
from django.db.utils import ProgrammingError, OperationalError
from jumpserver.utils import get_current_request
from .local import thread_local
from .signals import django_ready
pattern = re.compile(r'FROM `(\w+)`')
logger = logging.getLogger("jumpserver.common")
@ -74,17 +70,3 @@ if settings.DEBUG and DEBUG_DB:
request_finished.connect(on_request_finished_logging_db_query)
else:
request_finished.connect(on_request_finished_release_local)
@receiver(django_ready)
def monkey_patch_settings(sender, **kwargs):
def monkey_patch_getattr(self, name):
val = getattr(self._wrapped, name)
if callable(val):
val = val()
return val
try:
LazySettings.__getattr__ = monkey_patch_getattr
except (ProgrammingError, OperationalError):
pass

View File

@ -0,0 +1,27 @@
import redis
from django.conf import settings
def get_redis_client(db):
rc = redis.StrictRedis(
host=settings.REDIS_HOST,
port=settings.REDIS_PORT,
password=settings.REDIS_PASSWORD,
db=db
)
return rc
class RedisPubSub:
def __init__(self, ch, db=10):
self.ch = ch
self.redis = get_redis_client(db)
def subscribe(self):
ps = self.redis.pubsub()
ps.subscribe(self.ch)
return ps
def publish(self, data):
self.redis.publish(self.ch, data)
return True

View File

@ -426,98 +426,6 @@ class Config(dict):
return self.get(item)
class DynamicConfig:
def __init__(self, static_config):
self.static_config = static_config
self.db_setting = None
def __getitem__(self, item):
return self.dynamic(item)
def __getattr__(self, item):
return self.dynamic(item)
def dynamic(self, item):
return lambda: self.get(item)
def LOGIN_URL(self):
return self.get('LOGIN_URL')
def AUTHENTICATION_BACKENDS(self):
backends = [
'authentication.backends.pubkey.PublicKeyAuthBackend',
'django.contrib.auth.backends.ModelBackend',
]
if self.get('AUTH_LDAP'):
backends.insert(0, 'authentication.backends.ldap.LDAPAuthorizationBackend')
if self.static_config.get('AUTH_CAS'):
backends.insert(0, 'authentication.backends.cas.CASBackend')
if self.static_config.get('AUTH_OPENID'):
backends.insert(0, 'jms_oidc_rp.backends.OIDCAuthPasswordBackend')
backends.insert(0, 'jms_oidc_rp.backends.OIDCAuthCodeBackend')
if self.static_config.get('AUTH_RADIUS'):
backends.insert(0, 'authentication.backends.radius.RadiusBackend')
if self.static_config.get('AUTH_SSO'):
backends.insert(0, 'authentication.backends.api.SSOAuthentication')
return backends
def XPACK_LICENSE_IS_VALID(self):
if not HAS_XPACK:
return False
try:
from xpack.plugins.license.models import License
return License.has_valid_license()
except:
return False
def XPACK_INTERFACE_LOGIN_TITLE(self):
default_title = _('Welcome to the JumpServer open source fortress')
if not HAS_XPACK:
return default_title
try:
from xpack.plugins.interface.models import Interface
return Interface.get_login_title()
except:
return default_title
def LOGO_URLS(self):
logo_urls = {'logo_logout': static('img/logo.png'),
'logo_index': static('img/logo_text.png'),
'login_image': static('img/login_image.png'),
'favicon': static('img/facio.ico')}
if not HAS_XPACK:
return logo_urls
try:
from xpack.plugins.interface.models import Interface
obj = Interface.interface()
if obj:
if obj.logo_logout:
logo_urls.update({'logo_logout': obj.logo_logout.url})
if obj.logo_index:
logo_urls.update({'logo_index': obj.logo_index.url})
if obj.login_image:
logo_urls.update({'login_image': obj.login_image.url})
if obj.favicon:
logo_urls.update({'favicon': obj.favicon.url})
except:
pass
return logo_urls
def get_from_db(self, item):
if self.db_setting is not None:
value = self.db_setting.get(item)
if value is not None:
return value
return None
def get(self, item):
# 先从数据库中获取
value = self.get_from_db(item)
if value is not None:
return value
return self.static_config.get(item)
class ConfigManager:
config_class = Config
@ -694,7 +602,3 @@ class ConfigManager:
# 对config进行兼容处理
config.compatible()
return config
@classmethod
def get_dynamic_config(cls, config):
return DynamicConfig(config)

View File

@ -4,12 +4,11 @@ import os
from .conf import ConfigManager
__all__ = ['BASE_DIR', 'PROJECT_DIR', 'VERSION', 'CONFIG', 'DYNAMIC']
__all__ = ['BASE_DIR', 'PROJECT_DIR', 'VERSION', 'CONFIG']
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_DIR = os.path.dirname(BASE_DIR)
VERSION = '2.0.0'
CONFIG = ConfigManager.load_user_config()
DYNAMIC = ConfigManager.get_dynamic_config(CONFIG)

View File

@ -3,21 +3,21 @@
import os
import ldap
from ..const import CONFIG, DYNAMIC, PROJECT_DIR
from ..const import CONFIG, PROJECT_DIR
# OTP settings
OTP_ISSUER_NAME = CONFIG.OTP_ISSUER_NAME
OTP_VALID_WINDOW = CONFIG.OTP_VALID_WINDOW
# Auth LDAP settings
AUTH_LDAP = DYNAMIC.AUTH_LDAP
AUTH_LDAP_SERVER_URI = DYNAMIC.AUTH_LDAP_SERVER_URI
AUTH_LDAP_BIND_DN = DYNAMIC.AUTH_LDAP_BIND_DN
AUTH_LDAP_BIND_PASSWORD = DYNAMIC.AUTH_LDAP_BIND_PASSWORD
AUTH_LDAP_SEARCH_OU = DYNAMIC.AUTH_LDAP_SEARCH_OU
AUTH_LDAP_SEARCH_FILTER = DYNAMIC.AUTH_LDAP_SEARCH_FILTER
AUTH_LDAP_START_TLS = DYNAMIC.AUTH_LDAP_START_TLS
AUTH_LDAP_USER_ATTR_MAP = DYNAMIC.AUTH_LDAP_USER_ATTR_MAP
AUTH_LDAP = CONFIG.AUTH_LDAP
AUTH_LDAP_SERVER_URI = CONFIG.AUTH_LDAP_SERVER_URI
AUTH_LDAP_BIND_DN = CONFIG.AUTH_LDAP_BIND_DN
AUTH_LDAP_BIND_PASSWORD = CONFIG.AUTH_LDAP_BIND_PASSWORD
AUTH_LDAP_SEARCH_OU = CONFIG.AUTH_LDAP_SEARCH_OU
AUTH_LDAP_SEARCH_FILTER = CONFIG.AUTH_LDAP_SEARCH_FILTER
AUTH_LDAP_START_TLS = CONFIG.AUTH_LDAP_START_TLS
AUTH_LDAP_USER_ATTR_MAP = CONFIG.AUTH_LDAP_USER_ATTR_MAP
AUTH_LDAP_USER_QUERY_FIELD = 'username'
AUTH_LDAP_GLOBAL_OPTIONS = {
ldap.OPT_X_TLS_REQUIRE_CERT: ldap.OPT_X_TLS_NEVER,
@ -105,4 +105,17 @@ TOKEN_EXPIRATION = CONFIG.TOKEN_EXPIRATION
LOGIN_CONFIRM_ENABLE = CONFIG.LOGIN_CONFIRM_ENABLE
OTP_IN_RADIUS = CONFIG.OTP_IN_RADIUS
AUTHENTICATION_BACKENDS = DYNAMIC.AUTHENTICATION_BACKENDS
AUTHENTICATION_BACKENDS = [
'authentication.backends.pubkey.PublicKeyAuthBackend',
'django.contrib.auth.backends.ModelBackend',
]
if AUTH_CAS:
AUTHENTICATION_BACKENDS.insert(0, 'authentication.backends.cas.CASBackend')
if AUTH_OPENID:
AUTHENTICATION_BACKENDS.insert(0, 'jms_oidc_rp.backends.OIDCAuthPasswordBackend')
AUTHENTICATION_BACKENDS.insert(0, 'jms_oidc_rp.backends.OIDCAuthCodeBackend')
if AUTH_RADIUS:
AUTHENTICATION_BACKENDS.insert(0, 'authentication.backends.radius.RadiusBackend')
if AUTH_SSO:
AUTHENTICATION_BACKENDS.insert(0, 'authentication.backends.api.SSOAuthentication')

View File

@ -3,7 +3,7 @@ import os
from django.urls import reverse_lazy
from .. import const
from ..const import CONFIG, DYNAMIC
from ..const import CONFIG
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
VERSION = const.VERSION
@ -23,7 +23,7 @@ BOOTSTRAP_TOKEN = CONFIG.BOOTSTRAP_TOKEN
DEBUG = CONFIG.DEBUG
# Absolute url for some case, for example email link
SITE_URL = DYNAMIC.SITE_URL
SITE_URL = CONFIG.SITE_URL
# LOG LEVEL
LOG_LEVEL = CONFIG.LOG_LEVEL
@ -216,14 +216,14 @@ MEDIA_ROOT = os.path.join(PROJECT_DIR, 'data', 'media').replace('\\', '/') + '/'
FIXTURE_DIRS = [os.path.join(BASE_DIR, 'fixtures'), ]
# Email config
EMAIL_HOST = DYNAMIC.EMAIL_HOST
EMAIL_PORT = DYNAMIC.EMAIL_PORT
EMAIL_HOST_USER = DYNAMIC.EMAIL_HOST_USER
EMAIL_HOST_PASSWORD = DYNAMIC.EMAIL_HOST_PASSWORD
EMAIL_FROM = DYNAMIC.EMAIL_FROM
EMAIL_RECIPIENT = DYNAMIC.EMAIL_RECIPIENT
EMAIL_USE_SSL = DYNAMIC.EMAIL_USE_SSL
EMAIL_USE_TLS = DYNAMIC.EMAIL_USE_TLS
EMAIL_HOST = CONFIG.EMAIL_HOST
EMAIL_PORT = CONFIG.EMAIL_PORT
EMAIL_HOST_USER = CONFIG.EMAIL_HOST_USER
EMAIL_HOST_PASSWORD = CONFIG.EMAIL_HOST_PASSWORD
EMAIL_FROM = CONFIG.EMAIL_FROM
EMAIL_RECIPIENT = CONFIG.EMAIL_RECIPIENT
EMAIL_USE_SSL = CONFIG.EMAIL_USE_SSL
EMAIL_USE_TLS = CONFIG.EMAIL_USE_TLS
# Custom User Auth model

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
from ..const import CONFIG, DYNAMIC
from ..const import CONFIG
# Storage settings
COMMAND_STORAGE = {
@ -11,7 +11,7 @@ DEFAULT_TERMINAL_COMMAND_STORAGE = {
"TYPE": "server",
},
}
TERMINAL_COMMAND_STORAGE = DYNAMIC.TERMINAL_COMMAND_STORAGE or {}
TERMINAL_COMMAND_STORAGE = CONFIG.TERMINAL_COMMAND_STORAGE or {}
# Server 类型的录像存储
SERVER_REPLAY_STORAGE = CONFIG.SERVER_REPLAY_STORAGE
@ -28,20 +28,20 @@ DEFAULT_TERMINAL_REPLAY_STORAGE = {
"TYPE": "server",
},
}
TERMINAL_REPLAY_STORAGE = DYNAMIC.TERMINAL_REPLAY_STORAGE
TERMINAL_REPLAY_STORAGE = CONFIG.TERMINAL_REPLAY_STORAGE
# Security settings
SECURITY_MFA_AUTH = DYNAMIC.SECURITY_MFA_AUTH
SECURITY_COMMAND_EXECUTION = DYNAMIC.SECURITY_COMMAND_EXECUTION
SECURITY_LOGIN_LIMIT_COUNT = DYNAMIC.SECURITY_LOGIN_LIMIT_COUNT
SECURITY_LOGIN_LIMIT_TIME = DYNAMIC.SECURITY_LOGIN_LIMIT_TIME # Unit: minute
SECURITY_MAX_IDLE_TIME = DYNAMIC.SECURITY_MAX_IDLE_TIME # Unit: minute
SECURITY_PASSWORD_EXPIRATION_TIME = DYNAMIC.SECURITY_PASSWORD_EXPIRATION_TIME # Unit: day
SECURITY_PASSWORD_MIN_LENGTH = DYNAMIC.SECURITY_PASSWORD_MIN_LENGTH # Unit: bit
SECURITY_PASSWORD_UPPER_CASE = DYNAMIC.SECURITY_PASSWORD_UPPER_CASE
SECURITY_PASSWORD_LOWER_CASE = DYNAMIC.SECURITY_PASSWORD_LOWER_CASE
SECURITY_PASSWORD_NUMBER = DYNAMIC.SECURITY_PASSWORD_NUMBER
SECURITY_PASSWORD_SPECIAL_CHAR = DYNAMIC.SECURITY_PASSWORD_SPECIAL_CHAR
SECURITY_MFA_AUTH = CONFIG.SECURITY_MFA_AUTH
SECURITY_COMMAND_EXECUTION = CONFIG.SECURITY_COMMAND_EXECUTION
SECURITY_LOGIN_LIMIT_COUNT = CONFIG.SECURITY_LOGIN_LIMIT_COUNT
SECURITY_LOGIN_LIMIT_TIME = CONFIG.SECURITY_LOGIN_LIMIT_TIME # Unit: minute
SECURITY_MAX_IDLE_TIME = CONFIG.SECURITY_MAX_IDLE_TIME # Unit: minute
SECURITY_PASSWORD_EXPIRATION_TIME = CONFIG.SECURITY_PASSWORD_EXPIRATION_TIME # Unit: day
SECURITY_PASSWORD_MIN_LENGTH = CONFIG.SECURITY_PASSWORD_MIN_LENGTH # Unit: bit
SECURITY_PASSWORD_UPPER_CASE = CONFIG.SECURITY_PASSWORD_UPPER_CASE
SECURITY_PASSWORD_LOWER_CASE = CONFIG.SECURITY_PASSWORD_LOWER_CASE
SECURITY_PASSWORD_NUMBER = CONFIG.SECURITY_PASSWORD_NUMBER
SECURITY_PASSWORD_SPECIAL_CHAR = CONFIG.SECURITY_PASSWORD_SPECIAL_CHAR
SECURITY_PASSWORD_RULES = [
'SECURITY_PASSWORD_MIN_LENGTH',
'SECURITY_PASSWORD_UPPER_CASE',
@ -51,24 +51,24 @@ SECURITY_PASSWORD_RULES = [
]
SECURITY_MFA_VERIFY_TTL = CONFIG.SECURITY_MFA_VERIFY_TTL
SECURITY_VIEW_AUTH_NEED_MFA = CONFIG.SECURITY_VIEW_AUTH_NEED_MFA
SECURITY_SERVICE_ACCOUNT_REGISTRATION = DYNAMIC.SECURITY_SERVICE_ACCOUNT_REGISTRATION
SECURITY_SERVICE_ACCOUNT_REGISTRATION = CONFIG.SECURITY_SERVICE_ACCOUNT_REGISTRATION
SECURITY_LOGIN_CAPTCHA_ENABLED = CONFIG.SECURITY_LOGIN_CAPTCHA_ENABLED
SECURITY_LOGIN_CHALLENGE_ENABLED = CONFIG.SECURITY_LOGIN_CHALLENGE_ENABLED
SECURITY_DATA_CRYPTO_ALGO = CONFIG.SECURITY_DATA_CRYPTO_ALGO
SECURITY_INSECURE_COMMAND = DYNAMIC.SECURITY_INSECURE_COMMAND
SECURITY_INSECURE_COMMAND = CONFIG.SECURITY_INSECURE_COMMAND
SECURITY_INSECURE_COMMAND_LEVEL = CONFIG.SECURITY_INSECURE_COMMAND_LEVEL
SECURITY_INSECURE_COMMAND_EMAIL_RECEIVER = DYNAMIC.SECURITY_INSECURE_COMMAND_EMAIL_RECEIVER
SECURITY_INSECURE_COMMAND_EMAIL_RECEIVER = CONFIG.SECURITY_INSECURE_COMMAND_EMAIL_RECEIVER
# Terminal other setting
TERMINAL_PASSWORD_AUTH = DYNAMIC.TERMINAL_PASSWORD_AUTH
TERMINAL_PUBLIC_KEY_AUTH = DYNAMIC.TERMINAL_PUBLIC_KEY_AUTH
TERMINAL_HEARTBEAT_INTERVAL = DYNAMIC.TERMINAL_HEARTBEAT_INTERVAL
TERMINAL_ASSET_LIST_SORT_BY = DYNAMIC.TERMINAL_ASSET_LIST_SORT_BY
TERMINAL_ASSET_LIST_PAGE_SIZE = DYNAMIC.TERMINAL_ASSET_LIST_PAGE_SIZE
TERMINAL_SESSION_KEEP_DURATION = DYNAMIC.TERMINAL_SESSION_KEEP_DURATION
TERMINAL_HOST_KEY = DYNAMIC.TERMINAL_HOST_KEY
TERMINAL_HEADER_TITLE = DYNAMIC.TERMINAL_HEADER_TITLE
TERMINAL_TELNET_REGEX = DYNAMIC.TERMINAL_TELNET_REGEX
TERMINAL_PASSWORD_AUTH = CONFIG.TERMINAL_PASSWORD_AUTH
TERMINAL_PUBLIC_KEY_AUTH = CONFIG.TERMINAL_PUBLIC_KEY_AUTH
TERMINAL_HEARTBEAT_INTERVAL = CONFIG.TERMINAL_HEARTBEAT_INTERVAL
TERMINAL_ASSET_LIST_SORT_BY = CONFIG.TERMINAL_ASSET_LIST_SORT_BY
TERMINAL_ASSET_LIST_PAGE_SIZE = CONFIG.TERMINAL_ASSET_LIST_PAGE_SIZE
TERMINAL_SESSION_KEEP_DURATION = CONFIG.TERMINAL_SESSION_KEEP_DURATION
TERMINAL_HOST_KEY = CONFIG.TERMINAL_HOST_KEY
TERMINAL_HEADER_TITLE = CONFIG.TERMINAL_HEADER_TITLE
TERMINAL_TELNET_REGEX = CONFIG.TERMINAL_TELNET_REGEX
# User or user group permission cache time, default 3600 seconds
ASSETS_PERM_CACHE_ENABLE = CONFIG.ASSETS_PERM_CACHE_ENABLE
@ -90,32 +90,25 @@ PERIOD_TASK_ENABLED = CONFIG.PERIOD_TASK_ENABLED
USER_LOGIN_SINGLE_MACHINE_ENABLED = CONFIG.USER_LOGIN_SINGLE_MACHINE_ENABLED
# Email custom content
EMAIL_SUBJECT_PREFIX = DYNAMIC.EMAIL_SUBJECT_PREFIX
EMAIL_SUFFIX = DYNAMIC.EMAIL_SUFFIX
EMAIL_CUSTOM_USER_CREATED_SUBJECT = DYNAMIC.EMAIL_CUSTOM_USER_CREATED_SUBJECT
EMAIL_CUSTOM_USER_CREATED_HONORIFIC = DYNAMIC.EMAIL_CUSTOM_USER_CREATED_HONORIFIC
EMAIL_CUSTOM_USER_CREATED_BODY = DYNAMIC.EMAIL_CUSTOM_USER_CREATED_BODY
EMAIL_CUSTOM_USER_CREATED_SIGNATURE = DYNAMIC.EMAIL_CUSTOM_USER_CREATED_SIGNATURE
EMAIL_SUBJECT_PREFIX = CONFIG.EMAIL_SUBJECT_PREFIX
EMAIL_SUFFIX = CONFIG.EMAIL_SUFFIX
EMAIL_CUSTOM_USER_CREATED_SUBJECT = CONFIG.EMAIL_CUSTOM_USER_CREATED_SUBJECT
EMAIL_CUSTOM_USER_CREATED_HONORIFIC = CONFIG.EMAIL_CUSTOM_USER_CREATED_HONORIFIC
EMAIL_CUSTOM_USER_CREATED_BODY = CONFIG.EMAIL_CUSTOM_USER_CREATED_BODY
EMAIL_CUSTOM_USER_CREATED_SIGNATURE = CONFIG.EMAIL_CUSTOM_USER_CREATED_SIGNATURE
DISPLAY_PER_PAGE = CONFIG.DISPLAY_PER_PAGE
DEFAULT_EXPIRED_YEARS = 70
USER_GUIDE_URL = DYNAMIC.USER_GUIDE_URL
USER_GUIDE_URL = CONFIG.USER_GUIDE_URL
HTTP_LISTEN_PORT = CONFIG.HTTP_LISTEN_PORT
WS_LISTEN_PORT = CONFIG.WS_LISTEN_PORT
LOGIN_LOG_KEEP_DAYS = DYNAMIC.LOGIN_LOG_KEEP_DAYS
LOGIN_LOG_KEEP_DAYS = CONFIG.LOGIN_LOG_KEEP_DAYS
TASK_LOG_KEEP_DAYS = CONFIG.TASK_LOG_KEEP_DAYS
ORG_CHANGE_TO_URL = CONFIG.ORG_CHANGE_TO_URL
WINDOWS_SKIP_ALL_MANUAL_PASSWORD = CONFIG.WINDOWS_SKIP_ALL_MANUAL_PASSWORD
AUTH_EXPIRED_SECONDS = 60 * 5
# XPACK
XPACK_LICENSE_IS_VALID = DYNAMIC.XPACK_LICENSE_IS_VALID
XPACK_INTERFACE_LOGIN_TITLE = DYNAMIC.XPACK_INTERFACE_LOGIN_TITLE
LOGO_URLS = DYNAMIC.LOGO_URLS
CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED = CONFIG.CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED
DATETIME_DISPLAY_FORMAT = '%Y-%m-%d %H:%M:%S'

View File

@ -127,3 +127,8 @@ CELERY_WORKER_REDIRECT_STDOUTS_LEVEL = "INFO"
CELERY_TASK_SOFT_TIME_LIMIT = 3600
ANSIBLE_LOG_DIR = os.path.join(PROJECT_DIR, 'data', 'ansible')
#
REDIS_HOST = CONFIG.REDIS_HOST
REDIS_PORT = CONFIG.REDIS_PORT
REDIS_PASSWORD = CONFIG.REDIS_PASSWORD

Binary file not shown.

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-21 15:49+0800\n"
"POT-Creation-Date: 2021-01-26 17:25+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -36,10 +36,10 @@ msgstr "自定义"
#: assets/models/base.py:234 assets/models/cluster.py:18
#: assets/models/cmd_filter.py:21 assets/models/domain.py:21
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
#: orgs/models.py:23 perms/models/base.py:48 settings/models.py:27
#: orgs/models.py:23 perms/models/base.py:48 settings/models.py:29
#: terminal/models/storage.py:15 terminal/models/storage.py:55
#: terminal/models/task.py:16 terminal/models/terminal.py:131
#: users/forms/profile.py:20 users/models/group.py:15 users/models/user.py:518
#: users/forms/profile.py:20 users/models/group.py:15 users/models/user.py:517
#: users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_asset_permission.html:37
#: users/templates/users/user_asset_permission.html:154
@ -95,10 +95,10 @@ msgstr ""
#: assets/models/cmd_filter.py:57 assets/models/domain.py:22
#: assets/models/domain.py:56 assets/models/group.py:23
#: assets/models/label.py:23 ops/models/adhoc.py:37 orgs/models.py:26
#: perms/models/base.py:56 settings/models.py:32 terminal/models/storage.py:21
#: perms/models/base.py:56 settings/models.py:34 terminal/models/storage.py:21
#: terminal/models/storage.py:61 terminal/models/terminal.py:145
#: tickets/models/ticket.py:73 users/models/group.py:16
#: users/models/user.py:551 users/templates/users/user_detail.html:115
#: users/models/user.py:550 users/templates/users/user_detail.html:115
#: users/templates/users/user_granted_database_app.html:38
#: users/templates/users/user_granted_remote_app.html:37
#: users/templates/users/user_group_detail.html:62
@ -164,7 +164,7 @@ msgstr "目标URL"
#: assets/models/base.py:235 assets/models/gathered_user.py:15
#: audits/models.py:99 authentication/forms.py:11
#: authentication/templates/authentication/login.html:101
#: ops/models/adhoc.py:148 users/forms/profile.py:19 users/models/user.py:516
#: ops/models/adhoc.py:148 users/forms/profile.py:19 users/models/user.py:515
#: users/templates/users/_select_user_modal.html:14
#: users/templates/users/user_detail.html:53
#: users/templates/users/user_list.html:15
@ -181,7 +181,8 @@ msgstr "用户名"
#: assets/models/base.py:236 assets/serializers/asset_user.py:71
#: audits/signals_handler.py:42 authentication/forms.py:13
#: authentication/templates/authentication/login.html:109
#: users/forms/user.py:22 users/forms/user.py:193
#: settings/serializers/settings.py:84 users/forms/user.py:22
#: users/forms/user.py:193
#: users/templates/users/user_otp_check_password.html:13
#: users/templates/users/user_password_update.html:43
#: users/templates/users/user_password_verify.html:18
@ -204,7 +205,7 @@ msgstr "目标URL"
#: applications/serializers/attrs/application_type/mysql_workbench.py:18
#: assets/models/asset.py:190 assets/models/domain.py:52
#: assets/serializers/asset_user.py:46 settings/serializers/settings.py:52
#: assets/serializers/asset_user.py:46 settings/serializers/settings.py:103
#: users/templates/users/_granted_assets.html:26
#: users/templates/users/user_asset_permission.html:156
msgid "IP"
@ -260,7 +261,7 @@ msgid "Platform"
msgstr "系统平台"
#: assets/models/asset.py:191 assets/serializers/asset_user.py:45
#: assets/serializers/gathered_user.py:20 settings/serializers/settings.py:51
#: assets/serializers/gathered_user.py:20 settings/serializers/settings.py:102
#: users/templates/users/_granted_assets.html:25
#: users/templates/users/user_asset_permission.html:157
msgid "Hostname"
@ -368,7 +369,7 @@ msgstr "标签管理"
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:26
#: assets/models/cmd_filter.py:60 assets/models/group.py:21
#: common/db/models.py:67 common/mixins/models.py:49 orgs/models.py:24
#: orgs/models.py:427 perms/models/base.py:54 users/models/user.py:559
#: orgs/models.py:427 perms/models/base.py:54 users/models/user.py:558
#: users/serializers/group.py:35 users/templates/users/user_detail.html:97
#: xpack/plugins/change_auth_plan/models.py:81 xpack/plugins/cloud/models.py:58
#: xpack/plugins/cloud/models.py:156 xpack/plugins/gathered_user/models.py:30
@ -430,7 +431,7 @@ msgstr "带宽"
msgid "Contact"
msgstr "联系人"
#: assets/models/cluster.py:22 users/models/user.py:537
#: assets/models/cluster.py:22 users/models/user.py:536
#: users/templates/users/user_detail.html:62
msgid "Phone"
msgstr "手机"
@ -456,7 +457,7 @@ msgid "Default"
msgstr "默认"
#: assets/models/cluster.py:36 assets/models/label.py:14
#: users/models/user.py:678
#: users/models/user.py:677
msgid "System"
msgstr "系统"
@ -560,7 +561,7 @@ msgstr "默认资产组"
#: templates/index.html:78 terminal/backends/command/models.py:18
#: terminal/backends/command/serializers.py:12 terminal/models/session.py:37
#: tickets/models/comment.py:17 users/forms/group.py:15
#: users/models/user.py:159 users/models/user.py:666
#: users/models/user.py:158 users/models/user.py:665
#: users/serializers/group.py:20
#: users/templates/users/user_asset_permission.html:38
#: users/templates/users/user_asset_permission.html:64
@ -574,7 +575,7 @@ msgstr "默认资产组"
msgid "User"
msgstr "用户"
#: assets/models/label.py:19 assets/models/node.py:413 settings/models.py:28
#: assets/models/label.py:19 assets/models/node.py:413 settings/models.py:30
msgid "Value"
msgstr "值"
@ -741,14 +742,14 @@ msgid "Backend"
msgstr "后端"
#: assets/serializers/asset_user.py:75 users/forms/profile.py:148
#: users/models/user.py:548 users/templates/users/user_password_update.html:48
#: users/models/user.py:547 users/templates/users/user_password_update.html:48
#: users/templates/users/user_profile.html:69
#: users/templates/users/user_profile_update.html:46
#: users/templates/users/user_pubkey_update.html:46
msgid "Public key"
msgstr "SSH公钥"
#: assets/serializers/asset_user.py:79 users/models/user.py:545
#: assets/serializers/asset_user.py:79 users/models/user.py:544
msgid "Private key"
msgstr "ssh私钥"
@ -1045,7 +1046,7 @@ msgstr "修改者"
msgid "Disabled"
msgstr "禁用"
#: audits/models.py:90 settings/models.py:31
#: audits/models.py:90 settings/models.py:33
#: users/templates/users/user_detail.html:82
msgid "Enabled"
msgstr "启用"
@ -1079,7 +1080,7 @@ msgstr "用户代理"
#: audits/models.py:104
#: authentication/templates/authentication/_mfa_confirm_modal.html:14
#: authentication/templates/authentication/login_otp.html:6
#: users/forms/profile.py:52 users/models/user.py:540
#: users/forms/profile.py:52 users/models/user.py:539
#: users/serializers/user.py:232 users/templates/users/user_detail.html:77
#: users/templates/users/user_profile.html:87
msgid "MFA"
@ -1354,7 +1355,7 @@ msgid "Show"
msgstr "显示"
#: authentication/templates/authentication/_access_key_modal.html:66
#: users/models/user.py:444 users/serializers/user.py:229
#: users/models/user.py:443 users/serializers/user.py:229
#: users/templates/users/user_profile.html:94
#: users/templates/users/user_profile.html:163
#: users/templates/users/user_profile.html:166
@ -1363,7 +1364,7 @@ msgid "Disable"
msgstr "禁用"
#: authentication/templates/authentication/_access_key_modal.html:67
#: users/models/user.py:445 users/serializers/user.py:230
#: users/models/user.py:444 users/serializers/user.py:230
#: users/templates/users/user_profile.html:92
#: users/templates/users/user_profile.html:170
msgid "Enable"
@ -1600,11 +1601,6 @@ msgstr "字段必须唯一"
msgid "Should not contains special characters"
msgstr "不能包含特殊字符"
#: jumpserver/conf.py:474 xpack/plugins/interface/api.py:18
#: xpack/plugins/interface/models.py:36
msgid "Welcome to the JumpServer open source fortress"
msgstr "欢迎使用JumpServer开源堡垒机"
#: jumpserver/views/celery_flower.py:23
msgid "<h1>Flow service unavailable, check it</h1>"
msgstr ""
@ -1813,7 +1809,7 @@ msgstr "组织管理员"
msgid "Organization auditor"
msgstr "组织审计员"
#: orgs/models.py:424 users/forms/user.py:27 users/models/user.py:528
#: orgs/models.py:424 users/forms/user.py:27 users/models/user.py:527
#: users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:73
#: users/templates/users/user_list.html:16
@ -1837,7 +1833,7 @@ msgstr "管理员正在修改授权,请稍等"
msgid "The authorization cannot be revoked for the time being"
msgstr "该授权暂时不能撤销"
#: perms/models/application_permission.py:27 users/models/user.py:160
#: perms/models/application_permission.py:27 users/models/user.py:159
msgid "Application"
msgstr "应用程序"
@ -1845,7 +1841,7 @@ msgstr "应用程序"
msgid "Application permission"
msgstr "应用管理"
#: perms/models/asset_permission.py:34 settings/serializers/settings.py:56
#: perms/models/asset_permission.py:34 settings/serializers/settings.py:107
msgid "All"
msgstr "全部"
@ -1887,7 +1883,7 @@ msgid "Asset permission"
msgstr "资产授权"
#: perms/models/base.py:50 templates/_nav.html:21 users/forms/user.py:168
#: users/models/group.py:31 users/models/user.py:524
#: users/models/group.py:31 users/models/user.py:523
#: users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_asset_permission.html:39
#: users/templates/users/user_asset_permission.html:67
@ -1905,7 +1901,7 @@ msgstr "用户组"
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:77
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:43
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:81
#: users/models/user.py:556 users/templates/users/user_detail.html:93
#: users/models/user.py:555 users/templates/users/user_detail.html:93
#: users/templates/users/user_profile.html:120
msgid "Date expired"
msgstr "失效日期"
@ -1944,27 +1940,310 @@ msgstr "收藏夹"
msgid "Please wait while your data is being initialized"
msgstr "数据正在初始化,请稍等"
#: settings/api.py:34
#: settings/api/common.py:24
msgid "Test mail sent to {}, please check"
msgstr "邮件已经发送{}, 请检查"
#: settings/api.py:244
#: settings/api/common.py:110 xpack/plugins/interface/api.py:18
#: xpack/plugins/interface/models.py:36
msgid "Welcome to the JumpServer open source fortress"
msgstr "欢迎使用JumpServer开源堡垒机"
#: settings/api/ldap.py:189
msgid "Get ldap users is None"
msgstr "获取 LDAP 用户为 None"
#: settings/api.py:251
#: settings/api/ldap.py:196
msgid "Imported {} users successfully"
msgstr "导入 {} 个用户成功"
#: settings/models.py:96 users/templates/users/reset_password.html:29
#: settings/models.py:123 users/templates/users/reset_password.html:29
#: users/templates/users/user_profile.html:20
msgid "Setting"
msgstr "设置"
#: settings/serializers/settings.py:57
#: settings/serializers/settings.py:15
msgid "Site url"
msgstr "当前站点URL"
#: settings/serializers/settings.py:16
msgid "eg: http://demo.jumpserver.org:8080"
msgstr "如: http://demo.jumpserver.org:8080"
#: settings/serializers/settings.py:19
msgid "User guide url"
msgstr "用户向导URL"
#: settings/serializers/settings.py:20
msgid "User first login update profile done redirect to it"
msgstr "用户第一次登录修改profile后重定向到地址, 可以是 wiki 或 其他说明文档"
#: settings/serializers/settings.py:27
msgid "SMTP host"
msgstr "SMTP 主机"
#: settings/serializers/settings.py:28
msgid "SMTP port"
msgstr "SMTP 端口"
#: settings/serializers/settings.py:29
msgid "SMTP account"
msgstr "SMTP 账号"
#: settings/serializers/settings.py:31
msgid "SMTP password"
msgstr "SMTP 密码"
#: settings/serializers/settings.py:32
msgid "Tips: Some provider use token except password"
msgstr "提示:一些邮件提供商需要输入的是授权码"
#: settings/serializers/settings.py:35
msgid "Send user"
msgstr "发件人"
#: settings/serializers/settings.py:36
msgid "Tips: Send mail account, default SMTP account as the send account"
msgstr "提示:发送邮件账号,默认使用 SMTP 账号作为发送账号"
#: settings/serializers/settings.py:39
msgid "Test recipient"
msgstr "测试收件人"
#: settings/serializers/settings.py:40
msgid "Tips: Used only as a test mail recipient"
msgstr "提示:仅用来作为测试邮件收件人"
#: settings/serializers/settings.py:43
msgid "Use SSL"
msgstr "使用 SSL"
#: settings/serializers/settings.py:44
msgid "If SMTP port is 465, may be select"
msgstr "如果SMTP端口是465通常需要启用 SSL"
#: settings/serializers/settings.py:47
msgid "Use TLS"
msgstr "使用 TLS"
#: settings/serializers/settings.py:48
msgid "If SMTP port is 587, may be select"
msgstr "如果SMTP端口是587通常需要启用 TLS"
#: settings/serializers/settings.py:51
msgid "Subject prefix"
msgstr "主题前缀"
#: settings/serializers/settings.py:58
msgid "Create user email subject"
msgstr "邮件主题"
#: settings/serializers/settings.py:59
msgid ""
"Tips: When creating a user, send the subject of the email (eg:Create account "
"successfully)"
msgstr "提示: 创建用户时,发送设置密码邮件的主题 (例如: 创建用户成功)"
#: settings/serializers/settings.py:63
msgid "Create user honorific"
msgstr "邮件的敬语"
#: settings/serializers/settings.py:64
msgid "Tips: When creating a user, send the honorific of the email (eg:Hello)"
msgstr "提示: 创建用户时,发送设置密码邮件的敬语 (例如: 您好)"
#: settings/serializers/settings.py:68
msgid "Create user email content"
msgstr "邮件的内容"
#: settings/serializers/settings.py:69
msgid "Tips:When creating a user, send the content of the email"
msgstr "提示: 创建用户时,发送设置密码邮件的内容"
#: settings/serializers/settings.py:72
msgid "Signature"
msgstr "署名"
#: settings/serializers/settings.py:73
msgid "Tips: Email signature (eg:jumpserver)"
msgstr "邮件署名 (如:jumpserver)"
#: settings/serializers/settings.py:81
msgid "LDAP server"
msgstr "LDAP 地址"
#: settings/serializers/settings.py:81
msgid "eg: ldap://localhost:389"
msgstr ""
#: settings/serializers/settings.py:83
msgid "Bind DN"
msgstr "绑定 DN"
#: settings/serializers/settings.py:86
msgid "User OU"
msgstr "用户 OU"
#: settings/serializers/settings.py:87
msgid "Use | split multi OUs"
msgstr "多个 OU 使用 | 分割"
#: settings/serializers/settings.py:90
msgid "User search filter"
msgstr "用户过滤器"
#: settings/serializers/settings.py:91
#, python-format
msgid "Choice may be (cn|uid|sAMAccountName)=%(user)s)"
msgstr "可能的选项是(cn或uid或sAMAccountName=%(user)s)"
#: settings/serializers/settings.py:94
msgid "User attr map"
msgstr "用户属性映射"
#: settings/serializers/settings.py:95
msgid ""
"User attr map present how to map LDAP user attr to jumpserver, username,name,"
"email is jumpserver attr"
msgstr ""
"用户属性映射代表怎样将LDAP中用户属性映射到jumpserver用户上username, name,"
"email 是jumpserver的用户需要属性"
#: settings/serializers/settings.py:97
msgid "Enable LDAP auth"
msgstr "启用 LDAP 认证"
#: settings/serializers/settings.py:108
msgid "Auto"
msgstr "自动"
#: settings/serializers/settings.py:114
msgid "Password auth"
msgstr "密码认证"
#: settings/serializers/settings.py:115
msgid "Public key auth"
msgstr "密钥认证"
#: settings/serializers/settings.py:116
msgid "List sort by"
msgstr "资产列表排序"
#: settings/serializers/settings.py:117
msgid "List page size"
msgstr "资产列表每页数量"
#: settings/serializers/settings.py:119
msgid "Session keep duration"
msgstr "会话日志保存时间"
#: settings/serializers/settings.py:120
msgid ""
"Units: days, Session, record, command will be delete if more than duration, "
"only in database"
msgstr ""
"单位:天。 会话、录像、命令记录超过该时长将会被删除(仅影响数据库存储, oss等不"
"受影响)"
#: settings/serializers/settings.py:122
msgid "Telnet login regex"
msgstr "Telnet 成功正则表达式"
#: settings/serializers/settings.py:127
msgid "Global MFA auth"
msgstr "全局启用 MFA 认证"
#: settings/serializers/settings.py:128
msgid "All user enable MFA"
msgstr "强制每个启用多因子认证"
#: settings/serializers/settings.py:131
msgid "Batch command execution"
msgstr "批量命令执行"
#: settings/serializers/settings.py:132
msgid "Allow user run batch command or not using ansible"
msgstr "是否允许用户使用 ansible 执行批量命令"
#: settings/serializers/settings.py:135
msgid "Enable terminal register"
msgstr "终端注册"
#: settings/serializers/settings.py:136
msgid ""
"Allow terminal register, after all terminal setup, you should disable this "
"for security"
msgstr "是否允许终端注册,当所有终端启动后,为了安全应该关闭"
#: settings/serializers/settings.py:140
msgid "Limit the number of login failures"
msgstr "限制登录失败次数"
#: settings/serializers/settings.py:144
msgid "Block logon interval"
msgstr "禁止登录时间间隔"
#: settings/serializers/settings.py:145
msgid ""
"Tip: (unit/minute) if the user has failed to log in for a limited number of "
"times, no login is allowed during this time interval."
msgstr ""
"提示:(单位:分)当用户登录失败次数达到限制后,那么在此时间间隔内禁止登录"
#: settings/serializers/settings.py:149
msgid "Connection max idle time"
msgstr "连接最大空闲时间"
#: settings/serializers/settings.py:150
msgid "If idle time more than it, disconnect connection Unit: minute"
msgstr "提示:如果超过该配置没有操作,连接会被断开 (单位:分)"
#: settings/serializers/settings.py:154
msgid "User password expiration"
msgstr "用户密码过期时间"
#: settings/serializers/settings.py:155
msgid ""
"Tip: (unit: day) If the user does not update the password during the time, "
"the user password will expire failure;The password expiration reminder mail "
"will be automatic sent to the user by system within 5 days (daily) before "
"the password expires"
msgstr ""
"提示:(单位:天)如果用户在此期间没有更新密码,用户密码将过期失效; 密码过期"
"提醒邮件将在密码过期前5天内由系统每天自动发送给用户"
#: settings/serializers/settings.py:159
msgid "Password minimum length"
msgstr "密码最小长度"
#: settings/serializers/settings.py:162
msgid "Must contain capital"
msgstr "必须包含大写字符"
#: settings/serializers/settings.py:164
msgid "Must contain lowercase"
msgstr "必须包含小写字符"
#: settings/serializers/settings.py:165
msgid "Must contain numeric"
msgstr "必须包含数字"
#: settings/serializers/settings.py:166
msgid "Must contain special"
msgstr "必须包含特殊字符"
#: settings/serializers/settings.py:167
msgid "Insecure command alert"
msgstr "危险命令告警"
#: settings/serializers/settings.py:169
msgid "Email recipient"
msgstr "邮件收件人"
#: settings/serializers/settings.py:170
msgid "Multiple user using , split"
msgstr "多个用户,使用 , 分割"
#: settings/utils/ldap.py:411
msgid "Host or port is disconnected: {}"
msgstr "主机或端口不可连接: {}"
@ -3215,7 +3494,7 @@ msgstr "确认密码"
msgid "Password does not match"
msgstr "密码不一致"
#: users/forms/profile.py:89 users/models/user.py:520
#: users/forms/profile.py:89 users/models/user.py:519
#: users/templates/users/user_detail.html:57
#: users/templates/users/user_profile.html:59
msgid "Email"
@ -3256,7 +3535,7 @@ msgstr "不能和原来的密钥相同"
msgid "Not a valid ssh public key"
msgstr "SSH密钥不合法"
#: users/forms/user.py:31 users/models/user.py:563
#: users/forms/user.py:31 users/models/user.py:562
#: users/templates/users/user_detail.html:89
#: users/templates/users/user_list.html:18
#: users/templates/users/user_profile.html:102
@ -3290,39 +3569,39 @@ msgstr "设置密码"
msgid "Password strategy"
msgstr "密码策略"
#: users/models/user.py:157
#: users/models/user.py:156
msgid "System administrator"
msgstr "系统管理员"
#: users/models/user.py:158
#: users/models/user.py:157
msgid "System auditor"
msgstr "系统审计员"
#: users/models/user.py:446 users/templates/users/user_profile.html:90
#: users/models/user.py:445 users/templates/users/user_profile.html:90
msgid "Force enable"
msgstr "强制启用"
#: users/models/user.py:508
#: users/models/user.py:507
msgid "Local"
msgstr "数据库"
#: users/models/user.py:531
#: users/models/user.py:530
msgid "Avatar"
msgstr "头像"
#: users/models/user.py:534 users/templates/users/user_detail.html:68
#: users/models/user.py:533 users/templates/users/user_detail.html:68
msgid "Wechat"
msgstr "微信"
#: users/models/user.py:567
#: users/models/user.py:566
msgid "Date password last updated"
msgstr "最后更新密码日期"
#: users/models/user.py:674
#: users/models/user.py:673
msgid "Administrator"
msgstr "管理员"
#: users/models/user.py:677
#: users/models/user.py:676
msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员"

View File

@ -0,0 +1,2 @@
from .common import *
from .ldap import *

189
apps/settings/api/common.py Normal file
View File

@ -0,0 +1,189 @@
# -*- coding: utf-8 -*-
#
from smtplib import SMTPSenderRefused
from rest_framework import generics
from rest_framework.views import Response, APIView
from rest_framework.permissions import AllowAny
from django.conf import settings
from django.core.mail import send_mail, get_connection
from django.utils.translation import ugettext_lazy as _
from django.templatetags.static import static
from common.permissions import IsSuperUser
from common.utils import get_logger
from .. import serializers
from ..models import Setting
logger = get_logger(__file__)
class MailTestingAPI(APIView):
permission_classes = (IsSuperUser,)
serializer_class = serializers.MailTestSerializer
success_message = _("Test mail sent to {}, please check")
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
email_host = serializer.validated_data['EMAIL_HOST']
email_port = serializer.validated_data['EMAIL_PORT']
email_host_user = serializer.validated_data["EMAIL_HOST_USER"]
email_host_password = serializer.validated_data['EMAIL_HOST_PASSWORD']
email_from = serializer.validated_data["EMAIL_FROM"]
email_recipient = serializer.validated_data["EMAIL_RECIPIENT"]
email_use_ssl = serializer.validated_data['EMAIL_USE_SSL']
email_use_tls = serializer.validated_data['EMAIL_USE_TLS']
# 设置 settings 的值,会导致动态配置在当前进程失效
# for k, v in serializer.validated_data.items():
# if k.startswith('EMAIL'):
# setattr(settings, k, v)
try:
subject = "Test"
message = "Test smtp setting"
email_from = email_from or email_host_user
email_recipient = email_recipient or email_from
connection = get_connection(
host=email_host, port=email_port,
username=email_host_user, password=email_host_password,
use_tls=email_use_tls, use_ssl=email_use_ssl,
)
send_mail(
subject, message, email_from, [email_recipient],
connection=connection
)
except SMTPSenderRefused as e:
error = e.smtp_error
if isinstance(error, bytes):
for coding in ('gbk', 'utf8'):
try:
error = error.decode(coding)
except UnicodeDecodeError:
continue
else:
break
return Response({"error": str(error)}, status=400)
except Exception as e:
logger.error(e)
return Response({"error": str(e)}, status=400)
return Response({"msg": self.success_message.format(email_recipient)})
class PublicSettingApi(generics.RetrieveAPIView):
permission_classes = (AllowAny,)
serializer_class = serializers.PublicSettingSerializer
@staticmethod
def get_logo_urls():
logo_urls = {
'logo_logout': static('img/logo.png'),
'logo_index': static('img/logo_text.png'),
'login_image': static('img/login_image.png'),
'favicon': static('img/facio.ico')
}
if not settings.XPACK_ENABLED:
return logo_urls
from xpack.plugins.interface.models import Interface
obj = Interface.interface()
if not obj:
return logo_urls
for attr in ['logo_logout', 'logo_index', 'login_image', 'favicon']:
if getattr(obj, attr, '') and getattr(obj, attr).url:
logo_urls.update({attr: getattr(obj, attr).url})
return logo_urls
@staticmethod
def get_xpack_license_is_valid():
if not settings.XPACK_ENABLED:
return False
try:
from xpack.plugins.license.models import License
return License.has_valid_license()
except Exception as e:
logger.error(e)
return False
@staticmethod
def get_login_title():
default_title = _('Welcome to the JumpServer open source fortress')
if not settings.XPACK_ENABLED:
return default_title
from xpack.plugins.interface.models import Interface
return Interface.get_login_title()
def get_object(self):
instance = {
"data": {
"WINDOWS_SKIP_ALL_MANUAL_PASSWORD": settings.WINDOWS_SKIP_ALL_MANUAL_PASSWORD,
"SECURITY_MAX_IDLE_TIME": settings.SECURITY_MAX_IDLE_TIME,
"XPACK_ENABLED": settings.XPACK_ENABLED,
"LOGIN_CONFIRM_ENABLE": settings.LOGIN_CONFIRM_ENABLE,
"SECURITY_VIEW_AUTH_NEED_MFA": settings.SECURITY_VIEW_AUTH_NEED_MFA,
"SECURITY_MFA_VERIFY_TTL": settings.SECURITY_MFA_VERIFY_TTL,
"SECURITY_COMMAND_EXECUTION": settings.SECURITY_COMMAND_EXECUTION,
"SECURITY_PASSWORD_EXPIRATION_TIME": settings.SECURITY_PASSWORD_EXPIRATION_TIME,
"XPACK_LICENSE_IS_VALID": self.get_xpack_license_is_valid(),
"LOGIN_TITLE": self.get_login_title(),
"LOGO_URLS": self.get_logo_urls(),
"TICKETS_ENABLED": settings.TICKETS_ENABLED,
"PASSWORD_RULE": {
'SECURITY_PASSWORD_MIN_LENGTH': settings.SECURITY_PASSWORD_MIN_LENGTH,
'SECURITY_PASSWORD_UPPER_CASE': settings.SECURITY_PASSWORD_UPPER_CASE,
'SECURITY_PASSWORD_LOWER_CASE': settings.SECURITY_PASSWORD_LOWER_CASE,
'SECURITY_PASSWORD_NUMBER': settings.SECURITY_PASSWORD_NUMBER,
'SECURITY_PASSWORD_SPECIAL_CHAR': settings.SECURITY_PASSWORD_SPECIAL_CHAR,
}
}
}
return instance
class SettingsApi(generics.RetrieveUpdateAPIView):
permission_classes = (IsSuperUser,)
serializer_class_mapper = {
'all': serializers.SettingsSerializer,
'basic': serializers.BasicSettingSerializer,
'terminal': serializers.TerminalSettingSerializer,
'security': serializers.SecuritySettingSerializer,
'ldap': serializers.LDAPSettingSerializer,
'email': serializers.EmailSettingSerializer,
'email_content': serializers.EmailContentSettingSerializer,
}
def get_serializer_class(self):
category = self.request.query_params.get('category', serializers.BasicSettingSerializer)
return self.serializer_class_mapper.get(category, serializers.BasicSettingSerializer)
def get_fields(self):
serializer = self.get_serializer_class()()
fields = serializer.get_fields()
return fields
def get_object(self):
items = self.get_fields().keys()
return {item: getattr(settings, item) for item in items}
def parse_serializer_data(self, serializer):
data = []
fields = self.get_fields()
encrypted_items = [name for name, field in fields.items() if field.write_only]
category = self.request.query_params.get('category', '')
for name, value in serializer.validated_data.items():
encrypted = name in encrypted_items
data.append({
'name': name, 'value': value,
'encrypted': encrypted, 'category': category
})
return data
def perform_update(self, serializer):
settings_items = self.parse_serializer_data(serializer)
serializer_data = getattr(serializer, 'data', {})
for item in settings_items:
changed, setting = Setting.update_or_create(**item)
if not changed:
continue
serializer_data[setting.name] = setting.cleaned_value
setattr(serializer, '_data', serializer_data)

View File

@ -10,16 +10,15 @@ from rest_framework.views import Response, APIView
from django.conf import settings
from django.core.mail import send_mail, get_connection
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from .utils import (
from ..utils import (
LDAPServerUtil, LDAPCacheUtil, LDAPImportUtil, LDAPSyncUtil,
LDAP_USE_CACHE_FLAGS, LDAPTestUtil, ObjectDict
)
from .tasks import sync_ldap_user
from ..tasks import sync_ldap_user
from common.permissions import IsOrgAdmin, IsSuperUser
from common.utils import get_logger
from .serializers import (
from ..serializers import (
MailTestSerializer, LDAPTestConfigSerializer, LDAPUserSerializer,
PublicSettingSerializer, LDAPTestLoginSerializer, SettingsSerializer
)
@ -28,60 +27,6 @@ from users.models import User
logger = get_logger(__file__)
class MailTestingAPI(APIView):
permission_classes = (IsSuperUser,)
serializer_class = MailTestSerializer
success_message = _("Test mail sent to {}, please check")
def post(self, request):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
email_host = serializer.validated_data['EMAIL_HOST']
email_port = serializer.validated_data['EMAIL_PORT']
email_host_user = serializer.validated_data["EMAIL_HOST_USER"]
email_host_password = serializer.validated_data['EMAIL_HOST_PASSWORD']
email_from = serializer.validated_data["EMAIL_FROM"]
email_recipient = serializer.validated_data["EMAIL_RECIPIENT"]
email_use_ssl = serializer.validated_data['EMAIL_USE_SSL']
email_use_tls = serializer.validated_data['EMAIL_USE_TLS']
# 设置 settings 的值,会导致动态配置在当前进程失效
# for k, v in serializer.validated_data.items():
# if k.startswith('EMAIL'):
# setattr(settings, k, v)
try:
subject = "Test"
message = "Test smtp setting"
email_from = email_from or email_host_user
email_recipient = email_recipient or email_from
connection = get_connection(
host=email_host, port=email_port,
username=email_host_user, password=email_host_password,
use_tls=email_use_tls, use_ssl=email_use_ssl,
)
send_mail(
subject, message, email_from, [email_recipient],
connection=connection
)
except SMTPSenderRefused as e:
resp = e.smtp_error
if isinstance(resp, bytes):
for coding in ('gbk', 'utf8'):
try:
resp = resp.decode(coding)
except UnicodeDecodeError:
continue
else:
break
return Response({"error": str(resp)}, status=400)
except Exception as e:
print(e)
return Response({"error": str(e)}, status=400)
return Response({"msg": self.success_message.format(email_recipient)})
else:
return Response({"error": str(serializer.errors)}, status=400)
class LDAPTestingConfigAPI(APIView):
permission_classes = (IsSuperUser,)
serializer_class = LDAPTestConfigSerializer
@ -262,59 +207,3 @@ class LDAPCacheRefreshAPI(generics.RetrieveAPIView):
return Response(data={'msg': str(e)}, status=400)
return Response(data={'msg': 'success'})
class PublicSettingApi(generics.RetrieveAPIView):
permission_classes = ()
serializer_class = PublicSettingSerializer
def get_object(self):
instance = {
"data": {
"WINDOWS_SKIP_ALL_MANUAL_PASSWORD": settings.WINDOWS_SKIP_ALL_MANUAL_PASSWORD,
"SECURITY_MAX_IDLE_TIME": settings.SECURITY_MAX_IDLE_TIME,
"XPACK_ENABLED": settings.XPACK_ENABLED,
"XPACK_LICENSE_IS_VALID": settings.XPACK_LICENSE_IS_VALID,
"LOGIN_CONFIRM_ENABLE": settings.LOGIN_CONFIRM_ENABLE,
"SECURITY_VIEW_AUTH_NEED_MFA": settings.SECURITY_VIEW_AUTH_NEED_MFA,
"SECURITY_MFA_VERIFY_TTL": settings.SECURITY_MFA_VERIFY_TTL,
"SECURITY_COMMAND_EXECUTION": settings.SECURITY_COMMAND_EXECUTION,
"LOGIN_TITLE": settings.XPACK_INTERFACE_LOGIN_TITLE,
"SECURITY_PASSWORD_EXPIRATION_TIME": settings.SECURITY_PASSWORD_EXPIRATION_TIME,
"LOGO_URLS": settings.LOGO_URLS,
"TICKETS_ENABLED": settings.TICKETS_ENABLED,
"PASSWORD_RULE": {
'SECURITY_PASSWORD_MIN_LENGTH': settings.SECURITY_PASSWORD_MIN_LENGTH,
'SECURITY_PASSWORD_UPPER_CASE': settings.SECURITY_PASSWORD_UPPER_CASE,
'SECURITY_PASSWORD_LOWER_CASE': settings.SECURITY_PASSWORD_LOWER_CASE,
'SECURITY_PASSWORD_NUMBER': settings.SECURITY_PASSWORD_NUMBER,
'SECURITY_PASSWORD_SPECIAL_CHAR': settings.SECURITY_PASSWORD_SPECIAL_CHAR,
}
}
}
return instance
class SettingsApi(generics.RetrieveUpdateAPIView):
permission_classes = (IsSuperUser,)
serializer_class = SettingsSerializer
def get_object(self):
instance = {category: self._get_setting_fields_obj(list(category_serializer.get_fields()))
for category, category_serializer in self.serializer_class().get_fields().items()
if isinstance(category_serializer, serializers.Serializer)}
return ObjectDict(instance)
def perform_update(self, serializer):
serializer.save()
def _get_setting_fields_obj(self, category_fields):
if isinstance(category_fields, Iterable):
fields_data = {field_name: getattr(settings, field_name)
for field_name in category_fields}
return ObjectDict(fields_data)
if isinstance(category_fields, str):
fields_data = {category_fields: getattr(settings, category_fields)}
return ObjectDict(fields_data)
return ObjectDict()

View File

@ -3,9 +3,11 @@ import json
from django.db import models
from django.db.utils import ProgrammingError, OperationalError
from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache
from django.conf import settings
from common.utils import signer
from common.utils import signer, get_logger
logger = get_logger(__name__)
class SettingQuerySet(models.QuerySet):
@ -37,24 +39,6 @@ class Setting(models.Model):
def __str__(self):
return self.name
@classmethod
def get(cls, item):
cached = cls.get_from_cache(item)
if cached is not None:
return cached
instances = cls.objects.filter(name=item)
if len(instances) == 1:
s = instances[0]
s.refresh_setting()
return s.cleaned_value
return None
@classmethod
def get_from_cache(cls, item):
key = cls.cache_key_prefix + item
cached = cache.get(key)
return cached
@property
def cleaned_value(self):
try:
@ -87,9 +71,52 @@ class Setting(models.Model):
except (ProgrammingError, OperationalError):
pass
@classmethod
def refresh_item(cls, name):
item = cls.objects.filter(name=name).first()
if not item:
return
item.refresh_setting()
def refresh_setting(self):
key = self.cache_key_prefix + self.name
cache.set(key, self.cleaned_value, None)
logger.debug(f"Refresh setting: {self.name}")
if hasattr(self.__class__, f'refresh_{self.name}'):
getattr(self.__class__, f'refresh_{self.name}')()
else:
setattr(settings, self.name, self.cleaned_value)
@classmethod
def refresh_AUTH_LDAP(cls):
setting = cls.objects.filter(name='AUTH_LDAP').first()
if not setting:
return
ldap_backend = 'authentication.backends.ldap.LDAPAuthorizationBackend'
backends = settings.AUTHENTICATION_BACKENDS
has = ldap_backend in backends
if setting.cleaned_value and not has:
settings.AUTHENTICATION_BACKENDS.insert(0, ldap_backend)
if not setting.cleaned_value and has:
index = backends.index(ldap_backend)
backends.pop(index)
settings.AUTH_LDAP = setting.cleaned_value
@classmethod
def update_or_create(cls, name='', value='', encrypted=False, category=''):
"""
不能使用 Model 提供的update_or_create 因为这里有 encrypted cleaned_value
:return: (changed, instance)
"""
setting = cls.objects.filter(name=name).first()
changed = False
if not setting:
setting = Setting(name=name, encrypted=encrypted, category=category)
if setting.cleaned_value != value:
setting.encrypted = encrypted
setting.cleaned_value = value
setting.save()
changed = True
return changed, setting
class Meta:
db_table = "settings_setting"

View File

@ -1,49 +1,100 @@
# coding: utf-8
from django.db import transaction
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from ..models import Setting
__all__ = ['SettingsSerializer']
__all__ = [
'BasicSettingSerializer', 'EmailSettingSerializer', 'EmailContentSettingSerializer',
'LDAPSettingSerializer', 'TerminalSettingSerializer', 'SecuritySettingSerializer',
'SettingsSerializer'
]
class BasicSettingSerializer(serializers.Serializer):
SITE_URL = serializers.URLField(required=True)
USER_GUIDE_URL = serializers.URLField(required=False, allow_blank=True, )
EMAIL_SUBJECT_PREFIX = serializers.CharField(max_length=1024, required=True)
SITE_URL = serializers.URLField(
required=True, label=_("Site url"),
help_text=_('eg: http://demo.jumpserver.org:8080')
)
USER_GUIDE_URL = serializers.URLField(
required=False, allow_blank=True, label=_("User guide url"),
help_text=_('User first login update profile done redirect to it')
)
class EmailSettingSerializer(serializers.Serializer):
encrypt_fields = ["EMAIL_HOST_PASSWORD", ]
# encrypt_fields 现在使用 write_only 来判断了
EMAIL_HOST = serializers.CharField(max_length=1024, required=True)
EMAIL_PORT = serializers.CharField(max_length=5, required=True)
EMAIL_HOST_USER = serializers.CharField(max_length=128, required=True)
EMAIL_HOST_PASSWORD = serializers.CharField(max_length=1024, write_only=True, required=False, )
EMAIL_FROM = serializers.CharField(max_length=128, allow_blank=True, required=False)
EMAIL_RECIPIENT = serializers.CharField(max_length=128, allow_blank=True, required=False)
EMAIL_USE_SSL = serializers.BooleanField(required=False)
EMAIL_USE_TLS = serializers.BooleanField(required=False)
EMAIL_HOST = serializers.CharField(max_length=1024, required=True, label=_("SMTP host"))
EMAIL_PORT = serializers.CharField(max_length=5, required=True, label=_("SMTP port"))
EMAIL_HOST_USER = serializers.CharField(max_length=128, required=True, label=_("SMTP account"))
EMAIL_HOST_PASSWORD = serializers.CharField(
max_length=1024, write_only=True, required=False, label=_("SMTP password"),
help_text=_("Tips: Some provider use token except password")
)
EMAIL_FROM = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Send user'),
help_text=_('Tips: Send mail account, default SMTP account as the send account')
)
EMAIL_RECIPIENT = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Test recipient'),
help_text=_('Tips: Used only as a test mail recipient')
)
EMAIL_USE_SSL = serializers.BooleanField(
required=False, label=_('Use SSL'),
help_text=_('If SMTP port is 465, may be select')
)
EMAIL_USE_TLS = serializers.BooleanField(
required=False, label=_("Use TLS"),
help_text=_('If SMTP port is 587, may be select')
)
EMAIL_SUBJECT_PREFIX = serializers.CharField(
max_length=1024, required=True, label=_('Subject prefix')
)
class EmailContentSettingSerializer(serializers.Serializer):
EMAIL_CUSTOM_USER_CREATED_SUBJECT = serializers.CharField(max_length=1024, allow_blank=True, required=False, )
EMAIL_CUSTOM_USER_CREATED_HONORIFIC = serializers.CharField(max_length=1024, allow_blank=True, required=False, )
EMAIL_CUSTOM_USER_CREATED_BODY = serializers.CharField(max_length=4096, allow_blank=True, required=False)
EMAIL_CUSTOM_USER_CREATED_SIGNATURE = serializers.CharField(max_length=512, allow_blank=True, required=False)
EMAIL_CUSTOM_USER_CREATED_SUBJECT = serializers.CharField(
max_length=1024, allow_blank=True, required=False,
label=_('Create user email subject'),
help_text=_('Tips: When creating a user, send the subject of the email (eg:Create account successfully)')
)
EMAIL_CUSTOM_USER_CREATED_HONORIFIC = serializers.CharField(
max_length=1024, allow_blank=True, required=False,
label=_('Create user honorific'),
help_text=_('Tips: When creating a user, send the honorific of the email (eg:Hello)')
)
EMAIL_CUSTOM_USER_CREATED_BODY = serializers.CharField(
max_length=4096, allow_blank=True, required=False,
label=_('Create user email content'),
help_text=_('Tips:When creating a user, send the content of the email')
)
EMAIL_CUSTOM_USER_CREATED_SIGNATURE = serializers.CharField(
max_length=512, allow_blank=True, required=False, label=_('Signature'),
help_text=_('Tips: Email signature (eg:jumpserver)')
)
class LdapSettingSerializer(serializers.Serializer):
encrypt_fields = ["AUTH_LDAP_BIND_PASSWORD", ]
class LDAPSettingSerializer(serializers.Serializer):
# encrypt_fields 现在使用 write_only 来判断了
AUTH_LDAP_SERVER_URI = serializers.CharField(required=True)
AUTH_LDAP_BIND_DN = serializers.CharField(required=False)
AUTH_LDAP_BIND_PASSWORD = serializers.CharField(max_length=1024, write_only=True, required=False)
AUTH_LDAP_SEARCH_OU = serializers.CharField(max_length=1024, allow_blank=True, required=False)
AUTH_LDAP_SEARCH_FILTER = serializers.CharField(max_length=1024, required=True)
AUTH_LDAP_USER_ATTR_MAP = serializers.DictField(required=True)
AUTH_LDAP = serializers.BooleanField(required=False)
AUTH_LDAP_SERVER_URI = serializers.CharField(
required=True, max_length=1024, label=_('LDAP server'), help_text=_('eg: ldap://localhost:389')
)
AUTH_LDAP_BIND_DN = serializers.CharField(required=False, max_length=1024, label=_('Bind DN'))
AUTH_LDAP_BIND_PASSWORD = serializers.CharField(max_length=1024, write_only=True, required=False, label=_('Password'))
AUTH_LDAP_SEARCH_OU = serializers.CharField(
max_length=1024, allow_blank=True, required=False, label=_('User OU'),
help_text=_('Use | split multi OUs')
)
AUTH_LDAP_SEARCH_FILTER = serializers.CharField(
max_length=1024, required=True, label=_('User search filter'),
help_text=_('Choice may be (cn|uid|sAMAccountName)=%(user)s)')
)
AUTH_LDAP_USER_ATTR_MAP = serializers.DictField(
required=True, label=_('User attr map'),
help_text=_('User attr map present how to map LDAP user attr to jumpserver, username,name,email is jumpserver attr')
)
AUTH_LDAP = serializers.BooleanField(required=False, label=_('Enable LDAP auth'))
class TerminalSettingSerializer(serializers.Serializer):
@ -60,67 +111,75 @@ class TerminalSettingSerializer(serializers.Serializer):
('25', '25'),
('50', '50'),
)
TERMINAL_PASSWORD_AUTH = serializers.BooleanField(required=False)
TERMINAL_PUBLIC_KEY_AUTH = serializers.BooleanField(required=False)
TERMINAL_HEARTBEAT_INTERVAL = serializers.IntegerField(min_value=5, max_value=99999, required=False)
TERMINAL_ASSET_LIST_SORT_BY = serializers.ChoiceField(SORT_BY_CHOICES, required=False)
TERMINAL_ASSET_LIST_PAGE_SIZE = serializers.ChoiceField(PAGE_SIZE_CHOICES, required=False)
TERMINAL_SESSION_KEEP_DURATION = serializers.IntegerField(min_value=1, max_value=99999, required=True)
TERMINAL_TELNET_REGEX = serializers.CharField(allow_blank=True, required=False)
TERMINAL_PASSWORD_AUTH = serializers.BooleanField(required=False, label=_('Password auth'))
TERMINAL_PUBLIC_KEY_AUTH = serializers.BooleanField(required=False, label=_('Public key auth'))
TERMINAL_ASSET_LIST_SORT_BY = serializers.ChoiceField(SORT_BY_CHOICES, required=False, label=_('List sort by'))
TERMINAL_ASSET_LIST_PAGE_SIZE = serializers.ChoiceField(PAGE_SIZE_CHOICES, required=False, label=_('List page size'))
TERMINAL_SESSION_KEEP_DURATION = serializers.IntegerField(
min_value=1, max_value=99999, required=True, label=_('Session keep duration'),
help_text=_('Units: days, Session, record, command will be delete if more than duration, only in database')
)
TERMINAL_TELNET_REGEX = serializers.CharField(allow_blank=True, max_length=1024, required=False, label=_('Telnet login regex'))
class SecuritySettingSerializer(serializers.Serializer):
SECURITY_MFA_AUTH = serializers.BooleanField(required=False)
SECURITY_COMMAND_EXECUTION = serializers.BooleanField(required=False)
SECURITY_SERVICE_ACCOUNT_REGISTRATION = serializers.BooleanField(required=True)
SECURITY_LOGIN_LIMIT_COUNT = serializers.IntegerField(min_value=3, max_value=99999, required=True)
SECURITY_LOGIN_LIMIT_TIME = serializers.IntegerField(min_value=5, max_value=99999, required=True)
SECURITY_MAX_IDLE_TIME = serializers.IntegerField(min_value=1, max_value=99999, required=False)
SECURITY_PASSWORD_EXPIRATION_TIME = serializers.IntegerField(min_value=1, max_value=99999, required=True)
SECURITY_PASSWORD_MIN_LENGTH = serializers.IntegerField(min_value=6, max_value=30, required=True)
SECURITY_PASSWORD_UPPER_CASE = serializers.BooleanField(required=False)
SECURITY_PASSWORD_LOWER_CASE = serializers.BooleanField(required=False)
SECURITY_PASSWORD_NUMBER = serializers.BooleanField(required=False)
SECURITY_PASSWORD_SPECIAL_CHAR = serializers.BooleanField(required=False)
SECURITY_INSECURE_COMMAND = serializers.BooleanField(required=False)
SECURITY_INSECURE_COMMAND_EMAIL_RECEIVER = serializers.CharField(max_length=8192, required=False, allow_blank=True)
SECURITY_MFA_AUTH = serializers.BooleanField(
required=False, label=_("Global MFA auth"),
help_text=_('All user enable MFA')
)
SECURITY_COMMAND_EXECUTION = serializers.BooleanField(
required=False, label=_('Batch command execution'),
help_text=_('Allow user run batch command or not using ansible')
)
SECURITY_SERVICE_ACCOUNT_REGISTRATION = serializers.BooleanField(
required=True, label=_('Enable terminal register'),
help_text=_("Allow terminal register, after all terminal setup, you should disable this for security")
)
SECURITY_LOGIN_LIMIT_COUNT = serializers.IntegerField(
min_value=3, max_value=99999,
label=_('Limit the number of login failures')
)
SECURITY_LOGIN_LIMIT_TIME = serializers.IntegerField(
min_value=5, max_value=99999, required=True,
label=_('Block logon interval'),
help_text=_('Tip: (unit/minute) if the user has failed to log in for a limited number of times, no login is allowed during this time interval.')
)
SECURITY_MAX_IDLE_TIME = serializers.IntegerField(
min_value=1, max_value=99999, required=False,
label=_('Connection max idle time'),
help_text=_('If idle time more than it, disconnect connection Unit: minute')
)
SECURITY_PASSWORD_EXPIRATION_TIME = serializers.IntegerField(
min_value=1, max_value=99999, required=True,
label=_('User password expiration'),
help_text=_('Tip: (unit: day) If the user does not update the password during the time, the user password will expire failure;The password expiration reminder mail will be automatic sent to the user by system within 5 days (daily) before the password expires')
)
SECURITY_PASSWORD_MIN_LENGTH = serializers.IntegerField(
min_value=6, max_value=30, required=True,
label=_('Password minimum length')
)
SECURITY_PASSWORD_UPPER_CASE = serializers.BooleanField(
required=False, label=_('Must contain capital')
)
SECURITY_PASSWORD_LOWER_CASE = serializers.BooleanField(required=False, label=_('Must contain lowercase'))
SECURITY_PASSWORD_NUMBER = serializers.BooleanField(required=False, label=_('Must contain numeric'))
SECURITY_PASSWORD_SPECIAL_CHAR = serializers.BooleanField(required=False, label=_('Must contain special'))
SECURITY_INSECURE_COMMAND = serializers.BooleanField(required=False, label=_('Insecure command alert'))
SECURITY_INSECURE_COMMAND_EMAIL_RECEIVER = serializers.CharField(
max_length=8192, required=False, allow_blank=True, label=_('Email recipient'),
help_text=_('Multiple user using , split')
)
class SettingsSerializer(serializers.Serializer):
basic = BasicSettingSerializer(required=False)
email = EmailSettingSerializer(required=False)
email_content = EmailContentSettingSerializer(required=False)
ldap = LdapSettingSerializer(required=False)
terminal = TerminalSettingSerializer(required=False)
security = SecuritySettingSerializer(required=False)
class SettingsSerializer(
BasicSettingSerializer,
EmailSettingSerializer,
EmailContentSettingSerializer,
LDAPSettingSerializer,
TerminalSettingSerializer,
SecuritySettingSerializer
):
encrypt_fields = ["EMAIL_HOST_PASSWORD", "AUTH_LDAP_BIND_PASSWORD"]
# encrypt_fields 现在使用 write_only 来判断了
pass
def create(self, validated_data):
pass
def update(self, instance, validated_data):
for category, category_data in validated_data.items():
if not category_data:
continue
self.update_validated_settings(category_data)
for field_name, field_value in category_data.items():
setattr(getattr(instance, category), field_name, field_value)
return instance
def update_validated_settings(self, validated_data, category='default'):
if not validated_data:
return
with transaction.atomic():
for field_name, field_value in validated_data.items():
try:
setting = Setting.objects.get(name=field_name)
except Setting.DoesNotExist:
setting = Setting()
encrypted = True if field_name in self.encrypt_fields else False
setting.name = field_name
setting.category = category
setting.encrypted = encrypted
setting.cleaned_value = field_value
setting.save()

View File

@ -1,28 +1,44 @@
# -*- coding: utf-8 -*-
#
import json
import threading
from django.dispatch import receiver
from django.db.models.signals import post_save, pre_save
from django.utils.functional import LazyObject
from jumpserver.utils import current_request
from common.decorator import on_transaction_commit
from common.utils import get_logger, ssh_key_gen
from common.utils.connection import RedisPubSub
from common.signals import django_ready
from .models import Setting
logger = get_logger(__file__)
@receiver(post_save, sender=Setting, dispatch_uid="my_unique_identifier")
def get_settings_pub_sub():
return RedisPubSub('settings')
class SettingSubPub(LazyObject):
def _setup(self):
self._wrapped = get_settings_pub_sub()
setting_pub_sub = SettingSubPub()
@receiver(post_save, sender=Setting)
@on_transaction_commit
def refresh_settings_on_changed(sender, instance=None, **kwargs):
if instance:
instance.refresh_setting()
setting_pub_sub.publish(instance.name)
@receiver(django_ready)
def on_django_ready_add_db_config(sender, **kwargs):
from django.conf import settings
settings.DYNAMIC.db_setting = Setting
Setting.refresh_all_settings()
@receiver(django_ready)
@ -41,9 +57,27 @@ def auto_generate_terminal_host_key(sender, **kwargs):
def on_create_set_created_by(sender, instance=None, **kwargs):
if getattr(instance, '_ignore_auto_created_by', False) is True:
return
if hasattr(instance, 'created_by') and not instance.created_by:
if current_request and current_request.user.is_authenticated:
user_name = current_request.user.name
if isinstance(user_name, str):
user_name = user_name[:30]
instance.created_by = user_name
if not hasattr(instance, 'created_by') or instance.created_by:
return
if current_request and current_request.user.is_authenticated:
user_name = current_request.user.name
if isinstance(user_name, str):
user_name = user_name[:30]
instance.created_by = user_name
@receiver(django_ready)
def subscribe_settings_change(sender, **kwargs):
logger.debug("Start subscribe setting change")
def keep_subscribe():
sub = setting_pub_sub.subscribe()
for msg in sub.listen():
if msg["type"] != "message":
continue
item = msg['data'].decode()
logger.debug("Found setting change: {}".format(str(item)))
Setting.refresh_item(item)
t = threading.Thread(target=keep_subscribe)
t.daemon = True
t.start()

View File

@ -16,7 +16,6 @@ from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.shortcuts import reverse
from common.local import LOCAL_DYNAMIC_SETTINGS
from orgs.utils import current_org
from orgs.models import OrganizationMember, Organization
from common.utils import date_expired_default, get_logger, lazyproperty
@ -452,7 +451,7 @@ class MFAMixin:
@property
def mfa_force_enabled(self):
if LOCAL_DYNAMIC_SETTINGS.SECURITY_MFA_AUTH:
if settings.SECURITY_MFA_AUTH:
return True
return self.mfa_level == 2