diff --git a/apps/assets/signals_handler/system_user.py b/apps/assets/signals_handler/system_user.py index 9e1ee2045..00111030c 100644 --- a/apps/assets/signals_handler/system_user.py +++ b/apps/assets/signals_handler/system_user.py @@ -131,8 +131,8 @@ def on_system_user_groups_change(instance, action, pk_set, reverse, **kwargs): @on_transaction_commit def on_system_user_update(instance: SystemUser, created, **kwargs): """ - 当系统用户更新时,可能更新了秘钥,用户名等,这时要自动推送系统用户到资产上, - 其实应该当 用户名,密码,秘钥 sudo等更新时再推送,这里偷个懒, + 当系统用户更新时,可能更新了密钥,用户名等,这时要自动推送系统用户到资产上, + 其实应该当 用户名,密码,密钥 sudo等更新时再推送,这里偷个懒, 这里直接取了 instance.assets 因为nodes和系统用户发生变化时,会自动将nodes下的资产 关联到上面 """ diff --git a/apps/audits/tasks.py b/apps/audits/tasks.py index 467967b82..171fbe633 100644 --- a/apps/audits/tasks.py +++ b/apps/audits/tasks.py @@ -5,14 +5,12 @@ from django.utils import timezone from celery import shared_task from ops.celery.decorator import ( - register_as_period_task, after_app_shutdown_clean_periodic + register_as_period_task ) from .models import UserLoginLog, OperateLog from common.utils import get_log_keep_day -@shared_task -@after_app_shutdown_clean_periodic def clean_login_log_period(): now = timezone.now() days = get_log_keep_day('LOGIN_LOG_KEEP_DAYS') @@ -20,8 +18,6 @@ def clean_login_log_period(): UserLoginLog.objects.filter(datetime__lt=expired_day).delete() -@shared_task -@after_app_shutdown_clean_periodic def clean_operation_log_period(): now = timezone.now() days = get_log_keep_day('OPERATE_LOG_KEEP_DAYS') @@ -29,7 +25,6 @@ def clean_operation_log_period(): OperateLog.objects.filter(datetime__lt=expired_day).delete() -@shared_task def clean_ftp_log_period(): now = timezone.now() days = get_log_keep_day('FTP_LOG_KEEP_DAYS') diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index cf5474d55..4fc39b4ac 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -51,7 +51,8 @@ class UserLoginView(mixins.AuthMixin, FormView): if settings.AUTH_OPENID: auth_type = 'OIDC' - openid_auth_url = reverse(settings.AUTH_OPENID_AUTH_LOGIN_URL_NAME) + f'?next={next_url}' + openid_auth_url = reverse(settings.AUTH_OPENID_AUTH_LOGIN_URL_NAME) + openid_auth_url = openid_auth_url + f'?next={next_url}' else: openid_auth_url = None @@ -64,16 +65,13 @@ class UserLoginView(mixins.AuthMixin, FormView): if not any([openid_auth_url, cas_auth_url]): return None - if settings.LOGIN_REDIRECT_TO_BACKEND == 'OPENID' and openid_auth_url: + login_redirect = settings.LOGIN_REDIRECT_TO_BACKEND.lower() + if login_redirect == ['CAS', 'cas'] and cas_auth_url: + auth_url = cas_auth_url + else: auth_url = openid_auth_url - elif settings.LOGIN_REDIRECT_TO_BACKEND == 'CAS' and cas_auth_url: - auth_url = cas_auth_url - - else: - auth_url = openid_auth_url or cas_auth_url - - if settings.LOGIN_REDIRECT_TO_BACKEND: + if settings.LOGIN_REDIRECT_TO_BACKEND or not settings.LOGIN_REDIRECT_MSG_ENABLED: redirect_url = auth_url else: message_data = { diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 47af0c38b..381eab090 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -179,13 +179,14 @@ class Config(dict): 'AUTH_OPENID_CLIENT_SECRET': 'client-secret', 'AUTH_OPENID_SHARE_SESSION': True, 'AUTH_OPENID_IGNORE_SSL_VERIFICATION': True, + # OpenID 新配置参数 (version >= 1.5.9) - 'AUTH_OPENID_PROVIDER_ENDPOINT': 'https://op-example.com/', - 'AUTH_OPENID_PROVIDER_AUTHORIZATION_ENDPOINT': 'https://op-example.com/authorize', - 'AUTH_OPENID_PROVIDER_TOKEN_ENDPOINT': 'https://op-example.com/token', - 'AUTH_OPENID_PROVIDER_JWKS_ENDPOINT': 'https://op-example.com/jwks', - 'AUTH_OPENID_PROVIDER_USERINFO_ENDPOINT': 'https://op-example.com/userinfo', - 'AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT': 'https://op-example.com/logout', + 'AUTH_OPENID_PROVIDER_ENDPOINT': 'https://oidc.example.com/', + 'AUTH_OPENID_PROVIDER_AUTHORIZATION_ENDPOINT': 'https://oidc.example.com/authorize', + 'AUTH_OPENID_PROVIDER_TOKEN_ENDPOINT': 'https://oidc.example.com/token', + 'AUTH_OPENID_PROVIDER_JWKS_ENDPOINT': 'https://oidc.example.com/jwks', + 'AUTH_OPENID_PROVIDER_USERINFO_ENDPOINT': 'https://oidc.example.com/userinfo', + 'AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT': 'https://oidc.example.com/logout', 'AUTH_OPENID_PROVIDER_SIGNATURE_ALG': 'HS256', 'AUTH_OPENID_PROVIDER_SIGNATURE_KEY': None, 'AUTH_OPENID_SCOPES': 'openid profile email', @@ -194,10 +195,13 @@ class Config(dict): 'AUTH_OPENID_USE_STATE': True, 'AUTH_OPENID_USE_NONCE': True, 'AUTH_OPENID_ALWAYS_UPDATE_USER': True, - # OpenID 旧配置参数 (version <= 1.5.8 (discarded)) - 'AUTH_OPENID_SERVER_URL': 'http://openid', + + # Keycloak 旧配置参数 (version <= 1.5.8 (discarded)) + 'AUTH_OPENID_KEYCLOAK': True, + 'AUTH_OPENID_SERVER_URL': 'https://keycloak.example.com', 'AUTH_OPENID_REALM_NAME': None, + # Raidus 认证 'AUTH_RADIUS': False, 'RADIUS_SERVER': 'localhost', 'RADIUS_PORT': 1812, @@ -205,8 +209,9 @@ class Config(dict): 'RADIUS_ENCRYPT_PASSWORD': True, 'OTP_IN_RADIUS': False, + # Cas 认证 'AUTH_CAS': False, - 'CAS_SERVER_URL': "http://host/cas/", + 'CAS_SERVER_URL': "https://example.com/cas/", 'CAS_ROOT_PROXIED_AS': '', 'CAS_LOGOUT_COMPLETELY': True, 'CAS_VERSION': 3, @@ -218,24 +223,31 @@ class Config(dict): 'AUTH_SSO': False, 'AUTH_SSO_AUTHKEY_TTL': 60 * 15, + # 企业微信 'AUTH_WECOM': False, 'WECOM_CORPID': '', 'WECOM_AGENTID': '', 'WECOM_SECRET': '', + # 钉钉 'AUTH_DINGTALK': False, 'DINGTALK_AGENTID': '', 'DINGTALK_APPKEY': '', 'DINGTALK_APPSECRET': '', + # 飞书 'AUTH_FEISHU': False, 'FEISHU_APP_ID': '', 'FEISHU_APP_SECRET': '', + 'LOGIN_REDIRECT_TO_BACKEND': '', # 'OPENID / CAS + 'LOGIN_REDIRECT_MSG_ENABLED': True, + 'OTP_VALID_WINDOW': 2, 'OTP_ISSUER_NAME': 'JumpServer', - 'EMAIL_SUFFIX': 'jumpserver.org', + 'EMAIL_SUFFIX': 'example.com', + # Terminal配置 'TERMINAL_PASSWORD_AUTH': True, 'TERMINAL_PUBLIC_KEY_AUTH': True, 'TERMINAL_HEARTBEAT_INTERVAL': 20, @@ -245,7 +257,9 @@ class Config(dict): 'TERMINAL_HOST_KEY': '', 'TERMINAL_TELNET_REGEX': '', 'TERMINAL_COMMAND_STORAGE': {}, + 'TERMINAL_RDP_ADDR': '', + # 安全配置 'SECURITY_MFA_AUTH': 0, # 0 不开启 1 全局开启 2 管理员开启 'SECURITY_COMMAND_EXECUTION': True, 'SECURITY_SERVICE_ACCOUNT_REGISTRATION': True, @@ -262,58 +276,60 @@ class Config(dict): 'SECURITY_PASSWORD_SPECIAL_CHAR': False, 'SECURITY_LOGIN_CHALLENGE_ENABLED': False, 'SECURITY_LOGIN_CAPTCHA_ENABLED': True, - 'SECURITY_DATA_CRYPTO_ALGO': 'aes', 'SECURITY_INSECURE_COMMAND': False, 'SECURITY_INSECURE_COMMAND_LEVEL': 5, 'SECURITY_INSECURE_COMMAND_EMAIL_RECEIVER': '', 'SECURITY_LUNA_REMEMBER_AUTH': True, 'SECURITY_WATERMARK_ENABLED': True, + 'SECURITY_MFA_VERIFY_TTL': 3600, 'SECURITY_SESSION_SHARE': True, + 'OLD_PASSWORD_HISTORY_LIMIT_COUNT': 5, + 'LOGIN_CONFIRM_ENABLE': False, # 准备废弃,放到 acl 中 + 'CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED': True, + 'USER_LOGIN_SINGLE_MACHINE_ENABLED': False, + 'ONLY_ALLOW_EXIST_USER_AUTH': False, + 'ONLY_ALLOW_AUTH_FROM_SOURCE': False, + # 启动前 'HTTP_BIND_HOST': '0.0.0.0', 'HTTP_LISTEN_PORT': 8080, 'WS_LISTEN_PORT': 8070, + 'SYSLOG_ADDR': '', # '192.168.0.1:514' + 'SYSLOG_FACILITY': 'user', + 'SYSLOG_SOCKTYPE': 2, + 'PERM_EXPIRED_CHECK_PERIODIC': 60 * 60, + 'FLOWER_URL': "127.0.0.1:5555", + 'LANGUAGE_CODE': 'zh', + 'TIME_ZONE': 'Asia/Shanghai', + 'FORCE_SCRIPT_NAME': '', + 'SESSION_COOKIE_SECURE': False, + 'CSRF_COOKIE_SECURE': False, + 'REFERER_CHECK_ENABLED': False, + 'SESSION_SAVE_EVERY_REQUEST': True, + 'SESSION_EXPIRE_AT_BROWSER_CLOSE_FORCE': False, + 'SERVER_REPLAY_STORAGE': {}, + 'SECURITY_DATA_CRYPTO_ALGO': 'aes', + + # 记录清理清理 'LOGIN_LOG_KEEP_DAYS': 200, 'TASK_LOG_KEEP_DAYS': 90, 'OPERATE_LOG_KEEP_DAYS': 200, 'FTP_LOG_KEEP_DAYS': 200, - 'ASSETS_PERM_CACHE_TIME': 3600 * 24, - 'SECURITY_MFA_VERIFY_TTL': 3600, - 'OLD_PASSWORD_HISTORY_LIMIT_COUNT': 5, - 'ASSETS_PERM_CACHE_ENABLE': HAS_XPACK, - 'SYSLOG_ADDR': '', # '192.168.0.1:514' - 'SYSLOG_FACILITY': 'user', - 'SYSLOG_SOCKTYPE': 2, - 'PERM_SINGLE_ASSET_TO_UNGROUP_NODE': False, - 'PERM_EXPIRED_CHECK_PERIODIC': 60 * 60, - 'WINDOWS_SSH_DEFAULT_SHELL': 'cmd', - 'FLOWER_URL': "127.0.0.1:5555", - 'DEFAULT_ORG_SHOW_ALL_USERS': True, - 'PERIOD_TASK_ENABLED': True, - 'FORCE_SCRIPT_NAME': '', - 'LOGIN_CONFIRM_ENABLE': False, - 'WINDOWS_SKIP_ALL_MANUAL_PASSWORD': False, - 'ORG_CHANGE_TO_URL': '', - 'LANGUAGE_CODE': 'zh', - 'TIME_ZONE': 'Asia/Shanghai', - 'CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED': True, - 'USER_LOGIN_SINGLE_MACHINE_ENABLED': False, - 'TICKETS_ENABLED': True, - 'SESSION_COOKIE_SECURE': False, - 'CSRF_COOKIE_SECURE': False, - 'REFERER_CHECK_ENABLED': False, - 'SERVER_REPLAY_STORAGE': {}, - 'CONNECTION_TOKEN_ENABLED': False, - 'ONLY_ALLOW_EXIST_USER_AUTH': False, - 'ONLY_ALLOW_AUTH_FROM_SOURCE': False, - 'SESSION_SAVE_EVERY_REQUEST': True, - 'SESSION_EXPIRE_AT_BROWSER_CLOSE_FORCE': False, - 'FORGOT_PASSWORD_URL': '', - 'HEALTH_CHECK_TOKEN': '', - 'LOGIN_REDIRECT_TO_BACKEND': None, # 'OPENID / CAS 'CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS': 30, - 'TERMINAL_RDP_ADDR': '' + # 废弃的 + 'DEFAULT_ORG_SHOW_ALL_USERS': True, + 'ORG_CHANGE_TO_URL': '', + 'WINDOWS_SKIP_ALL_MANUAL_PASSWORD': False, + 'CONNECTION_TOKEN_ENABLED': False, + + 'PERM_SINGLE_ASSET_TO_UNGROUP_NODE': False, + 'WINDOWS_SSH_DEFAULT_SHELL': 'cmd', + 'PERIOD_TASK_ENABLED': True, + + 'TICKETS_ENABLED': True, + 'FORGOT_PASSWORD_URL': '', + 'HEALTH_CHECK_TOKEN': '', } def compatible_auth_openid_of_key(self): @@ -324,6 +340,9 @@ 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: return diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index d586796ce..6dd1b1345 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -72,14 +72,9 @@ 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 -ASSETS_PERM_CACHE_TIME = CONFIG.ASSETS_PERM_CACHE_TIME - # Asset user auth external backend, default AuthBook backend BACKEND_ASSET_USER_AUTH_VAULT = False -DEFAULT_ORG_SHOW_ALL_USERS = CONFIG.DEFAULT_ORG_SHOW_ALL_USERS PERM_SINGLE_ASSET_TO_UNGROUP_NODE = CONFIG.PERM_SINGLE_ASSET_TO_UNGROUP_NODE PERM_EXPIRED_CHECK_PERIODIC = CONFIG.PERM_EXPIRED_CHECK_PERIODIC WINDOWS_SSH_DEFAULT_SHELL = CONFIG.WINDOWS_SSH_DEFAULT_SHELL @@ -132,5 +127,6 @@ SECURITY_WATERMARK_ENABLED = CONFIG.SECURITY_WATERMARK_ENABLED SECURITY_SESSION_SHARE = CONFIG.SECURITY_SESSION_SHARE LOGIN_REDIRECT_TO_BACKEND = CONFIG.LOGIN_REDIRECT_TO_BACKEND +LOGIN_REDIRECT_MSG_ENABLED = CONFIG.LOGIN_REDIRECT_MSG_ENABLED CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS = CONFIG.CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index d86c39893..3059ebf7a 100644 Binary files a/apps/locale/zh/LC_MESSAGES/django.mo and b/apps/locale/zh/LC_MESSAGES/django.mo differ diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 64638ff8a..d3d14eb9d 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-06 18:42+0800\n" +"POT-Creation-Date: 2021-09-09 13:51+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -46,7 +46,8 @@ msgstr "优先级可选范围为 1-100 (数值越小越优先)" #: acls/models/base.py:31 authentication/models.py:20 #: authentication/templates/authentication/_access_key_modal.html:32 -#: perms/models/base.py:48 users/templates/users/_select_user_modal.html:18 +#: perms/models/base.py:48 terminal/models/sharing.py:24 +#: users/templates/users/_select_user_modal.html:18 msgid "Active" msgstr "激活中" @@ -156,7 +157,7 @@ msgstr "" #: acls/serializers/login_acl.py:30 acls/serializers/login_asset_acl.py:31 #: applications/serializers/attrs/application_type/mysql_workbench.py:18 #: assets/models/asset.py:180 assets/models/domain.py:61 -#: assets/serializers/account.py:12 settings/serializers/settings.py:114 +#: assets/serializers/account.py:12 settings/serializers/terminal.py:8 #: users/templates/users/_granted_assets.html:26 #: users/templates/users/user_asset_permission.html:156 msgid "IP" @@ -198,7 +199,7 @@ msgstr "" #: acls/serializers/login_asset_acl.py:35 assets/models/asset.py:181 #: assets/serializers/account.py:13 assets/serializers/gathered_user.py:23 -#: settings/serializers/settings.py:113 +#: settings/serializers/terminal.py:7 #: users/templates/users/_granted_assets.html:25 #: users/templates/users/user_asset_permission.html:157 msgid "Hostname" @@ -308,7 +309,7 @@ msgstr "" #: assets/models/base.py:177 audits/signals_handler.py:63 #: authentication/forms.py:22 #: authentication/templates/authentication/login.html:164 -#: settings/serializers/settings.py:95 users/forms/profile.py:21 +#: settings/serializers/auth/ldap.py:44 users/forms/profile.py:21 #: users/templates/users/user_otp_check_password.html:13 #: users/templates/users/user_password_update.html:43 #: users/templates/users/user_password_verify.html:18 @@ -368,7 +369,8 @@ msgid "Cluster" msgstr "集群" #: applications/serializers/attrs/application_category/db.py:11 -#: ops/models/adhoc.py:146 xpack/plugins/cloud/serializers.py:65 +#: ops/models/adhoc.py:146 settings/serializers/auth/radius.py:14 +#: xpack/plugins/cloud/serializers.py:65 msgid "Host" msgstr "主机" @@ -378,7 +380,7 @@ msgstr "主机" #: applications/serializers/attrs/application_type/oracle.py:11 #: applications/serializers/attrs/application_type/pgsql.py:11 #: assets/models/asset.py:185 assets/models/domain.py:62 -#: xpack/plugins/cloud/serializers.py:66 +#: settings/serializers/auth/radius.py:15 xpack/plugins/cloud/serializers.py:66 msgid "Port" msgstr "端口" @@ -558,7 +560,7 @@ msgstr "创建者" msgid "Date created" msgstr "创建日期" -#: assets/models/authbook.py:17 +#: assets/models/authbook.py:17 settings/serializers/auth/cas.py:14 msgid "Version" msgstr "版本" @@ -1070,7 +1072,7 @@ msgid "Symlink" msgstr "建立软链接" #: audits/models.py:37 audits/models.py:60 audits/models.py:76 -#: terminal/models/session.py:45 +#: terminal/models/session.py:45 terminal/models/sharing.py:76 msgid "Remote addr" msgstr "远端地址" @@ -1082,7 +1084,7 @@ msgstr "操作" msgid "Filename" msgstr "文件名" -#: audits/models.py:42 audits/models.py:101 +#: audits/models.py:42 audits/models.py:101 terminal/models/sharing.py:84 msgid "Success" msgstr "成功" @@ -1162,7 +1164,8 @@ msgstr "用户代理" msgid "MFA" msgstr "多因子认证" -#: audits/models.py:111 xpack/plugins/change_auth_plan/models.py:336 +#: audits/models.py:111 terminal/models/sharing.py:88 +#: xpack/plugins/change_auth_plan/models.py:336 #: xpack/plugins/cloud/models.py:176 msgid "Reason" msgstr "原因" @@ -1429,7 +1432,7 @@ msgstr "{ApplicationPermission} *添加了* {SystemUser}" msgid "{ApplicationPermission} *REMOVE* {SystemUser}" msgstr "{ApplicationPermission} *移除了* {SystemUser}" -#: authentication/api/connection_token.py:222 +#: authentication/api/connection_token.py:226 msgid "Invalid token" msgstr "无效的令牌" @@ -1593,15 +1596,15 @@ msgstr "来源 IP 不被允许登录" msgid "SSO auth closed" msgstr "SSO 认证关闭了" -#: authentication/errors.py:273 authentication/mixins.py:277 +#: authentication/errors.py:273 authentication/mixins.py:319 msgid "Your password is too simple, please change it for security" msgstr "你的密码过于简单,为了安全,请修改" -#: authentication/errors.py:282 authentication/mixins.py:284 +#: authentication/errors.py:282 authentication/mixins.py:326 msgid "You should to change your password before login" msgstr "登录完成前,请先修改密码" -#: authentication/errors.py:291 authentication/mixins.py:291 +#: authentication/errors.py:291 authentication/mixins.py:333 msgid "Your password has expired, please reset before logging in" msgstr "您的密码已过期,先修改再登录" @@ -1618,7 +1621,7 @@ msgstr "{} 天内自动登录" msgid "MFA code" msgstr "多因子认证验证码" -#: authentication/mixins.py:267 +#: authentication/mixins.py:309 msgid "Please change your password" msgstr "请修改密码" @@ -1648,8 +1651,9 @@ msgid "ID" msgstr "ID" #: authentication/templates/authentication/_access_key_modal.html:31 +#: settings/serializers/auth/radius.py:17 msgid "Secret" -msgstr "秘钥" +msgstr "密钥" #: authentication/templates/authentication/_access_key_modal.html:33 msgid "Date" @@ -1661,7 +1665,7 @@ msgid "Show" msgstr "显示" #: authentication/templates/authentication/_access_key_modal.html:66 -#: settings/serializers/settings.py:149 users/models/user.py:464 +#: settings/serializers/security.py:25 users/models/user.py:464 #: users/serializers/profile.py:99 #: users/templates/users/user_verify_mfa.html:32 msgid "Disable" @@ -1750,7 +1754,7 @@ msgid "Open MFA Authenticator and enter the 6-bit dynamic code" msgstr "请打开MFA验证器,输入6位动态码" #: authentication/templates/authentication/login_otp.html:26 -#: users/templates/users/user_otp_check_password.html:15 +#: users/templates/users/user_otp_check_password.html:16 #: users/templates/users/user_otp_enable_bind.html:24 #: users/templates/users/user_otp_enable_install_app.html:29 #: users/templates/users/user_verify_mfa.html:26 @@ -1869,19 +1873,19 @@ msgstr "请使用密码登录,然后绑定飞书" msgid "Binding FeiShu failed" msgstr "绑定飞书失败" -#: authentication/views/login.py:80 +#: authentication/views/login.py:78 msgid "Redirecting" msgstr "跳转中" -#: authentication/views/login.py:81 +#: authentication/views/login.py:79 msgid "Redirecting to {} authentication" msgstr "正在跳转到 {} 认证" -#: authentication/views/login.py:107 +#: authentication/views/login.py:105 msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" -#: authentication/views/login.py:227 +#: authentication/views/login.py:215 msgid "" "Wait for {} confirm, You also can copy link to her/him
\n" " Don't close this page" @@ -1889,15 +1893,15 @@ msgstr "" "等待 {} 确认, 你也可以复制链接发给他/她
\n" " 不要关闭本页面" -#: authentication/views/login.py:232 +#: authentication/views/login.py:220 msgid "No ticket found" msgstr "没有发现工单" -#: authentication/views/login.py:264 +#: authentication/views/login.py:252 msgid "Logout success" msgstr "退出登录成功" -#: authentication/views/login.py:265 +#: authentication/views/login.py:253 msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" @@ -2114,6 +2118,7 @@ msgid "Cycle perform" msgstr "周期执行" #: ops/mixin.py:33 ops/mixin.py:90 ops/mixin.py:109 ops/mixin.py:150 +#: settings/serializers/auth/ldap.py:64 msgid "Regularly perform" msgstr "定期执行" @@ -2122,7 +2127,7 @@ msgstr "定期执行" msgid "Periodic perform" msgstr "定时执行" -#: ops/mixin.py:112 +#: ops/mixin.py:112 settings/serializers/auth/ldap.py:61 msgid "Interval" msgstr "间隔" @@ -2149,9 +2154,9 @@ msgstr "" "分 时 日 月 星期> (在线工" "具
注意: 如果同时设置了定期执行和周期执行,优先使用定期执行" -#: ops/mixin.py:162 -msgid "Tips: (Units: hour)" -msgstr "提示:(单位: 时)" +#: ops/mixin.py:162 settings/serializers/auth/ldap.py:61 +msgid "Unit: hour" +msgstr "单位: 时" #: ops/models/adhoc.py:35 msgid "Callback" @@ -2309,7 +2314,7 @@ msgstr "该授权暂时不能撤销" msgid "Application" msgstr "应用程序" -#: perms/models/asset_permission.py:37 settings/serializers/settings.py:118 +#: perms/models/asset_permission.py:37 settings/serializers/terminal.py:12 msgid "All" msgstr "全部" @@ -2373,17 +2378,13 @@ msgid "Date expired" msgstr "失效日期" #: perms/models/base.py:54 -#, fuzzy -#| msgid "No ticket found" msgid "From ticket" -msgstr "没有发现工单" +msgstr "来自工单" #: perms/serializers/application/permission.py:17 #: perms/serializers/asset/permission.py:43 -#, fuzzy -#| msgid "Authentication failed" msgid "Authorization rules" -msgstr "认证失败" +msgstr "授权规则" #: perms/serializers/application/permission.py:20 #: perms/serializers/application/permission.py:40 @@ -2441,20 +2442,15 @@ msgstr "节点名称" msgid "System users display" msgstr "系统用户名称" -#: settings/api/common.py:25 -msgid "Test mail sent to {}, please check" -msgstr "邮件已经发送{}, 请检查" - -#: settings/api/common.py:100 xpack/plugins/interface/api.py:18 -#: xpack/plugins/interface/models.py:36 -msgid "Welcome to the JumpServer open source Bastion Host" -msgstr "欢迎使用JumpServer开源堡垒机" - #: settings/api/dingtalk.py:36 settings/api/feishu.py:35 #: settings/api/wecom.py:36 msgid "Test success" msgstr "测试成功" +#: settings/api/email.py:21 +msgid "Test mail sent to {}, please check" +msgstr "邮件已经发送{}, 请检查" + #: settings/api/ldap.py:194 msgid "Get ldap users is None" msgstr "获取 LDAP 用户为 None" @@ -2463,170 +2459,125 @@ msgstr "获取 LDAP 用户为 None" msgid "Imported {} users successfully (Organization: {})" msgstr "成功导入 {} 个用户 ( 组织: {} )" +#: settings/api/public.py:41 xpack/plugins/interface/api.py:18 +#: xpack/plugins/interface/models.py:36 +msgid "Welcome to the JumpServer open source Bastion Host" +msgstr "欢迎使用JumpServer开源堡垒机" + #: settings/models.py:123 users/templates/users/reset_password.html:29 msgid "Setting" msgstr "设置" -#: settings/serializers/settings.py:16 -msgid "Site url" -msgstr "当前站点URL" +#: settings/serializers/auth/base.py:10 +msgid "CAS Auth" +msgstr "CAS 认证" -#: settings/serializers/settings.py:17 -msgid "eg: http://dev.jumpserver.org:8080" -msgstr "如: http://dev.jumpserver.org:8080" +#: settings/serializers/auth/base.py:11 +msgid "OPENID Auth" +msgstr "OIDC 认证" -#: settings/serializers/settings.py:21 -msgid "User guide url" -msgstr "用户向导URL" +#: settings/serializers/auth/base.py:12 +msgid "RADIUS Auth" +msgstr "RADIUS 认证" -#: settings/serializers/settings.py:22 -msgid "User first login update profile done redirect to it" -msgstr "用户第一次登录,修改profile后重定向到地址, 可以是 wiki 或 其他说明文档" +#: settings/serializers/auth/base.py:13 +msgid "DingTalk Auth" +msgstr "钉钉 认证" -#: settings/serializers/settings.py:25 +#: settings/serializers/auth/base.py:14 +msgid "FeiShu Auth" +msgstr "飞书 认证" + +#: settings/serializers/auth/base.py:15 +msgid "WeCom Auth" +msgstr "企业微信 认证" + +#: settings/serializers/auth/base.py:16 +msgid "SSO Auth" +msgstr "SSO Token 认证" + +#: settings/serializers/auth/base.py:18 settings/serializers/basic.py:15 msgid "Forgot password url" -msgstr "忘记密码URL" +msgstr "忘记密码 URL" -#: settings/serializers/settings.py:26 -msgid "" -"The forgot password url on login page, If you use ldap or cas external " -"authentication, you can set it" -msgstr "" -"登录页面忘记密码URL, 如果使用了 LDAP, OPENID 等外部认证系统,可以自定义用户重" -"置密码访问的地址" +#: settings/serializers/auth/base.py:21 +msgid "Health check token" +msgstr "健康检查 Token" -#: settings/serializers/settings.py:30 -msgid "Global organization name" -msgstr "全局组织名" +#: settings/serializers/auth/base.py:24 +msgid "Enable login redirect msg" +msgstr "启用登录跳转提示" -#: settings/serializers/settings.py:31 -msgid "The name of global organization to display" -msgstr "全局组织的显示名称,默认为 全局组织" +#: settings/serializers/auth/cas.py:11 +msgid "Enable CAS Auth" +msgstr "启用 CAS 认证" -#: settings/serializers/settings.py:38 -msgid "SMTP host" -msgstr "SMTP 主机" +#: settings/serializers/auth/cas.py:12 settings/serializers/auth/oidc.py:32 +msgid "Server url" +msgstr "服务端地址" -#: settings/serializers/settings.py:39 -msgid "SMTP port" -msgstr "SMTP 端口" +#: settings/serializers/auth/cas.py:13 +msgid "Logout completely" +msgstr "同步注销" -#: settings/serializers/settings.py:40 -msgid "SMTP account" -msgstr "SMTP 账号" +#: settings/serializers/auth/cas.py:15 +msgid "Username attr" +msgstr "用户名属性" -#: settings/serializers/settings.py:42 -msgid "SMTP password" -msgstr "SMTP 密码" +#: settings/serializers/auth/cas.py:16 +msgid "Enable attributes map" +msgstr "启用属性映射" -#: settings/serializers/settings.py:43 -msgid "Tips: Some provider use token except password" -msgstr "提示:一些邮件提供商需要输入的是授权码" +#: settings/serializers/auth/cas.py:17 +msgid "Rename attr" +msgstr "映射属性" -#: settings/serializers/settings.py:46 -msgid "Send user" -msgstr "发件人" +#: settings/serializers/auth/cas.py:18 +msgid "Create user if not" +msgstr "创建用户(如果不存在)" -#: settings/serializers/settings.py:47 -msgid "Tips: Send mail account, default SMTP account as the send account" -msgstr "提示:发送邮件账号,默认使用 SMTP 账号作为发送账号" +#: settings/serializers/auth/dingtalk.py:11 +msgid "Enable DingTalk Auth" +msgstr "启用钉钉认证" -#: settings/serializers/settings.py:50 -msgid "Test recipient" -msgstr "测试收件人" +#: settings/serializers/auth/feishu.py:10 +msgid "Enable FeiShu Auth" +msgstr "启用飞书认证" -#: settings/serializers/settings.py:51 -msgid "Tips: Used only as a test mail recipient" -msgstr "提示:仅用来作为测试邮件收件人" - -#: settings/serializers/settings.py:54 -msgid "Use SSL" -msgstr "使用 SSL" - -#: settings/serializers/settings.py:55 -msgid "If SMTP port is 465, may be select" -msgstr "如果SMTP端口是465,通常需要启用 SSL" - -#: settings/serializers/settings.py:58 -msgid "Use TLS" -msgstr "使用 TLS" - -#: settings/serializers/settings.py:59 -msgid "If SMTP port is 587, may be select" -msgstr "如果SMTP端口是587,通常需要启用 TLS" - -#: settings/serializers/settings.py:62 -msgid "Subject prefix" -msgstr "主题前缀" - -#: settings/serializers/settings.py:69 -msgid "Create user email subject" -msgstr "邮件主题" - -#: settings/serializers/settings.py:70 -msgid "" -"Tips: When creating a user, send the subject of the email (eg:Create account " -"successfully)" -msgstr "提示: 创建用户时,发送设置密码邮件的主题 (例如: 创建用户成功)" - -#: settings/serializers/settings.py:74 -msgid "Create user honorific" -msgstr "邮件的敬语" - -#: settings/serializers/settings.py:75 -msgid "Tips: When creating a user, send the honorific of the email (eg:Hello)" -msgstr "提示: 创建用户时,发送设置密码邮件的敬语 (例如: 您好)" - -#: settings/serializers/settings.py:79 -msgid "Create user email content" -msgstr "邮件的内容" - -#: settings/serializers/settings.py:80 -msgid "Tips:When creating a user, send the content of the email" -msgstr "提示: 创建用户时,发送设置密码邮件的内容" - -#: settings/serializers/settings.py:83 -msgid "Signature" -msgstr "署名" - -#: settings/serializers/settings.py:84 -msgid "Tips: Email signature (eg:jumpserver)" -msgstr "邮件署名 (如:jumpserver)" - -#: settings/serializers/settings.py:92 +#: settings/serializers/auth/ldap.py:39 msgid "LDAP server" msgstr "LDAP 地址" -#: settings/serializers/settings.py:92 +#: settings/serializers/auth/ldap.py:40 msgid "eg: ldap://localhost:389" msgstr "如: ldap://localhost:389" -#: settings/serializers/settings.py:94 +#: settings/serializers/auth/ldap.py:42 msgid "Bind DN" msgstr "绑定 DN" -#: settings/serializers/settings.py:97 +#: settings/serializers/auth/ldap.py:46 msgid "User OU" msgstr "用户 OU" -#: settings/serializers/settings.py:98 +#: settings/serializers/auth/ldap.py:47 msgid "Use | split multi OUs" msgstr "多个 OU 使用 | 分割" -#: settings/serializers/settings.py:101 +#: settings/serializers/auth/ldap.py:50 msgid "User search filter" msgstr "用户过滤器" -#: settings/serializers/settings.py:102 +#: settings/serializers/auth/ldap.py:51 #, python-format msgid "Choice may be (cn|uid|sAMAccountName)=%(user)s)" msgstr "可能的选项是(cn或uid或sAMAccountName=%(user)s)" -#: settings/serializers/settings.py:105 +#: settings/serializers/auth/ldap.py:54 msgid "User attr map" msgstr "用户属性映射" -#: settings/serializers/settings.py:106 +#: settings/serializers/auth/ldap.py:55 msgid "" "User attr map present how to map LDAP user attr to jumpserver, username,name," "email is jumpserver attr" @@ -2634,23 +2585,497 @@ msgstr "" "用户属性映射代表怎样将LDAP中用户属性映射到jumpserver用户上,username, name," "email 是jumpserver的用户需要属性" -#: settings/serializers/settings.py:108 +#: settings/serializers/auth/ldap.py:58 xpack/plugins/cloud/serializers.py:211 +#: xpack/plugins/gathered_user/serializers.py:20 +msgid "Periodic display" +msgstr "定时执行" + +#: settings/serializers/auth/ldap.py:66 +msgid "Connect timeout" +msgstr "连接超时时间" + +#: settings/serializers/auth/ldap.py:67 +msgid "Search paged size" +msgstr "搜索分页数量" + +#: settings/serializers/auth/ldap.py:69 msgid "Enable LDAP auth" msgstr "启用 LDAP 认证" -#: settings/serializers/settings.py:119 +#: settings/serializers/auth/oidc.py:12 +msgid "Base site url" +msgstr "JumpServer 地址" + +#: settings/serializers/auth/oidc.py:15 +msgid "Client Id" +msgstr "客户端 ID" + +#: settings/serializers/auth/oidc.py:18 xpack/plugins/cloud/serializers.py:33 +msgid "Client Secret" +msgstr "客户端密钥" + +#: settings/serializers/auth/oidc.py:20 +msgid "Share session" +msgstr "共享会话" + +#: settings/serializers/auth/oidc.py:22 +msgid "Ignore ssl verification" +msgstr "忽略 SSL 证书验证" + +#: settings/serializers/auth/oidc.py:29 +msgid "Use Keycloak" +msgstr "使用 Keycloak" + +#: settings/serializers/auth/oidc.py:35 +msgid "Realm name" +msgstr "域" + +#: settings/serializers/auth/oidc.py:41 +msgid "Enable OPENID Auth" +msgstr "启用 OIDC 认证" + +#: settings/serializers/auth/oidc.py:43 +msgid "Provider endpoint" +msgstr "端点地址" + +#: settings/serializers/auth/oidc.py:46 +msgid "Provider auth endpoint" +msgstr "授权端点地址" + +#: settings/serializers/auth/oidc.py:49 +msgid "Provider token endpoint" +msgstr "token 端点地址" + +#: settings/serializers/auth/oidc.py:52 +msgid "Provider jwks endpoint" +msgstr "jwks 端点地址" + +#: settings/serializers/auth/oidc.py:55 +msgid "Provider userinfo endpoint" +msgstr "用户信息端点地址" + +#: settings/serializers/auth/oidc.py:58 +msgid "Provider end session endpoint" +msgstr "注销会话端点地址" + +#: settings/serializers/auth/oidc.py:61 +msgid "Provider sign alg" +msgstr "签名算法" + +#: settings/serializers/auth/oidc.py:64 +msgid "Provider sign key" +msgstr "签名 Key" + +#: settings/serializers/auth/oidc.py:66 +msgid "Scopes" +msgstr "连接范围" + +#: settings/serializers/auth/oidc.py:68 +msgid "Id token max age" +msgstr "令牌有效时间" + +#: settings/serializers/auth/oidc.py:71 +msgid "Id token include claims" +msgstr "声明" + +#: settings/serializers/auth/oidc.py:73 +msgid "Use state" +msgstr "使用状态" + +#: settings/serializers/auth/oidc.py:74 +msgid "Use nonce" +msgstr "临时使用" + +#: settings/serializers/auth/oidc.py:76 +msgid "Always update user" +msgstr "总是更新用户信息" + +#: settings/serializers/auth/radius.py:13 +msgid "Enable RADIUS Auth" +msgstr "启用 RADIUS 认证" + +#: settings/serializers/auth/radius.py:19 +msgid "OTP in radius" +msgstr "使用 Radius OTP" + +#: settings/serializers/auth/sso.py:12 +msgid "Enable SSO auth" +msgstr "启用 SSO Token 认证" + +#: settings/serializers/auth/sso.py:13 +msgid "Other service can using SSO token login to JumpServer without password" +msgstr "其它系统可以使用 SSO Token 对接 JumpServer, 免去登录的过程" + +#: settings/serializers/auth/sso.py:16 +msgid "SSO auth key TTL" +msgstr "Token 有效期" + +#: settings/serializers/auth/sso.py:16 settings/serializers/security.py:72 +msgid "Unit: second" +msgstr "单位: 秒" + +#: settings/serializers/auth/wecom.py:11 +msgid "Enable WeCom Auth" +msgstr "启用企业微信认证" + +#: settings/serializers/basic.py:7 +msgid "Site url" +msgstr "当前站点URL" + +#: settings/serializers/basic.py:8 +msgid "eg: http://dev.jumpserver.org:8080" +msgstr "如: http://dev.jumpserver.org:8080" + +#: settings/serializers/basic.py:11 +msgid "User guide url" +msgstr "用户向导URL" + +#: settings/serializers/basic.py:12 +msgid "User first login update profile done redirect to it" +msgstr "用户第一次登录,修改profile后重定向到地址, 可以是 wiki 或 其他说明文档" + +#: settings/serializers/basic.py:16 +msgid "" +"The forgot password url on login page, If you use ldap or cas external " +"authentication, you can set it" +msgstr "" +"登录页面忘记密码URL, 如果使用了 LDAP, OPENID 等外部认证系统,可以自定义用户重" +"置密码访问的地址" + +#: settings/serializers/basic.py:20 +msgid "Global organization name" +msgstr "全局组织名" + +#: settings/serializers/basic.py:21 +msgid "The name of global organization to display" +msgstr "全局组织的显示名称,默认为 全局组织" + +#: settings/serializers/cleaning.py:9 +msgid "Login log keep days" +msgstr "登录日志" + +#: settings/serializers/cleaning.py:9 settings/serializers/cleaning.py:12 +#: settings/serializers/cleaning.py:15 settings/serializers/cleaning.py:18 +#: settings/serializers/cleaning.py:21 +msgid "Unit: day" +msgstr "单位: 天" + +#: settings/serializers/cleaning.py:12 +msgid "Task log keep days" +msgstr "任务日志" + +#: settings/serializers/cleaning.py:15 +msgid "Operate log keep days" +msgstr "操作日志" + +#: settings/serializers/cleaning.py:18 +msgid "FTP log keep days" +msgstr "上传下载" + +#: settings/serializers/cleaning.py:21 +msgid "Cloud sync record keep days" +msgstr "云同步记录" + +#: settings/serializers/email.py:24 +msgid "SMTP host" +msgstr "SMTP 主机" + +#: settings/serializers/email.py:25 +msgid "SMTP port" +msgstr "SMTP 端口" + +#: settings/serializers/email.py:26 +msgid "SMTP account" +msgstr "SMTP 账号" + +#: settings/serializers/email.py:28 +msgid "SMTP password" +msgstr "SMTP 密码" + +#: settings/serializers/email.py:29 +msgid "Tips: Some provider use token except password" +msgstr "提示:一些邮件提供商需要输入的是授权码" + +#: settings/serializers/email.py:32 +msgid "Send user" +msgstr "发件人" + +#: settings/serializers/email.py:33 +msgid "Tips: Send mail account, default SMTP account as the send account" +msgstr "提示:发送邮件账号,默认使用 SMTP 账号作为发送账号" + +#: settings/serializers/email.py:36 +msgid "Test recipient" +msgstr "测试收件人" + +#: settings/serializers/email.py:37 +msgid "Tips: Used only as a test mail recipient" +msgstr "提示:仅用来作为测试邮件收件人" + +#: settings/serializers/email.py:40 +msgid "Use SSL" +msgstr "使用 SSL" + +#: settings/serializers/email.py:41 +msgid "If SMTP port is 465, may be select" +msgstr "如果SMTP端口是465,通常需要启用 SSL" + +#: settings/serializers/email.py:44 +msgid "Use TLS" +msgstr "使用 TLS" + +#: settings/serializers/email.py:45 +msgid "If SMTP port is 587, may be select" +msgstr "如果SMTP端口是587,通常需要启用 TLS" + +#: settings/serializers/email.py:48 +msgid "Subject prefix" +msgstr "主题前缀" + +#: settings/serializers/email.py:55 +msgid "Create user email subject" +msgstr "邮件主题" + +#: settings/serializers/email.py:56 +msgid "" +"Tips: When creating a user, send the subject of the email (eg:Create account " +"successfully)" +msgstr "提示: 创建用户时,发送设置密码邮件的主题 (例如: 创建用户成功)" + +#: settings/serializers/email.py:60 +msgid "Create user honorific" +msgstr "邮件的敬语" + +#: settings/serializers/email.py:61 +msgid "Tips: When creating a user, send the honorific of the email (eg:Hello)" +msgstr "提示: 创建用户时,发送设置密码邮件的敬语 (例如: 您好)" + +#: settings/serializers/email.py:65 +msgid "Create user email content" +msgstr "邮件的内容" + +#: settings/serializers/email.py:66 +msgid "Tips:When creating a user, send the content of the email" +msgstr "提示: 创建用户时,发送设置密码邮件的内容" + +#: settings/serializers/email.py:69 +msgid "Signature" +msgstr "署名" + +#: settings/serializers/email.py:70 +msgid "Tips: Email signature (eg:jumpserver)" +msgstr "邮件署名 (如:jumpserver)" + +#: settings/serializers/other.py:9 +msgid "Email suffix" +msgstr "邮件后缀" + +#: settings/serializers/other.py:10 +msgid "" +"This is used by default if no email is returned during SSO authentication" +msgstr "SSO认证时,如果没有返回邮件地址,将使用该后缀" + +#: settings/serializers/other.py:12 +msgid "Enable tickets" +msgstr "开启工单系统" + +#: settings/serializers/other.py:15 +msgid "OTP issuer name" +msgstr "OTP 扫描后的名称" + +#: settings/serializers/other.py:17 +msgid "OTP valid window" +msgstr "OTP 延迟有效次数" + +#: settings/serializers/other.py:19 +msgid "Enable period task" +msgstr "启用周期任务" + +#: settings/serializers/other.py:21 +msgid "Ansible windows default shell" +msgstr "Ansible windows shell" + +#: settings/serializers/other.py:25 +msgid "Perm single to ungroup node" +msgstr "直接授权资产放在未分组节点" + +#: settings/serializers/security.py:8 +msgid "Password minimum length" +msgstr "密码最小长度" + +#: settings/serializers/security.py:12 +msgid "Admin user password minimum length" +msgstr "管理员密码最小长度" + +#: settings/serializers/security.py:15 +msgid "Must contain capital" +msgstr "必须包含大写字符" + +#: settings/serializers/security.py:17 +msgid "Must contain lowercase" +msgstr "必须包含小写字符" + +#: settings/serializers/security.py:18 +msgid "Must contain numeric" +msgstr "必须包含数字" + +#: settings/serializers/security.py:19 +msgid "Must contain special" +msgstr "必须包含特殊字符" + +#: settings/serializers/security.py:26 +msgid "All users" +msgstr "所有用户" + +#: settings/serializers/security.py:27 +msgid "Only admin users" +msgstr "仅管理员" + +#: settings/serializers/security.py:29 +msgid "Global MFA auth" +msgstr "全局启用 MFA 认证" + +#: settings/serializers/security.py:33 +msgid "Limit the number of login failures" +msgstr "限制登录失败次数" + +#: settings/serializers/security.py:37 +msgid "Block logon interval" +msgstr "禁止登录时间间隔" + +#: settings/serializers/security.py:39 +msgid "" +"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/security.py:45 +msgid "User password expiration" +msgstr "用户密码过期时间" + +#: settings/serializers/security.py:47 +msgid "" +"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/security.py:54 +msgid "Number of repeated historical passwords" +msgstr "不能设置近几次密码" + +#: settings/serializers/security.py:56 +msgid "" +"Tip: When the user resets the password, it cannot be the previous n " +"historical passwords of the user" +msgstr "提示:用户重置密码时,不能为该用户前几次使用过的密码" + +#: settings/serializers/security.py:61 +msgid "Only single device login" +msgstr "仅一台设备登录" + +#: settings/serializers/security.py:62 +msgid "Next device login, pre login will be logout" +msgstr "下个设备登录,上次登录会被顶掉" + +#: settings/serializers/security.py:65 +msgid "Only exist user login" +msgstr "仅已存在用户登录" + +#: settings/serializers/security.py:66 settings/serializers/security.py:70 +msgid "If enable, CAS、OIDC auth will be failed, if user not exist yet" +msgstr "开启后,如果系统中不存在该用户,CAS、OIDC 登录将会失败" + +#: settings/serializers/security.py:69 +msgid "Only from source login" +msgstr "仅从用户来源登录" + +#: settings/serializers/security.py:72 +msgid "MFA verify TTL" +msgstr "MFA 校验有效期" + +#: settings/serializers/security.py:75 +msgid "Enable Login captcha" +msgstr "启用登录验证码" + +#: settings/serializers/security.py:81 +msgid "Enable terminal register" +msgstr "终端注册" + +#: settings/serializers/security.py:82 +msgid "" +"Allow terminal register, after all terminal setup, you should disable this " +"for security" +msgstr "是否允许终端注册,当所有终端启动后,为了安全应该关闭" + +#: settings/serializers/security.py:85 +msgid "Replay watermark" +msgstr "录像水印" + +#: settings/serializers/security.py:86 +msgid "Enabled, the session replay contains watermark information" +msgstr "启用后,会话录像将包含水印信息" + +#: settings/serializers/security.py:90 +msgid "Connection max idle time" +msgstr "连接最大空闲时间" + +#: settings/serializers/security.py:91 +msgid "If idle time more than it, disconnect connection Unit: minute" +msgstr "提示:如果超过该配置没有操作,连接会被断开 (单位:分)" + +#: settings/serializers/security.py:94 +msgid "Remember manual auth" +msgstr "保存手动输入密码" + +#: settings/serializers/security.py:97 +msgid "Enable change auth secure mode" +msgstr "启用改密安全模式" + +#: settings/serializers/security.py:100 +msgid "Insecure command alert" +msgstr "危险命令告警" + +#: settings/serializers/security.py:103 +msgid "Email recipient" +msgstr "邮件收件人" + +#: settings/serializers/security.py:104 +msgid "Multiple user using , split" +msgstr "多个用户,使用 , 分割" + +#: settings/serializers/security.py:107 +msgid "Batch command execution" +msgstr "批量命令执行" + +#: settings/serializers/security.py:108 +msgid "Allow user run batch command or not using ansible" +msgstr "是否允许用户使用 ansible 执行批量命令" + +#: settings/serializers/security.py:111 +msgid "Session share" +msgstr "会话分享" + +#: settings/serializers/security.py:112 +msgid "Enabled, Allows user active session to be shared with other users" +msgstr "开启后允许用户分享已连接的资产会话给它人,协同工作" + +#: settings/serializers/terminal.py:13 msgid "Auto" msgstr "自动" -#: settings/serializers/settings.py:125 +#: settings/serializers/terminal.py:19 msgid "Password auth" msgstr "密码认证" -#: settings/serializers/settings.py:127 +#: settings/serializers/terminal.py:21 msgid "Public key auth" msgstr "密钥认证" -#: settings/serializers/settings.py:128 +#: settings/serializers/terminal.py:22 msgid "" "Tips: If use other auth method, like AD/LDAP, you should disable this to " "avoid being able to log in after deleting" @@ -2658,171 +3083,44 @@ msgstr "" "提示:如果你使用其它认证方式,如 AD/LDAP,你应该禁用此项,以避免第三方系统删" "除后,还可以登录" -#: settings/serializers/settings.py:131 +#: settings/serializers/terminal.py:25 msgid "List sort by" msgstr "资产列表排序" -#: settings/serializers/settings.py:132 +#: settings/serializers/terminal.py:27 msgid "List page size" msgstr "资产列表每页数量" -#: settings/serializers/settings.py:134 +#: settings/serializers/terminal.py:29 msgid "Session keep duration" msgstr "会话日志保存时间" -#: settings/serializers/settings.py:135 +#: settings/serializers/terminal.py:30 msgid "" -"Units: days, Session, record, command will be delete if more than duration, " +"Unit: days, Session, record, command will be delete if more than duration, " "only in database" msgstr "" "单位:天。 会话、录像、命令记录超过该时长将会被删除(仅影响数据库存储, oss等不" "受影响)" -#: settings/serializers/settings.py:137 +#: settings/serializers/terminal.py:33 msgid "Telnet login regex" msgstr "Telnet 成功正则表达式" -#: settings/serializers/settings.py:139 +#: settings/serializers/terminal.py:34 +msgid "" +"The login success message varies with devices. if you cannot log in to the " +"device through Telnet, set this parameter" +msgstr "不同设备登录成功提示不一样,所以如果 telnet 不能正常登录,可以这里设置" + +#: settings/serializers/terminal.py:38 msgid "RDP address" msgstr "RDP 地址" -#: settings/serializers/settings.py:142 +#: settings/serializers/terminal.py:41 msgid "RDP visit address, eg: dev.jumpserver.org:3389" msgstr "RDP 访问地址, 如: dev.jumpserver.org:3389" -#: settings/serializers/settings.py:150 -msgid "All users" -msgstr "所有用户" - -#: settings/serializers/settings.py:151 -msgid "Only admin users" -msgstr "仅管理员" - -#: settings/serializers/settings.py:153 -msgid "Global MFA auth" -msgstr "全局启用 MFA 认证" - -#: settings/serializers/settings.py:156 -msgid "Batch command execution" -msgstr "批量命令执行" - -#: settings/serializers/settings.py:157 -msgid "Allow user run batch command or not using ansible" -msgstr "是否允许用户使用 ansible 执行批量命令" - -#: settings/serializers/settings.py:160 -msgid "Enable terminal register" -msgstr "终端注册" - -#: settings/serializers/settings.py:161 -msgid "" -"Allow terminal register, after all terminal setup, you should disable this " -"for security" -msgstr "是否允许终端注册,当所有终端启动后,为了安全应该关闭" - -#: settings/serializers/settings.py:164 -msgid "Replay watermark" -msgstr "录像水印" - -#: settings/serializers/settings.py:165 -msgid "Enabled, the session replay contains watermark information" -msgstr "启用后,会话录像将包含水印信息" - -#: settings/serializers/settings.py:169 -msgid "Limit the number of login failures" -msgstr "限制登录失败次数" - -#: settings/serializers/settings.py:173 -msgid "Block logon interval" -msgstr "禁止登录时间间隔" - -#: settings/serializers/settings.py:174 -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:178 -msgid "Connection max idle time" -msgstr "连接最大空闲时间" - -#: settings/serializers/settings.py:179 -msgid "If idle time more than it, disconnect connection Unit: minute" -msgstr "提示:如果超过该配置没有操作,连接会被断开 (单位:分)" - -#: settings/serializers/settings.py:183 -msgid "User password expiration" -msgstr "用户密码过期时间" - -#: settings/serializers/settings.py:184 -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:188 -msgid "Number of repeated historical passwords" -msgstr "不能设置近几次密码" - -#: settings/serializers/settings.py:189 -msgid "" -"Tip: When the user resets the password, it cannot be the previous n " -"historical passwords of the user" -msgstr "提示:用户重置密码时,不能为该用户前几次使用过的密码" - -#: settings/serializers/settings.py:193 -msgid "Password minimum length" -msgstr "密码最小长度" - -#: settings/serializers/settings.py:197 -msgid "Admin user password minimum length" -msgstr "管理员密码最小长度" - -#: settings/serializers/settings.py:200 -msgid "Must contain capital" -msgstr "必须包含大写字符" - -#: settings/serializers/settings.py:202 -msgid "Must contain lowercase" -msgstr "必须包含小写字符" - -#: settings/serializers/settings.py:203 -msgid "Must contain numeric" -msgstr "必须包含数字" - -#: settings/serializers/settings.py:204 -msgid "Must contain special" -msgstr "必须包含特殊字符" - -#: settings/serializers/settings.py:205 -msgid "Insecure command alert" -msgstr "危险命令告警" - -#: settings/serializers/settings.py:207 -msgid "Email recipient" -msgstr "邮件收件人" - -#: settings/serializers/settings.py:208 -msgid "Multiple user using , split" -msgstr "多个用户,使用 , 分割" - -#: settings/serializers/settings.py:216 -msgid "Enable WeCom Auth" -msgstr "启用企业微信认证" - -#: settings/serializers/settings.py:223 -msgid "Enable DingTalk Auth" -msgstr "启用钉钉认证" - -#: settings/serializers/settings.py:229 -msgid "Enable FeiShu Auth" -msgstr "启用飞书认证" - #: settings/utils/ldap.py:412 msgid "ldap:// or ldaps:// protocol is used." msgstr "使用 ldap:// 或 ldaps:// 协议" @@ -3389,6 +3687,10 @@ msgstr "用户不存在: {}" msgid "User does not have permission" msgstr "用户没有权限" +#: terminal/api/sharing.py:28 +msgid "Secure session sharing settings is disabled" +msgstr "" + #: terminal/api/storage.py:30 msgid "Deleting the default storage is not allowed" msgstr "不允许删除默认存储配置" @@ -3442,7 +3744,8 @@ msgstr "输入" msgid "Output" msgstr "输出" -#: terminal/backends/command/models.py:23 +#: terminal/backends/command/models.py:23 terminal/models/sharing.py:15 +#: terminal/models/sharing.py:58 msgid "Session" msgstr "会话" @@ -3488,7 +3791,7 @@ msgstr "不支持批量创建" msgid "Storage is invalid" msgstr "存储无效" -#: terminal/models/session.py:44 +#: terminal/models/session.py:44 terminal/models/sharing.py:81 msgid "Login from" msgstr "登录来源" @@ -3500,6 +3803,50 @@ msgstr "回放" msgid "Date end" msgstr "结束日期" +#: terminal/models/sharing.py:20 +msgid "Creator" +msgstr "创建者" + +#: terminal/models/sharing.py:22 terminal/models/sharing.py:60 +msgid "Verify code" +msgstr "验证码" + +#: terminal/models/sharing.py:27 +msgid "Expired time (min)" +msgstr "过期时间 (分)" + +#: terminal/models/sharing.py:48 +msgid "Link not active" +msgstr "链接失效" + +#: terminal/models/sharing.py:50 +msgid "Link expired" +msgstr "是否过期" + +#: terminal/models/sharing.py:63 +msgid "Session sharing" +msgstr "会话分享" + +#: terminal/models/sharing.py:67 terminal/serializers/sharing.py:49 +msgid "Joiner" +msgstr "" + +#: terminal/models/sharing.py:70 +msgid "Date joined" +msgstr "加入日期" + +#: terminal/models/sharing.py:73 +msgid "Date left" +msgstr "结束日期" + +#: terminal/models/sharing.py:91 xpack/plugins/change_auth_plan/models.py:307 +msgid "Finished" +msgstr "结束" + +#: terminal/models/sharing.py:111 +msgid "Invalid verification code" +msgstr "验证码不正确" + #: terminal/models/status.py:18 msgid "Session Online" msgstr "在线会话" @@ -3755,24 +4102,20 @@ msgid "Open" msgstr "打开" #: tickets/const.py:18 tickets/const.py:25 -#, fuzzy -#| msgid "Approve" msgid "Approved" -msgstr "同意" +msgstr "已同意" #: tickets/const.py:19 tickets/const.py:26 -#, fuzzy -#| msgid "Reject" msgid "Rejected" -msgstr "拒绝" +msgstr "已拒绝" #: tickets/const.py:20 tickets/const.py:31 msgid "Closed" -msgstr "关闭" +msgstr "关闭的" #: tickets/const.py:24 msgid "Notified" -msgstr "" +msgstr "已通知" #: tickets/const.py:37 msgid "Approve" @@ -3780,11 +4123,11 @@ msgstr "同意" #: tickets/const.py:42 msgid "One level" -msgstr "1级审批" +msgstr "1 级" #: tickets/const.py:43 msgid "Two level" -msgstr "2级审批" +msgstr "2 级" #: tickets/const.py:47 msgid "Super admin" @@ -3796,7 +4139,7 @@ msgstr "组织管理员" #: tickets/const.py:49 msgid "Super admin and org admin" -msgstr "超级管理员和组织管理员" +msgstr "组织管理员或超级管理员" #: tickets/const.py:50 msgid "Custom user" @@ -3940,13 +4283,11 @@ msgstr "内容" #: tickets/models/flow.py:19 tickets/models/flow.py:55 #: tickets/models/ticket.py:25 msgid "Approve level" -msgstr "审批等级" +msgstr "审批级别" #: tickets/models/flow.py:24 tickets/serializers/ticket/ticket.py:140 -#, fuzzy -#| msgid "Approved date start" msgid "Approve strategy" -msgstr "批准的开始日期" +msgstr "审批策略" #: tickets/models/flow.py:29 tickets/serializers/ticket/ticket.py:141 msgid "Assignees" @@ -3962,7 +4303,7 @@ msgstr "工单批准信息" #: tickets/models/flow.py:60 msgid "Ticket flow" -msgstr "工单标题" +msgstr "工单流程" #: tickets/models/ticket.py:38 msgid "Ticket assignee" @@ -3978,7 +4319,7 @@ msgstr "状态" #: tickets/models/ticket.py:61 msgid "Approval step" -msgstr "同意" +msgstr "审批步骤" #: tickets/models/ticket.py:66 msgid "Applicant" @@ -3990,13 +4331,11 @@ msgstr "申请人名称" #: tickets/models/ticket.py:69 msgid "Process" -msgstr "处理人" +msgstr "流程" #: tickets/models/ticket.py:74 -#, fuzzy -#| msgid "Tickets" msgid "TicketFlow" -msgstr "工单管理" +msgstr "工单流程" #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:16 #: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:16 @@ -4009,11 +4348,11 @@ msgstr "申请应用" #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:40 msgid "Apply applications display" -msgstr "批准的应用名称" +msgstr "应用名称名称" #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:44 msgid "Apply system users" -msgstr "批准的系统用户" +msgstr "系统用户" #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:49 msgid "Apply system user display" @@ -4099,11 +4438,11 @@ msgstr "提交数据中的类型 (`{}`) 与请求URL地址中的类型 (`{}`) #: tickets/serializers/ticket/ticket.py:115 msgid "The ticket flow `{}` does not exist" -msgstr "组织 `{}` 不存在" +msgstr "工单流程 `{}` 不存在" #: tickets/serializers/ticket/ticket.py:182 msgid "The current organization type already exists" -msgstr "当前组织 ({}) 不能被删除" +msgstr "当前组织已存在该类型" #: tickets/utils.py:37 msgid "New Ticket - {} ({})" @@ -4877,7 +5216,7 @@ msgstr "多因子认证禁用成功" msgid "MFA disable success, return login page" msgstr "多因子认证禁用成功,返回登录页面" -#: users/views/profile/password.py:30 +#: users/views/profile/password.py:32 users/views/profile/password.py:36 msgid "Password invalid" msgstr "用户名或密码无效" @@ -5005,10 +5344,6 @@ msgstr "验证密码/密钥" msgid "Keep auth" msgstr "保存密码/密钥" -#: xpack/plugins/change_auth_plan/models.py:307 -msgid "Finished" -msgstr "结束" - #: xpack/plugins/change_auth_plan/models.py:333 msgid "Step" msgstr "步骤" @@ -5361,10 +5696,6 @@ msgstr "" msgid "Client ID" msgstr "客户端 ID" -#: xpack/plugins/cloud/serializers.py:33 -msgid "Client Secret" -msgstr "客户端密钥" - #: xpack/plugins/cloud/serializers.py:36 msgid "Tenant ID" msgstr "租户 ID" @@ -5374,8 +5705,6 @@ msgid "Subscription ID" msgstr "订阅 ID" #: xpack/plugins/cloud/serializers.py:51 -#, fuzzy -#| msgid "This field is required." msgid "This field is required" msgstr "该字段是必填项。" @@ -5400,11 +5729,6 @@ msgstr "执行次数" msgid "Instance count" msgstr "实例个数" -#: xpack/plugins/cloud/serializers.py:211 -#: xpack/plugins/gathered_user/serializers.py:20 -msgid "Periodic display" -msgstr "定时执行" - #: xpack/plugins/cloud/utils.py:65 msgid "Account unavailable" msgstr "账户无效" @@ -5493,6 +5817,30 @@ msgstr "旗舰版" msgid "Community edition" msgstr "社区版" +#~ msgid "Approval level" +#~ msgstr "同意" + +#~ msgid "Login challenge enabled" +#~ msgstr "登录页面开启CHALLENGE输入框" + +#~ msgid "Login captcha enabled" +#~ msgstr "登录页面开启验证码" + +#~ msgid "Insecure command level" +#~ msgstr "不安全命令等级" + +#~ msgid "Encrypt password" +#~ msgstr "密码加密" + +#~ msgid "Create User" +#~ msgstr "创建用户" + +#~ msgid "Apply attr to use" +#~ msgstr "申请可用属性" + +#~ msgid "User login only in users" +#~ msgstr "仅在用户列表中用户认证" + #~ msgid "Approved applications" #~ msgstr "批准的应用" diff --git a/apps/notifications/ws.py b/apps/notifications/ws.py index 45cbb6d00..4b9d9e4bd 100644 --- a/apps/notifications/ws.py +++ b/apps/notifications/ws.py @@ -4,7 +4,6 @@ from redis.exceptions import ConnectionError from channels.generic.websocket import JsonWebsocketConsumer from common.utils import get_logger -from .models import SiteMessage from .site_msg import SiteMessageUtil from .signals_handler import new_site_msg_chan diff --git a/apps/ops/ansible/inventory.py b/apps/ops/ansible/inventory.py index 707855cc3..df20b06d6 100644 --- a/apps/ops/ansible/inventory.py +++ b/apps/ops/ansible/inventory.py @@ -46,7 +46,7 @@ class BaseHost(Host): if host_data.get('username'): self.set_variable('ansible_user', host_data['username']) - # 添加密码和秘钥 + # 添加密码和密钥 if host_data.get('password'): self.set_variable('ansible_ssh_pass', host_data['password']) if host_data.get('private_key'): diff --git a/apps/ops/mixin.py b/apps/ops/mixin.py index c6f1ce184..e64a763fc 100644 --- a/apps/ops/mixin.py +++ b/apps/ops/mixin.py @@ -159,7 +159,7 @@ class PeriodTaskFormMixin(forms.Form): ) interval = forms.IntegerField( required=False, initial=24, - help_text=_('Tips: (Units: hour)'), label=_("Cycle perform"), + help_text=_('Unit: hour'), label=_("Cycle perform"), ) def get_initial_for_field(self, field, field_name): diff --git a/apps/settings/api/__init__.py b/apps/settings/api/__init__.py index d7cfa4cec..1ef4336e5 100644 --- a/apps/settings/api/__init__.py +++ b/apps/settings/api/__init__.py @@ -1,5 +1,7 @@ -from .common import * +from .settings import * from .ldap import * from .wecom import * from .dingtalk import * from .feishu import * +from .public import * +from .email import * diff --git a/apps/settings/api/common.py b/apps/settings/api/common.py deleted file mode 100644 index 74a76c646..000000000 --- a/apps/settings/api/common.py +++ /dev/null @@ -1,193 +0,0 @@ -# -*- 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 jumpserver.utils import has_valid_xpack_license -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.jpg'), - '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_login_title(): - default_title = _('Welcome to the JumpServer open source Bastion Host') - 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, - "OLD_PASSWORD_HISTORY_LIMIT_COUNT": settings.OLD_PASSWORD_HISTORY_LIMIT_COUNT, - "SECURITY_COMMAND_EXECUTION": settings.SECURITY_COMMAND_EXECUTION, - "SECURITY_PASSWORD_EXPIRATION_TIME": settings.SECURITY_PASSWORD_EXPIRATION_TIME, - "SECURITY_LUNA_REMEMBER_AUTH": settings.SECURITY_LUNA_REMEMBER_AUTH, - "XPACK_LICENSE_IS_VALID": has_valid_xpack_license(), - "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_ADMIN_USER_PASSWORD_MIN_LENGTH': settings.SECURITY_ADMIN_USER_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, - }, - "AUTH_WECOM": settings.AUTH_WECOM, - "AUTH_DINGTALK": settings.AUTH_DINGTALK, - "AUTH_FEISHU": settings.AUTH_FEISHU, - 'SECURITY_WATERMARK_ENABLED': settings.SECURITY_WATERMARK_ENABLED, - 'SECURITY_SESSION_SHARE': settings.SECURITY_SESSION_SHARE - } - } - 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, - 'wecom': serializers.WeComSettingSerializer, - 'dingtalk': serializers.DingTalkSettingSerializer, - 'feishu': serializers.FeiShuSettingSerializer, - } - - 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() - obj = {item: getattr(settings, item) for item in items} - return obj - - 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 - if encrypted and value in ['', None]: - continue - 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) diff --git a/apps/settings/api/email.py b/apps/settings/api/email.py new file mode 100644 index 000000000..91163213a --- /dev/null +++ b/apps/settings/api/email.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# + +from smtplib import SMTPSenderRefused +from rest_framework.views import Response, APIView +from django.core.mail import send_mail, get_connection +from django.utils.translation import ugettext_lazy as _ + +from common.permissions import IsSuperUser +from common.utils import get_logger +from .. import serializers + +logger = get_logger(__file__) + +__all__ = ['MailTestingAPI'] + + +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)}) \ No newline at end of file diff --git a/apps/settings/api/public.py b/apps/settings/api/public.py new file mode 100644 index 000000000..15907f599 --- /dev/null +++ b/apps/settings/api/public.py @@ -0,0 +1,79 @@ +from rest_framework import generics +from rest_framework.permissions import AllowAny +from django.conf import settings +from django.utils.translation import ugettext_lazy as _ +from django.templatetags.static import static + +from jumpserver.utils import has_valid_xpack_license +from common.utils import get_logger +from .. import serializers + +logger = get_logger(__name__) + +__all__ = ['PublicSettingApi'] + + +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.jpg'), + '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_login_title(): + default_title = _('Welcome to the JumpServer open source Bastion Host') + 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, + "OLD_PASSWORD_HISTORY_LIMIT_COUNT": settings.OLD_PASSWORD_HISTORY_LIMIT_COUNT, + "SECURITY_COMMAND_EXECUTION": settings.SECURITY_COMMAND_EXECUTION, + "SECURITY_PASSWORD_EXPIRATION_TIME": settings.SECURITY_PASSWORD_EXPIRATION_TIME, + "SECURITY_LUNA_REMEMBER_AUTH": settings.SECURITY_LUNA_REMEMBER_AUTH, + "XPACK_LICENSE_IS_VALID": has_valid_xpack_license(), + "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_ADMIN_USER_PASSWORD_MIN_LENGTH': settings.SECURITY_ADMIN_USER_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, + }, + "AUTH_WECOM": settings.AUTH_WECOM, + "AUTH_DINGTALK": settings.AUTH_DINGTALK, + "AUTH_FEISHU": settings.AUTH_FEISHU, + 'SECURITY_WATERMARK_ENABLED': settings.SECURITY_WATERMARK_ENABLED, + 'SECURITY_SESSION_SHARE': settings.SECURITY_SESSION_SHARE, + } + } + return instance diff --git a/apps/settings/api/settings.py b/apps/settings/api/settings.py new file mode 100644 index 000000000..db7fdef4c --- /dev/null +++ b/apps/settings/api/settings.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +# + +from rest_framework import generics +from django.conf import settings + +from jumpserver.conf import Config +from common.permissions import IsSuperUser +from common.utils import get_logger +from .. import serializers +from ..models import Setting + +logger = get_logger(__file__) + + +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, + 'wecom': serializers.WeComSettingSerializer, + 'dingtalk': serializers.DingTalkSettingSerializer, + 'feishu': serializers.FeiShuSettingSerializer, + 'auth': serializers.AuthSettingSerializer, + 'oidc': serializers.OIDCSettingSerializer, + 'keycloak': serializers.KeycloakSettingSerializer, + 'radius': serializers.RadiusSettingSerializer, + 'cas': serializers.CASSettingSerializer, + 'sso': serializers.SSOSettingSerializer, + 'clean': serializers.CleaningSerializer, + 'other': serializers.OtherSettingSerializer, + } + + def get_serializer_class(self): + category = self.request.query_params.get('category', 'basic') + default = serializers.BasicSettingSerializer + cls = self.serializer_class_mapper.get(category, default) + return cls + + def get_fields(self): + serializer = self.get_serializer_class()() + fields = serializer.get_fields() + return fields + + def get_object(self): + items = self.get_fields().keys() + obj = {} + for item in items: + if hasattr(settings, item): + obj[item] = getattr(settings, item) + else: + obj[item] = Config.defaults[item] + return obj + + 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 + if encrypted and value in ['', None]: + continue + 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) + if hasattr(serializer, 'post_save'): + serializer.post_save() diff --git a/apps/settings/migrations/0003_auto_20210901_1035.py b/apps/settings/migrations/0003_auto_20210901_1035.py new file mode 100644 index 000000000..c6f37625a --- /dev/null +++ b/apps/settings/migrations/0003_auto_20210901_1035.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.12 on 2021-09-01 02:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('settings', '0002_auto_20210729_1546'), + ] + + operations = [ + migrations.AlterField( + model_name='setting', + name='value', + field=models.TextField(blank=True, null=True, verbose_name='Value'), + ), + ] diff --git a/apps/settings/models.py b/apps/settings/models.py index 0a986efcb..1660e318d 100644 --- a/apps/settings/models.py +++ b/apps/settings/models.py @@ -27,7 +27,7 @@ class SettingManager(models.Manager): class Setting(models.Model): name = models.CharField(max_length=128, unique=True, verbose_name=_("Name")) - value = models.TextField(verbose_name=_("Value")) + value = models.TextField(verbose_name=_("Value"), null=True, blank=True) category = models.CharField(max_length=128, default="default") encrypted = models.BooleanField(default=False) enabled = models.BooleanField(verbose_name=_("Enabled"), default=True) diff --git a/apps/settings/serializers/__init__.py b/apps/settings/serializers/__init__.py index 5868a76df..0a55f645d 100644 --- a/apps/settings/serializers/__init__.py +++ b/apps/settings/serializers/__init__.py @@ -1,7 +1,13 @@ # coding: utf-8 # +from .basic import * +from .auth import * from .email import * -from .ldap import * from .public import * from .settings import * +from .security import * +from .terminal import * +from .cleaning import * +from .other import * + diff --git a/apps/settings/serializers/auth/__init__.py b/apps/settings/serializers/auth/__init__.py new file mode 100644 index 000000000..e8040d316 --- /dev/null +++ b/apps/settings/serializers/auth/__init__.py @@ -0,0 +1,9 @@ +from .cas import * +from .ldap import * +from .oidc import * +from .radius import * +from .dingtalk import * +from .feishu import * +from .wecom import * +from .sso import * +from .base import * diff --git a/apps/settings/serializers/auth/base.py b/apps/settings/serializers/auth/base.py new file mode 100644 index 000000000..d98816108 --- /dev/null +++ b/apps/settings/serializers/auth/base.py @@ -0,0 +1,25 @@ +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +__all__ = [ + 'AuthSettingSerializer', +] + + +class AuthSettingSerializer(serializers.Serializer): + AUTH_CAS = serializers.BooleanField(required=False, label=_('CAS Auth')) + AUTH_OPENID = serializers.BooleanField(required=False, label=_('OPENID Auth')) + AUTH_RADIUS = serializers.BooleanField(required=False, label=_('RADIUS Auth')) + AUTH_DINGTALK = serializers.BooleanField(default=False, label=_('DingTalk Auth')) + AUTH_FEISHU = serializers.BooleanField(default=False, label=_('FeiShu Auth')) + AUTH_WECOM = serializers.BooleanField(default=False, label=_('WeCom Auth')) + AUTH_SSO = serializers.BooleanField(default=False, label=_("SSO Auth")) + FORGOT_PASSWORD_URL = serializers.CharField( + required=False, max_length=1024, label=_("Forgot password url") + ) + HEALTH_CHECK_TOKEN = serializers.CharField( + required=False, max_length=1024, label=_("Health check token") + ) + LOGIN_REDIRECT_MSG_ENABLED = serializers.BooleanField( + required=False, label=_("Enable login redirect msg") + ) diff --git a/apps/settings/serializers/auth/cas.py b/apps/settings/serializers/auth/cas.py new file mode 100644 index 000000000..49a02505f --- /dev/null +++ b/apps/settings/serializers/auth/cas.py @@ -0,0 +1,18 @@ + +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +__all__ = [ + 'CASSettingSerializer', +] + + +class CASSettingSerializer(serializers.Serializer): + AUTH_CAS = serializers.BooleanField(required=False, label=_('Enable CAS Auth')) + CAS_SERVER_URL = serializers.CharField(required=False, max_length=1024, label=_('Server url')) + CAS_LOGOUT_COMPLETELY = serializers.BooleanField(required=False, label=_('Logout completely')) + CAS_VERSION = serializers.IntegerField(required=False, label=_('Version')) + CAS_USERNAME_ATTRIBUTE = serializers.CharField(required=False, max_length=1024, label=_('Username attr')) + CAS_APPLY_ATTRIBUTES_TO_USER = serializers.BooleanField(required=False, label=_('Enable attributes map')) + CAS_RENAME_ATTRIBUTES = serializers.DictField(required=False, label=_('Rename attr')) + CAS_CREATE_USER = serializers.BooleanField(required=False, label=_('Create user if not')) diff --git a/apps/settings/serializers/auth/dingtalk.py b/apps/settings/serializers/auth/dingtalk.py new file mode 100644 index 000000000..062f19f26 --- /dev/null +++ b/apps/settings/serializers/auth/dingtalk.py @@ -0,0 +1,11 @@ +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +__all__ = ['DingTalkSettingSerializer'] + + +class DingTalkSettingSerializer(serializers.Serializer): + DINGTALK_AGENTID = serializers.CharField(max_length=256, required=True, label='AgentId') + DINGTALK_APPKEY = serializers.CharField(max_length=256, required=True, label='AppKey') + DINGTALK_APPSECRET = serializers.CharField(max_length=256, required=False, label='AppSecret', write_only=True) + AUTH_DINGTALK = serializers.BooleanField(default=False, label=_('Enable DingTalk Auth')) diff --git a/apps/settings/serializers/auth/feishu.py b/apps/settings/serializers/auth/feishu.py new file mode 100644 index 000000000..68b7ee2b1 --- /dev/null +++ b/apps/settings/serializers/auth/feishu.py @@ -0,0 +1,11 @@ +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +__all__ = ['FeiShuSettingSerializer'] + + +class FeiShuSettingSerializer(serializers.Serializer): + FEISHU_APP_ID = serializers.CharField(max_length=256, required=True, label='App ID') + FEISHU_APP_SECRET = serializers.CharField(max_length=256, required=False, label='App Secret', write_only=True) + AUTH_FEISHU = serializers.BooleanField(default=False, label=_('Enable FeiShu Auth')) + diff --git a/apps/settings/serializers/auth/ldap.py b/apps/settings/serializers/auth/ldap.py new file mode 100644 index 000000000..d4eba4089 --- /dev/null +++ b/apps/settings/serializers/auth/ldap.py @@ -0,0 +1,74 @@ + +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +__all__ = [ + 'LDAPTestConfigSerializer', 'LDAPUserSerializer', 'LDAPTestLoginSerializer', + 'LDAPSettingSerializer', +] + + +class LDAPTestConfigSerializer(serializers.Serializer): + AUTH_LDAP_SERVER_URI = serializers.CharField(max_length=1024) + AUTH_LDAP_BIND_DN = serializers.CharField(max_length=1024, required=False, allow_blank=True) + AUTH_LDAP_BIND_PASSWORD = serializers.CharField(required=False, allow_blank=True) + AUTH_LDAP_SEARCH_OU = serializers.CharField() + AUTH_LDAP_SEARCH_FILTER = serializers.CharField() + AUTH_LDAP_USER_ATTR_MAP = serializers.CharField() + AUTH_LDAP_START_TLS = serializers.BooleanField(required=False) + AUTH_LDAP = serializers.BooleanField(required=False) + + +class LDAPTestLoginSerializer(serializers.Serializer): + username = serializers.CharField(max_length=1024, required=True) + password = serializers.CharField(max_length=2014, required=True) + + +class LDAPUserSerializer(serializers.Serializer): + id = serializers.CharField() + username = serializers.CharField() + name = serializers.CharField() + email = serializers.CharField() + existing = serializers.BooleanField(read_only=True) + + +class LDAPSettingSerializer(serializers.Serializer): + # encrypt_fields 现在使用 write_only 来判断了 + + 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_SYNC_IS_PERIODIC = serializers.BooleanField(required=False, label=_('Periodic display')) + AUTH_LDAP_SYNC_INTERVAL = serializers.CharField( + required=False, max_length=1024, allow_null=True, + label=_('Interval'), help_text=_('Unit: hour') + ) + AUTH_LDAP_SYNC_CRONTAB = serializers.CharField( + required=False, max_length=1024, allow_null=True, label=_('Regularly perform') + ) + AUTH_LDAP_CONNECT_TIMEOUT = serializers.IntegerField(required=False, label=_('Connect timeout')) + AUTH_LDAP_SEARCH_PAGED_SIZE = serializers.IntegerField(required=False, label=_('Search paged size')) + + AUTH_LDAP = serializers.BooleanField(required=False, label=_('Enable LDAP auth')) + + @staticmethod + def post_save(): + from users.tasks import import_ldap_user_periodic + import_ldap_user_periodic() diff --git a/apps/settings/serializers/auth/oidc.py b/apps/settings/serializers/auth/oidc.py new file mode 100644 index 000000000..21f0f989e --- /dev/null +++ b/apps/settings/serializers/auth/oidc.py @@ -0,0 +1,78 @@ +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +__all__ = [ + 'OIDCSettingSerializer', 'KeycloakSettingSerializer', +] + + +class CommonSettingSerializer(serializers.Serializer): + # OpenID 公有配置参数 (version <= 1.5.8 或 version >= 1.5.8) + BASE_SITE_URL = serializers.CharField( + required=False, allow_null=True, max_length=1024, label=_('Base site url') + ) + AUTH_OPENID_CLIENT_ID = serializers.CharField( + required=False, max_length=1024, label=_('Client Id') + ) + AUTH_OPENID_CLIENT_SECRET = serializers.CharField( + required=False, max_length=1024, write_only=True, label=_('Client Secret') + ) + AUTH_OPENID_SHARE_SESSION = serializers.BooleanField(required=False, label=_('Share session')) + AUTH_OPENID_IGNORE_SSL_VERIFICATION = serializers.BooleanField( + required=False, label=_('Ignore ssl verification') + ) + + +class KeycloakSettingSerializer(CommonSettingSerializer): + # OpenID 旧配置参数 (version <= 1.5.8 (discarded)) + AUTH_OPENID_KEYCLOAK = serializers.BooleanField( + label=_("Use Keycloak"), required=False, default=False + ) + AUTH_OPENID_SERVER_URL = serializers.CharField( + required=False, max_length=1024, label=_('Server url') + ) + AUTH_OPENID_REALM_NAME = serializers.CharField( + required=False, max_length=1024, allow_null=True, label=_('Realm name') + ) + + +class OIDCSettingSerializer(KeycloakSettingSerializer): + # OpenID 新配置参数 (version >= 1.5.9) + AUTH_OPENID = serializers.BooleanField(required=False, label=_('Enable OPENID Auth')) + AUTH_OPENID_PROVIDER_ENDPOINT = serializers.CharField( + required=False, max_length=1024, label=_('Provider endpoint') + ) + AUTH_OPENID_PROVIDER_AUTHORIZATION_ENDPOINT = serializers.CharField( + required=False, max_length=1024, label=_('Provider auth endpoint') + ) + AUTH_OPENID_PROVIDER_TOKEN_ENDPOINT = serializers.CharField( + required=False, max_length=1024, label=_('Provider token endpoint') + ) + AUTH_OPENID_PROVIDER_JWKS_ENDPOINT = serializers.CharField( + required=False, max_length=1024, label=_('Provider jwks endpoint') + ) + AUTH_OPENID_PROVIDER_USERINFO_ENDPOINT = serializers.CharField( + required=False, max_length=1024, label=_('Provider userinfo endpoint') + ) + AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT = serializers.CharField( + required=False, max_length=1024, label=_('Provider end session endpoint') + ) + AUTH_OPENID_PROVIDER_SIGNATURE_ALG = serializers.CharField( + required=False, max_length=1024, label=_('Provider sign alg') + ) + AUTH_OPENID_PROVIDER_SIGNATURE_KEY = serializers.CharField( + required=False, max_length=1024, allow_null=True, label=_('Provider sign key') + ) + AUTH_OPENID_SCOPES = serializers.CharField(required=False, max_length=1024, label=_('Scopes')) + AUTH_OPENID_ID_TOKEN_MAX_AGE = serializers.IntegerField( + required=False, label=_('Id token max age') + ) + AUTH_OPENID_ID_TOKEN_INCLUDE_CLAIMS = serializers.BooleanField( + required=False, label=_('Id token include claims') + ) + AUTH_OPENID_USE_STATE = serializers.BooleanField(required=False, label=_('Use state')) + AUTH_OPENID_USE_NONCE = serializers.BooleanField(required=False, label=_('Use nonce')) + AUTH_OPENID_ALWAYS_UPDATE_USER = serializers.BooleanField( + required=False, label=_('Always update user') + ) + diff --git a/apps/settings/serializers/auth/radius.py b/apps/settings/serializers/auth/radius.py new file mode 100644 index 000000000..0a956d18a --- /dev/null +++ b/apps/settings/serializers/auth/radius.py @@ -0,0 +1,19 @@ +# coding: utf-8 +# + +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +__all__ = [ + 'RadiusSettingSerializer', +] + + +class RadiusSettingSerializer(serializers.Serializer): + AUTH_RADIUS = serializers.BooleanField(required=False, label=_('Enable RADIUS Auth')) + RADIUS_SERVER = serializers.CharField(required=False, max_length=1024, label=_('Host')) + RADIUS_PORT = serializers.IntegerField(required=False, label=_('Port')) + RADIUS_SECRET = serializers.CharField( + required=False, max_length=1024, allow_null=True, label=_('Secret'), write_only=True + ) + OTP_IN_RADIUS = serializers.BooleanField(required=False, label=_('OTP in radius')) diff --git a/apps/settings/serializers/auth/sso.py b/apps/settings/serializers/auth/sso.py new file mode 100644 index 000000000..38481cf2a --- /dev/null +++ b/apps/settings/serializers/auth/sso.py @@ -0,0 +1,17 @@ + +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +__all__ = [ + 'SSOSettingSerializer', +] + + +class SSOSettingSerializer(serializers.Serializer): + AUTH_SSO = serializers.BooleanField( + required=False, label=_('Enable SSO auth'), + help_text=_("Other service can using SSO token login to JumpServer without password") + ) + AUTH_SSO_AUTHKEY_TTL = serializers.IntegerField( + required=False, label=_('SSO auth key TTL'), help_text=_("Unit: second") + ) diff --git a/apps/settings/serializers/auth/wecom.py b/apps/settings/serializers/auth/wecom.py new file mode 100644 index 000000000..ceb83aa85 --- /dev/null +++ b/apps/settings/serializers/auth/wecom.py @@ -0,0 +1,11 @@ +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +__all__ = ['WeComSettingSerializer'] + + +class WeComSettingSerializer(serializers.Serializer): + WECOM_CORPID = serializers.CharField(max_length=256, required=True, label='corpid') + WECOM_AGENTID = serializers.CharField(max_length=256, required=True, label='agentid') + WECOM_SECRET = serializers.CharField(max_length=256, required=False, label='secret', write_only=True) + AUTH_WECOM = serializers.BooleanField(default=False, label=_('Enable WeCom Auth')) \ No newline at end of file diff --git a/apps/settings/serializers/basic.py b/apps/settings/serializers/basic.py new file mode 100644 index 000000000..82b7f83dd --- /dev/null +++ b/apps/settings/serializers/basic.py @@ -0,0 +1,22 @@ +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + + +class BasicSettingSerializer(serializers.Serializer): + SITE_URL = serializers.URLField( + required=True, label=_("Site url"), + help_text=_('eg: http://dev.jumpserver.org:8080') + ) + USER_GUIDE_URL = serializers.URLField( + required=False, allow_blank=True, allow_null=True, label=_("User guide url"), + help_text=_('User first login update profile done redirect to it') + ) + FORGOT_PASSWORD_URL = serializers.URLField( + required=False, allow_blank=True, allow_null=True, label=_("Forgot password url"), + help_text=_('The forgot password url on login page, If you use ' + 'ldap or cas external authentication, you can set it') + ) + GLOBAL_ORG_DISPLAY_NAME = serializers.CharField( + required=False, max_length=1024, allow_blank=True, allow_null=True, label=_("Global organization name"), + help_text=_('The name of global organization to display') + ) diff --git a/apps/settings/serializers/cleaning.py b/apps/settings/serializers/cleaning.py new file mode 100644 index 000000000..182a97eaa --- /dev/null +++ b/apps/settings/serializers/cleaning.py @@ -0,0 +1,22 @@ +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +__all__ = ['CleaningSerializer'] + + +class CleaningSerializer(serializers.Serializer): + LOGIN_LOG_KEEP_DAYS = serializers.IntegerField( + label=_("Login log keep days"), help_text=_("Unit: day") + ) + TASK_LOG_KEEP_DAYS = serializers.IntegerField( + label=_("Task log keep days"), help_text=_("Unit: day") + ) + OPERATE_LOG_KEEP_DAYS = serializers.IntegerField( + label=_("Operate log keep days"), help_text=_("Unit: day") + ) + FTP_LOG_KEEP_DAYS = serializers.IntegerField( + label=_("FTP log keep days"), help_text=_("Unit: day") + ) + CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS = serializers.IntegerField( + label=_("Cloud sync record keep days"), help_text=_("Unit: day") + ) diff --git a/apps/settings/serializers/email.py b/apps/settings/serializers/email.py index 6d033f6aa..2ad7f84e6 100644 --- a/apps/settings/serializers/email.py +++ b/apps/settings/serializers/email.py @@ -1,9 +1,10 @@ # coding: utf-8 # +from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers -__all__ = ['MailTestSerializer'] +__all__ = ['MailTestSerializer', 'EmailSettingSerializer', 'EmailContentSettingSerializer'] class MailTestSerializer(serializers.Serializer): @@ -15,3 +16,56 @@ class MailTestSerializer(serializers.Serializer): EMAIL_RECIPIENT = serializers.CharField(required=False, allow_blank=True) EMAIL_USE_SSL = serializers.BooleanField(default=False) EMAIL_USE_TLS = serializers.BooleanField(default=False) + + +class EmailSettingSerializer(serializers.Serializer): + # encrypt_fields 现在使用 write_only 来判断了 + + 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, + 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)') + ) diff --git a/apps/settings/serializers/ldap.py b/apps/settings/serializers/ldap.py deleted file mode 100644 index 1ccc02c26..000000000 --- a/apps/settings/serializers/ldap.py +++ /dev/null @@ -1,34 +0,0 @@ -# coding: utf-8 -# - -from django.utils.translation import ugettext_lazy as _ -from rest_framework import serializers - -__all__ = [ - 'LDAPTestConfigSerializer', 'LDAPUserSerializer', 'LDAPTestLoginSerializer' -] - - -class LDAPTestConfigSerializer(serializers.Serializer): - AUTH_LDAP_SERVER_URI = serializers.CharField(max_length=1024) - AUTH_LDAP_BIND_DN = serializers.CharField(max_length=1024, required=False, allow_blank=True) - AUTH_LDAP_BIND_PASSWORD = serializers.CharField(required=False, allow_blank=True) - AUTH_LDAP_SEARCH_OU = serializers.CharField() - AUTH_LDAP_SEARCH_FILTER = serializers.CharField() - AUTH_LDAP_USER_ATTR_MAP = serializers.CharField() - AUTH_LDAP_START_TLS = serializers.BooleanField(required=False) - AUTH_LDAP = serializers.BooleanField(required=False) - - -class LDAPTestLoginSerializer(serializers.Serializer): - username = serializers.CharField(max_length=1024, required=True) - password = serializers.CharField(max_length=2014, required=True) - - -class LDAPUserSerializer(serializers.Serializer): - id = serializers.CharField() - username = serializers.CharField() - name = serializers.CharField() - email = serializers.CharField() - existing = serializers.BooleanField(read_only=True) - diff --git a/apps/settings/serializers/other.py b/apps/settings/serializers/other.py new file mode 100644 index 000000000..f8ebaeceb --- /dev/null +++ b/apps/settings/serializers/other.py @@ -0,0 +1,28 @@ +from abc import ABCMeta + +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + + +class OtherSettingSerializer(serializers.Serializer): + EMAIL_SUFFIX = serializers.CharField( + required=False, max_length=1024, label=_("Email suffix"), + help_text=_('This is used by default if no email is returned during SSO authentication') + ) + TICKETS_ENABLED = serializers.BooleanField(required=False, default=True, label=_("Enable tickets")) + + OTP_ISSUER_NAME = serializers.CharField( + required=False, max_length=1024, label=_('OTP issuer name'), + ) + OTP_VALID_WINDOW = serializers.IntegerField(label=_("OTP valid window")) + + PERIOD_TASK_ENABLED = serializers.BooleanField(required=False, label=_("Enable period task")) + WINDOWS_SSH_DEFAULT_SHELL = serializers.CharField( + required=False, max_length=1024, label=_('Ansible windows default shell') + ) + + PERM_SINGLE_ASSET_TO_UNGROUP_NODE = serializers.BooleanField( + required=False, label=_("Perm single to ungroup node") + ) + + diff --git a/apps/settings/serializers/security.py b/apps/settings/serializers/security.py new file mode 100644 index 000000000..5749a5c8f --- /dev/null +++ b/apps/settings/serializers/security.py @@ -0,0 +1,115 @@ +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + + +class SecurityPasswordRuleSerializer(serializers.Serializer): + SECURITY_PASSWORD_MIN_LENGTH = serializers.IntegerField( + min_value=6, max_value=30, required=True, + label=_('Password minimum length') + ) + SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH = serializers.IntegerField( + min_value=6, max_value=30, required=True, + label=_('Admin user 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')) + + +class SecurityAuthSerializer(serializers.Serializer): + SECURITY_MFA_AUTH = serializers.ChoiceField( + choices=( + [0, _('Disable')], + [1, _('All users')], + [2, _('Only admin users')], + ), + required=False, label=_("Global MFA auth") + ) + 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=_( + '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_PASSWORD_EXPIRATION_TIME = serializers.IntegerField( + min_value=1, max_value=99999, required=True, + label=_('User password expiration'), + help_text=_( + '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' + ) + ) + OLD_PASSWORD_HISTORY_LIMIT_COUNT = serializers.IntegerField( + min_value=0, max_value=99999, required=True, + label=_('Number of repeated historical passwords'), + help_text=_( + 'Tip: When the user resets the password, it cannot be ' + 'the previous n historical passwords of the user' + ) + ) + USER_LOGIN_SINGLE_MACHINE_ENABLED = serializers.BooleanField( + required=False, default=False, label=_("Only single device login"), + help_text=_("Next device login, pre login will be logout") + ) + ONLY_ALLOW_EXIST_USER_AUTH = serializers.BooleanField( + required=False, default=False, label=_("Only exist user login"), + help_text=_("If enable, CAS、OIDC auth will be failed, if user not exist yet") + ) + 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") + ) + SECURITY_MFA_VERIFY_TTL = serializers.IntegerField(label=_("MFA verify TTL"), help_text=_("Unit: second")) + SECURITY_LOGIN_CAPTCHA_ENABLED = serializers.BooleanField( + required=False, default=True, + label=_("Enable Login captcha") + ) + + +class SecuritySettingSerializer(SecurityPasswordRuleSerializer, SecurityAuthSerializer): + 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_WATERMARK_ENABLED = serializers.BooleanField( + required=True, label=_('Replay watermark'), + help_text=_('Enabled, the session replay contains watermark information') + ) + 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_LUNA_REMEMBER_AUTH = serializers.BooleanField( + label=_("Remember manual auth") + ) + CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED = serializers.BooleanField( + label=_("Enable change auth secure mode") + ) + 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') + ) + SECURITY_COMMAND_EXECUTION = serializers.BooleanField( + required=False, label=_('Batch command execution'), + help_text=_('Allow user run batch command or not using ansible') + ) + SECURITY_SESSION_SHARE = serializers.BooleanField( + required=True, label=_('Session share'), + help_text=_("Enabled, Allows user active session to be shared with other users") + ) + + diff --git a/apps/settings/serializers/settings.py b/apps/settings/serializers/settings.py index fbdb75f2c..edd8a30a8 100644 --- a/apps/settings/serializers/settings.py +++ b/apps/settings/serializers/settings.py @@ -1,250 +1,38 @@ # coding: utf-8 -from django.utils.translation import ugettext_lazy as _ -from rest_framework import serializers +from .basic import BasicSettingSerializer +from .other import OtherSettingSerializer +from .email import EmailSettingSerializer, EmailContentSettingSerializer +from .auth import ( + LDAPSettingSerializer, OIDCSettingSerializer, KeycloakSettingSerializer, + CASSettingSerializer, RadiusSettingSerializer, FeiShuSettingSerializer, + WeComSettingSerializer, DingTalkSettingSerializer +) +from .terminal import TerminalSettingSerializer +from .security import SecuritySettingSerializer +from .cleaning import CleaningSerializer __all__ = [ - 'BasicSettingSerializer', 'EmailSettingSerializer', 'EmailContentSettingSerializer', - 'LDAPSettingSerializer', 'TerminalSettingSerializer', 'SecuritySettingSerializer', - 'SettingsSerializer', 'WeComSettingSerializer', 'DingTalkSettingSerializer', - 'FeiShuSettingSerializer', + 'SettingsSerializer', ] -class BasicSettingSerializer(serializers.Serializer): - SITE_URL = serializers.URLField( - required=True, label=_("Site url"), - help_text=_('eg: http://dev.jumpserver.org:8080') - ) - - USER_GUIDE_URL = serializers.URLField( - required=False, allow_blank=True, allow_null=True, label=_("User guide url"), - help_text=_('User first login update profile done redirect to it') - ) - FORGOT_PASSWORD_URL = serializers.URLField( - required=False, allow_blank=True, allow_null=True, label=_("Forgot password url"), - help_text=_('The forgot password url on login page, If you use ' - 'ldap or cas external authentication, you can set it') - ) - GLOBAL_ORG_DISPLAY_NAME = serializers.CharField( - required=False, max_length=1024, allow_blank=True, allow_null=True, label=_("Global organization name"), - help_text=_('The name of global organization to display') - ) - - -class EmailSettingSerializer(serializers.Serializer): - # encrypt_fields 现在使用 write_only 来判断了 - - 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, - 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 现在使用 write_only 来判断了 - - 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): - SORT_BY_CHOICES = ( - ('hostname', _('Hostname')), - ('ip', _('IP')) - ) - - PAGE_SIZE_CHOICES = ( - ('all', _('All')), - ('auto', _('Auto')), - ('10', '10'), - ('15', '15'), - ('25', '25'), - ('50', '50'), - ) - TERMINAL_PASSWORD_AUTH = serializers.BooleanField(required=False, label=_('Password auth')) - TERMINAL_PUBLIC_KEY_AUTH = serializers.BooleanField( - required=False, label=_('Public key auth'), - help_text=_('Tips: If use other auth method, like AD/LDAP, you should disable this to ' - 'avoid being able to log in after deleting') - ) - 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')) - TERMINAL_RDP_ADDR = serializers.CharField( - required=False, label=_("RDP address"), - max_length=1024, - allow_blank=True, - help_text=_('RDP visit address, eg: dev.jumpserver.org:3389') - ) - - -class SecuritySettingSerializer(serializers.Serializer): - SECURITY_MFA_AUTH = serializers.ChoiceField( - choices=( - [0, _('Disable')], - [1, _('All users')], - [2, _('Only admin users')], - ), - required=False, label=_("Global MFA auth") - ) - 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_WATERMARK_ENABLED = serializers.BooleanField( - required=True, label=_('Replay watermark'), - help_text=_('Enabled, the session replay contains watermark information') - ) - SECURITY_SESSION_SHARE = serializers.BooleanField( - required=True, label=_('Session share'), - help_text=_("Enabled, Allows user active session to be shared with other users") - ) - 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') - ) - OLD_PASSWORD_HISTORY_LIMIT_COUNT = serializers.IntegerField( - min_value=0, max_value=99999, required=True, - label=_('Number of repeated historical passwords'), - help_text=_('Tip: When the user resets the password, it cannot be the previous n historical passwords of the user') - ) - SECURITY_PASSWORD_MIN_LENGTH = serializers.IntegerField( - min_value=6, max_value=30, required=True, - label=_('Password minimum length') - ) - SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH = serializers.IntegerField( - min_value=6, max_value=30, required=True, - label=_('Admin user 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 WeComSettingSerializer(serializers.Serializer): - WECOM_CORPID = serializers.CharField(max_length=256, required=True, label='corpid') - WECOM_AGENTID = serializers.CharField(max_length=256, required=True, label='agentid') - WECOM_SECRET = serializers.CharField(max_length=256, required=False, label='secret', write_only=True) - AUTH_WECOM = serializers.BooleanField(default=False, label=_('Enable WeCom Auth')) - - -class DingTalkSettingSerializer(serializers.Serializer): - DINGTALK_AGENTID = serializers.CharField(max_length=256, required=True, label='AgentId') - DINGTALK_APPKEY = serializers.CharField(max_length=256, required=True, label='AppKey') - DINGTALK_APPSECRET = serializers.CharField(max_length=256, required=False, label='AppSecret', write_only=True) - AUTH_DINGTALK = serializers.BooleanField(default=False, label=_('Enable DingTalk Auth')) - - -class FeiShuSettingSerializer(serializers.Serializer): - FEISHU_APP_ID = serializers.CharField(max_length=256, required=True, label='App ID') - FEISHU_APP_SECRET = serializers.CharField(max_length=256, required=False, label='App Secret', write_only=True) - AUTH_FEISHU = serializers.BooleanField(default=False, label=_('Enable FeiShu Auth')) - - class SettingsSerializer( BasicSettingSerializer, - EmailSettingSerializer, - EmailContentSettingSerializer, LDAPSettingSerializer, TerminalSettingSerializer, SecuritySettingSerializer, WeComSettingSerializer, DingTalkSettingSerializer, FeiShuSettingSerializer, + EmailSettingSerializer, + EmailContentSettingSerializer, + OtherSettingSerializer, + OIDCSettingSerializer, + KeycloakSettingSerializer, + CASSettingSerializer, + RadiusSettingSerializer, + CleaningSerializer ): - # encrypt_fields 现在使用 write_only 来判断了 pass - diff --git a/apps/settings/serializers/terminal.py b/apps/settings/serializers/terminal.py new file mode 100644 index 000000000..215f21c37 --- /dev/null +++ b/apps/settings/serializers/terminal.py @@ -0,0 +1,42 @@ +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + + +class TerminalSettingSerializer(serializers.Serializer): + SORT_BY_CHOICES = ( + ('hostname', _('Hostname')), + ('ip', _('IP')) + ) + + PAGE_SIZE_CHOICES = ( + ('all', _('All')), + ('auto', _('Auto')), + ('10', '10'), + ('15', '15'), + ('25', '25'), + ('50', '50'), + ) + TERMINAL_PASSWORD_AUTH = serializers.BooleanField(required=False, label=_('Password auth')) + TERMINAL_PUBLIC_KEY_AUTH = serializers.BooleanField( + required=False, label=_('Public key auth'), + help_text=_('Tips: If use other auth method, like AD/LDAP, you should disable this to ' + 'avoid being able to log in after deleting') + ) + 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=_('Unit: 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'), + help_text=_("The login success message varies with devices. " + "if you cannot log in to the device through Telnet, set this parameter") + ) + TERMINAL_RDP_ADDR = serializers.CharField( + required=False, label=_("RDP address"), + max_length=1024, + allow_blank=True, + help_text=_('RDP visit address, eg: dev.jumpserver.org:3389') + ) diff --git a/apps/users/tasks.py b/apps/users/tasks.py index fc938499b..dfe67d586 100644 --- a/apps/users/tasks.py +++ b/apps/users/tasks.py @@ -92,8 +92,8 @@ def import_ldap_user(): def import_ldap_user_periodic(): if not settings.AUTH_LDAP: return + task_name = 'import_ldap_user_periodic' if not settings.AUTH_LDAP_SYNC_IS_PERIODIC: - task_name = sys._getframe().f_code.co_name disable_celery_periodic_task(task_name) return @@ -104,7 +104,7 @@ def import_ldap_user_periodic(): interval = None crontab = settings.AUTH_LDAP_SYNC_CRONTAB tasks = { - 'import_ldap_user_periodic': { + task_name: { 'task': import_ldap_user.name, 'interval': interval, 'crontab': crontab, diff --git a/config_example.yml b/config_example.yml index d2124df2e..dd509c0aa 100644 --- a/config_example.yml +++ b/config_example.yml @@ -1,5 +1,5 @@ # SECURITY WARNING: keep the secret key used in production secret! -# 加密秘钥 生产环境中请修改为随机字符串,请勿外泄, 可使用命令生成 +# 加密密钥 生产环境中请修改为随机字符串,请勿外泄, 可使用命令生成 # $ cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 49;echo SECRET_KEY: